flpolprojects commited on
Commit
9d3c6b2
·
verified ·
1 Parent(s): a283ce0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -34
app.py CHANGED
@@ -26,19 +26,22 @@ def load_data():
26
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
27
  data = json.load(file)
28
  logging.info("Данные успешно загружены из JSON")
 
 
 
29
  return data
30
  except FileNotFoundError:
31
  logging.warning("Локальный файл базы данных не найден после скачивания.")
32
- return []
33
  except json.JSONDecodeError:
34
  logging.error("Ошибка: Невозможно декодировать JSON файл.")
35
- return []
36
  except RepositoryNotFoundError:
37
  logging.error("Репозиторий не найден. Создание локальной базы данных.")
38
- return []
39
  except Exception as e:
40
  logging.error(f"Произошла ошибка при загрузке данных: {e}")
41
- return []
42
 
43
  def save_data(data):
44
  try:
@@ -90,9 +93,9 @@ def periodic_backup():
90
 
91
  @app.route('/')
92
  def catalog():
93
- products = load_data()
94
- # Получаем уникальные категории из продуктов
95
- categories = sorted(set(product.get('category', 'Без категории') for product in products))
96
 
97
  catalog_html = '''
98
  <!DOCTYPE html>
@@ -615,12 +618,11 @@ def catalog():
615
  }
616
  }
617
 
618
- // Поисковая строка
619
  document.getElementById('search-input').addEventListener('input', function(e) {
620
  filterProducts();
621
  });
622
 
623
- // Фильтрация по категориям
624
  const categoryFilters = document.querySelectorAll('.category-filter');
625
  categoryFilters.forEach(filter => {
626
  filter.addEventListener('click', function() {
@@ -661,7 +663,8 @@ def catalog():
661
 
662
  @app.route('/product/<int:index>')
663
  def product_detail(index):
664
- products = load_data()
 
665
  try:
666
  product = products[index]
667
  except IndexError:
@@ -700,10 +703,37 @@ def product_detail(index):
700
 
701
  @app.route('/admin', methods=['GET', 'POST'])
702
  def admin():
703
- products = load_data()
 
 
 
704
  if request.method == 'POST':
705
  action = request.form.get('action')
706
- if action == 'add':
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
707
  name = request.form.get('name')
708
  price = request.form.get('price')
709
  description = request.form.get('description')
@@ -740,7 +770,7 @@ def admin():
740
  if os.path.exists(temp_path):
741
  os.remove(temp_path)
742
 
743
- if not name or not price or not description or not category:
744
  logging.error("Ошибка: Не все обязательные поля заполнены")
745
  return "Ошибка: Заполните все обязательные поля", 400
746
 
@@ -754,20 +784,14 @@ def admin():
754
  'name': name,
755
  'price': price,
756
  'description': description,
757
- 'category': category,
758
  'photos': photos_list
759
  }
760
 
761
  products.append(new_product)
762
- logging.info(f"Новый товар добавлен в список: {new_product}")
763
-
764
- try:
765
- save_data(products)
766
- logging.info("Товар успешно сохранен в базе данных")
767
- return redirect(url_for('admin'))
768
- except Exception as e:
769
- logging.error(f"Ошибка при сохранении товара: {e}")
770
- return f"Ошибка при сохранении товара: {e}", 500
771
 
772
  elif action == 'edit':
773
  index = int(request.form.get('index'))
@@ -815,9 +839,9 @@ def admin():
815
  logging.error("Ошибка: Цена должна быть числом")
816
  return "Ошибка: Цена должна быть числом", 400
817
  products[index]['description'] = description
818
- products[index]['category'] = category
819
 
820
- save_data(products)
821
  logging.info(f"Товар с индексом {index} успешно отредактирован")
822
  return redirect(url_for('admin'))
823
 
@@ -825,7 +849,7 @@ def admin():
825
  index = int(request.form.get('index'))
826
  logging.info(f"Удаление товара с индексом {index}")
827
  del products[index]
828
- save_data(products)
829
  logging.info("Товар успешно удален")
830
  return redirect(url_for('admin'))
831
 
@@ -842,7 +866,7 @@ def admin():
842
  margin: 20px;
843
  background-color: #f9f9f9;
844
  }
845
- h1 {
846
  color: #333;
847
  }
848
  form {
@@ -878,10 +902,16 @@ def admin():
878
  button:hover {
879
  background-color: #218838;
880
  }
881
- .product-list {
 
 
 
 
 
 
882
  margin-top: 20px;
883
  }
884
- .product-item {
885
  background-color: #fff;
886
  border: 1px solid #ddd;
887
  padding: 10px;
@@ -909,7 +939,7 @@ def admin():
909
  padding: 6px 10px;
910
  font-size: 0.9em;
911
  }
912
- .product-item {
913
  padding: 8px;
914
  }
915
  }
@@ -926,12 +956,39 @@ def admin():
926
  <label for="description">Описание:</label>
927
  <textarea id="description" name="description" rows="4" required></textarea>
928
  <label for="category">Категория:</label>
929
- <input type="text" id="category" name="category" required placeholder="Введите категорию">
 
 
 
 
 
930
  <label for="photos">Фотографии товара (максимум 2):</label>
931
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
932
  <button type="submit">Добавить товар</button>
933
  </form>
934
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
935
  <h2>Управление базой данных</h2>
936
  <form method="POST" action="{{ url_for('backup') }}">
937
  <button type="submit">Создать резервную копию</button>
@@ -967,7 +1024,12 @@ def admin():
967
  <label for="description">Описание:</label>
968
  <textarea id="description" name="description" rows="4" required>{{ product['description'] }}</textarea>
969
  <label for="category">Категория:</label>
970
- <input type="text" id="category" name="category" value="{{ product.get('category', 'Без категории') }}" required>
 
 
 
 
 
971
  <label for="photos">Фотографии товара (максимум 2):</label>
972
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
973
  <button type="submit">Сохранить изменения</button>
@@ -976,7 +1038,7 @@ def admin():
976
  <form method="POST">
977
  <input type="hidden" name="action" value="delete">
978
  <input type="hidden" name="index" value="{{ loop.index0 }}">
979
- <button type="submit">Удалить</button>
980
  </form>
981
  </div>
982
  {% endfor %}
@@ -984,7 +1046,7 @@ def admin():
984
  </body>
985
  </html>
986
  '''
987
- return render_template_string(admin_html, products=products, repo_id=REPO_ID)
988
 
989
  @app.route('/backup', methods=['POST'])
990
  def backup():
 
26
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
27
  data = json.load(file)
28
  logging.info("Данные успешно загружены из JSON")
29
+ # Проверяем структуру данных и добавляем categories, если его нет
30
+ if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
31
+ return {'products': [], 'categories': [] if not isinstance(data, list) else data}
32
  return data
33
  except FileNotFoundError:
34
  logging.warning("Локальный файл базы данных не найден после скачивания.")
35
+ return {'products': [], 'categories': []}
36
  except json.JSONDecodeError:
37
  logging.error("Ошибка: Невозможно декодировать JSON файл.")
38
+ return {'products': [], 'categories': []}
39
  except RepositoryNotFoundError:
40
  logging.error("Репозиторий не найден. Создание локальной базы данных.")
41
+ return {'products': [], 'categories': []}
42
  except Exception as e:
43
  logging.error(f"Произошла ошибка при загрузке данных: {e}")
44
+ return {'products': [], 'categories': []}
45
 
46
  def save_data(data):
47
  try:
 
93
 
94
  @app.route('/')
95
  def catalog():
96
+ data = load_data()
97
+ products = data['products']
98
+ categories = data['categories']
99
 
100
  catalog_html = '''
101
  <!DOCTYPE html>
 
618
  }
619
  }
620
 
621
+ // Поисковая строка и фильтрация
622
  document.getElementById('search-input').addEventListener('input', function(e) {
623
  filterProducts();
624
  });
625
 
 
626
  const categoryFilters = document.querySelectorAll('.category-filter');
627
  categoryFilters.forEach(filter => {
628
  filter.addEventListener('click', function() {
 
663
 
664
  @app.route('/product/<int:index>')
665
  def product_detail(index):
666
+ data = load_data()
667
+ products = data['products']
668
  try:
669
  product = products[index]
670
  except IndexError:
 
703
 
704
  @app.route('/admin', methods=['GET', 'POST'])
705
  def admin():
706
+ data = load_data()
707
+ products = data['products']
708
+ categories = data['categories']
709
+
710
  if request.method == 'POST':
711
  action = request.form.get('action')
712
+
713
+ # Обработка категорий
714
+ if action == 'add_category':
715
+ category_name = request.form.get('category_name')
716
+ if category_name and category_name not in categories:
717
+ categories.append(category_name)
718
+ save_data(data)
719
+ logging.info(f"Добавлена новая категория: {category_name}")
720
+ return redirect(url_for('admin'))
721
+ else:
722
+ return "Ошибка: Категория уже существует или не указано название", 400
723
+
724
+ elif action == 'delete_category':
725
+ category_index = int(request.form.get('category_index'))
726
+ deleted_category = categories.pop(category_index)
727
+ # Обновляем продукты, убирая удаленную категорию
728
+ for product in products:
729
+ if product.get('category') == deleted_category:
730
+ product['category'] = 'Без категории'
731
+ save_data(data)
732
+ logging.info(f"Удалена категория: {deleted_category}")
733
+ return redirect(url_for('admin'))
734
+
735
+ # Обработка продуктов
736
+ elif action == 'add':
737
  name = request.form.get('name')
738
  price = request.form.get('price')
739
  description = request.form.get('description')
 
770
  if os.path.exists(temp_path):
771
  os.remove(temp_path)
772
 
773
+ if not name or not price or not description:
774
  logging.error("Ошибка: Не все обязательные поля заполнены")
775
  return "Ошибка: Заполните все обязательные поля", 400
776
 
 
784
  'name': name,
785
  'price': price,
786
  'description': description,
787
+ 'category': category if category in categories else 'Без категории',
788
  'photos': photos_list
789
  }
790
 
791
  products.append(new_product)
792
+ save_data(data)
793
+ logging.info(f"Новый товар добавлен: {new_product}")
794
+ return redirect(url_for('admin'))
 
 
 
 
 
 
795
 
796
  elif action == 'edit':
797
  index = int(request.form.get('index'))
 
839
  logging.error("Ошибка: Цена должна быть числом")
840
  return "Ошибка: Цена должна быть числом", 400
841
  products[index]['description'] = description
842
+ products[index]['category'] = category if category in categories else 'Без категории'
843
 
844
+ save_data(data)
845
  logging.info(f"Товар с индексом {index} успешно отредактирован")
846
  return redirect(url_for('admin'))
847
 
 
849
  index = int(request.form.get('index'))
850
  logging.info(f"Удаление товара с индексом {index}")
851
  del products[index]
852
+ save_data(data)
853
  logging.info("Товар успешно удален")
854
  return redirect(url_for('admin'))
855
 
 
866
  margin: 20px;
867
  background-color: #f9f9f9;
868
  }
869
+ h1, h2 {
870
  color: #333;
871
  }
872
  form {
 
902
  button:hover {
903
  background-color: #218838;
904
  }
905
+ .delete-button {
906
+ background-color: #dc3545;
907
+ }
908
+ .delete-button:hover {
909
+ background-color: #c82333;
910
+ }
911
+ .product-list, .category-list {
912
  margin-top: 20px;
913
  }
914
+ .product-item, .category-item {
915
  background-color: #fff;
916
  border: 1px solid #ddd;
917
  padding: 10px;
 
939
  padding: 6px 10px;
940
  font-size: 0.9em;
941
  }
942
+ .product-item, .category-item {
943
  padding: 8px;
944
  }
945
  }
 
956
  <label for="description">Описание:</label>
957
  <textarea id="description" name="description" rows="4" required></textarea>
958
  <label for="category">Категория:</label>
959
+ <select id="category" name="category">
960
+ <option value="Без категории">Без категории</option>
961
+ {% for category in categories %}
962
+ <option value="{{ category }}">{{ category }}</option>
963
+ {% endfor %}
964
+ </select>
965
  <label for="photos">Фотографии товара (максимум 2):</label>
966
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
967
  <button type="submit">Добавить товар</button>
968
  </form>
969
 
970
+ <h1>Управление категориями</h1>
971
+ <form method="POST">
972
+ <input type="hidden" name="action" value="add_category">
973
+ <label for="category_name">Название категории:</label>
974
+ <input type="text" id="category_name" name="category_name" required>
975
+ <button type="submit">Добавить категорию</button>
976
+ </form>
977
+
978
+ <h2>Список категорий</h2>
979
+ <div class="category-list">
980
+ {% for category in categories %}
981
+ <div class="category-item">
982
+ <h3>{{ category }}</h3>
983
+ <form method="POST" style="display: inline;">
984
+ <input type="hidden" name="action" value="delete_category">
985
+ <input type="hidden" name="category_index" value="{{ loop.index0 }}">
986
+ <button type="submit" class="delete-button">Удалить</button>
987
+ </form>
988
+ </div>
989
+ {% endfor %}
990
+ </div>
991
+
992
  <h2>Управление базой данных</h2>
993
  <form method="POST" action="{{ url_for('backup') }}">
994
  <button type="submit">Создать резервную копию</button>
 
1024
  <label for="description">Описание:</label>
1025
  <textarea id="description" name="description" rows="4" required>{{ product['description'] }}</textarea>
1026
  <label for="category">Категория:</label>
1027
+ <select id="category" name="category">
1028
+ <option value="Без категории" {% if product.get('category', 'Без категории') == 'Без категории' %}selected{% endif %}>Без категории</option>
1029
+ {% for category in categories %}
1030
+ <option value="{{ category }}" {% if product.get('category') == category %}selected{% endif %}>{{ category }}</option>
1031
+ {% endfor %}
1032
+ </select>
1033
  <label for="photos">Фотографии товара (максимум 2):</label>
1034
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
1035
  <button type="submit">Сохранить изменения</button>
 
1038
  <form method="POST">
1039
  <input type="hidden" name="action" value="delete">
1040
  <input type="hidden" name="index" value="{{ loop.index0 }}">
1041
+ <button type="submit" class="delete-button">Удалить</button>
1042
  </form>
1043
  </div>
1044
  {% endfor %}
 
1046
  </body>
1047
  </html>
1048
  '''
1049
+ return render_template_string(admin_html, products=products, categories=categories, repo_id=REPO_ID)
1050
 
1051
  @app.route('/backup', methods=['POST'])
1052
  def backup():