diff --git "a/Optomshoptxt.txt" "b/Optomshoptxt.txt" new file mode 100644--- /dev/null +++ "b/Optomshoptxt.txt" @@ -0,0 +1,3681 @@ + + +from flask import Flask, render_template_string, request, redirect, url_for +import json +import os +import logging +import threading +import time +from datetime import datetime +from huggingface_hub import HfApi, hf_hub_download +from huggingface_hub.utils import RepositoryNotFoundError +from werkzeug.utils import secure_filename + +app = Flask(__name__) +DATA_FILE = 'data_firecollection.json' + +# Настройки Hugging Face +REPO_ID = "Kgshop/Clients2" # Замените, если нужно +HF_TOKEN_WRITE = os.getenv("HF_TOKEN") +HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") + +# Настройка логирования +logging.basicConfig(level=logging.DEBUG) + +def load_data(): + try: + download_db_from_hf() + with open(DATA_FILE, 'r', encoding='utf-8') as file: + data = json.load(file) + logging.info("Данные успешно загружены из JSON") + if not isinstance(data, dict) or 'products' not in data or 'categories' not in data: + return {'products': [], 'categories': []} # Corrected: return empty dict with lists + return data + except FileNotFoundError: + logging.warning("Локальный файл базы данных не найден после скачивания.") + return {'products': [], 'categories': []} + except json.JSONDecodeError: + logging.error("Ошибка: Невозможно декодировать JSON файл.") + return {'products': [], 'categories': []} + except RepositoryNotFoundError: + logging.error("Репозиторий не найден. Создание локальной базы данных.") + return {'products': [], 'categories': []} + except Exception as e: + logging.error(f"Произошла ошибка при загрузке данных: {e}") + return {'products': [], 'categories': []} + +def save_data(data): + try: + with open(DATA_FILE, 'w', encoding='utf-8') as file: + json.dump(data, file, ensure_ascii=False, indent=4) + logging.info("Данные успешно сохранены в JSON") + upload_db_to_hf() + except Exception as e: + logging.error(f"Ошибка при сохранении данных: {e}") + raise + +def upload_db_to_hf(): + try: + api = HfApi() + api.upload_file( + path_or_fileobj=DATA_FILE, + path_in_repo=DATA_FILE, + repo_id=REPO_ID, + repo_type="dataset", + token=HF_TOKEN_WRITE, + commit_message=f"Автоматическое резервное копирование базы данных {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + ) + logging.info("Резервная копия JSON базы успешно загружена на Hugging Face.") + except Exception as e: + logging.error(f"Ошибка при загрузке резервной копии: {e}") + +def download_db_from_hf(): + try: + hf_hub_download( + repo_id=REPO_ID, + filename=DATA_FILE, + repo_type="dataset", + token=HF_TOKEN_READ, + local_dir=".", + local_dir_use_symlinks=False + ) + logging.info("JSON база успешно скачана из Hugging Face.") + except RepositoryNotFoundError as e: + logging.error(f"Репозиторий не найден: {e}") + raise + except Exception as e: + logging.error(f"Ошибка при скачивании JSON базы: {e}") + raise + +def periodic_backup(): + while True: + upload_db_to_hf() + time.sleep(800) + +@app.route('/') +def catalog(): + data = load_data() + products = data['products'] + # categories = data['categories'] Removed categories from main page + + catalog_html = ''' + + + + + + Fire collection + + + + + + +
+
+ Fire collection Logo + +
+ + +
+

Наш адрес

+

Рынок Дордой, 0 проход , 2034 контейнер

+

График работы

+

По адресу: без выходных с 8:00 до 16:00, онлайн: круглосуточно

+
+ +
+ +
+
+ {% for product in products %} +
+ + {% if product.get('photos') and product['photos']|length > 0 %} +
+ {{ product['name'] }} +
+ {% endif %} + {% if product.get('wholesale_price') and product.get('min_wholesale') %} + Опт от {{ product['min_wholesale'] }} + {% endif %} + {% if product.get('discount') %} + Скидка {{ product['discount'] }}% + {% endif %} +

{{ product['name'] }}

+
+ {% if product.get('discount') %} + {{ product['price'] }} с + {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с + Скидка: {{ product['discount'] }}% + {% else %} + {{ product['price'] }} с + {% endif %} +
+

{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}

+ +
+ {% endfor %} +
+
+ + + + + + + + + + + + + + + + + + + + + + ''' + return render_template_string(catalog_html, products=products, repo_id=REPO_ID) + +@app.route('/categories') +def categories_page(): + data = load_data() + categories = data['categories'] + + categories_html = ''' + + + + + + Fire collection + + + + + +
+
+ Fire collection Logo + +
+
+ {% for category in categories %} + +

{{ category }}

+
+ {% endfor %} +
+
+ + + + + + + ''' + return render_template_string(categories_html, categories=categories) + +@app.route('/category/') +def category_products(category): + data = load_data() + products = [p for p in data['products'] if p.get('category') == category] + # categories = data['categories'] # No longer needed here + + category_html = ''' + + + + + + Fire collection + + + + + + +
+
+ Fire collection Logo + +
+
+ {% for product in products %} +
+ + {% if product.get('photos') and product['photos']|length > 0 %} +
+ {{ product['name'] }} +
+ {% endif %} + {% if product.get('wholesale_price') and product.get('min_wholesale') %} + Опт от {{ product['min_wholesale'] }} + {% endif %} + {% if product.get('discount') %} + Скидка {{ product['discount'] }}% + {% endif %} +

{{ product['name'] }}

+
+ {% if product.get('discount') %} + {{ product['price'] }} с + {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с + Скидка: {{ product['discount'] }}% + {% else %} + {{ product['price'] }} с + {% endif %} +
+

{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}

+ +
+ {% endfor %} +
+
+ + + + + + + + + + + + + + + + + + + + + ''' + return render_template_string(category_html, products=products, category=category, repo_id=REPO_ID) + + +@app.route('/favorites') +def favorites_page(): + data = load_data() + products = data['products'] + favorites = request.args.get('favorites', '[]') # Not really used directly, but kept for consistency + + favorites_html = ''' + + + + + + Fire collection + + + + + + +
+
+ Fire collection Logo + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + ''' + return render_template_string(favorites_html, products=products, repo_id=REPO_ID) + + + +@app.route('/discounts') +def discounts_page(): + data = load_data() + products = [p for p in data['products'] if p.get('discount')] + # categories = data['categories'] # No longer used in this view + + discounts_html = ''' + + + + + + Fire collection + + + + + + +
+
+ Fire collection Logo + +
+
+ {% for product in products %} +
+ + {% if product.get('photos') and product['photos']|length > 0 %} +
+ {{ product['name'] }} +
+ {% endif %} + {% if product.get('wholesale_price') and product.get('min_wholesale') %} + Опт от {{ product['min_wholesale'] }} + {% endif %} + {% if product.get('discount') %} + Скидка {{ product['discount'] }}% + {% endif %} +

{{ product['name'] }}

+
+ {% if product.get('discount') %} + {{ product['price'] }} с + {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с + Скидка: {{ product['discount'] }}% + {% else %} + {{ product['price'] }} с + {% endif %} +
+

{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}

+ +
+ {% endfor %} +
+
+ + + + + + + + + + + + + + + + + + + + + ''' + return render_template_string(discounts_html, products=products, repo_id=REPO_ID) + + +@app.route('/product/') +def product_details(index): + data = load_data() + if index < 0 or index >= len(data['products']): + return "Product not found", 404 # Return a 404 error if index is out of bounds + product = data['products'][index] + + product_html = ''' + +
+

{{ product['name'] }}

+
+
+ {% for photo in product.get('photos', []) %} +
+ {{ product['name'] }} +
+ {% endfor %} +
+
+
+
+
+
+ {% if product.get('discount') %} + {{ product['price'] }} с + {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с + Скидка: {{ product['discount'] }}% + {% else %} + {{ product['price'] }} с + {% endif %} +
+

Описание: {{ product['description'] }}

+

Категория: {{ product.get('category', 'Без категории') }}

+ {% if product.get('colors') %} +
+ Цвета: + {% for color in product['colors'] %} + {{ color }} + {% endfor %} +
+ {% endif %} +
+ ''' + return render_template_string(product_html, product=product, repo_id=REPO_ID) + +@app.route('/admin', methods=['GET', 'POST']) +def admin(): + data = load_data() + products = data['products'] + categories = data['categories'] + + if request.method == 'POST': + action = request.form.get('action') + + if action == 'add': + photos = request.files.getlist('photos') + photo_filenames = [] + for photo in photos: + if photo and photo.filename: + filename = secure_filename(photo.filename) + api = HfApi() + api.upload_file( + path_or_fileobj=photo, + path_in_repo=f"photos/{filename}", + repo_id=REPO_ID, + repo_type="dataset", + token=HF_TOKEN_WRITE + ) + photo_filenames.append(filename) + + colors = request.form.getlist('colors') + colors = [color.strip() for color in colors if color.strip()] + + new_product = { + 'name': request.form['name'], + 'price': float(request.form['price']), + 'description': request.form['description'], + 'category': request.form['category'], + 'colors': colors, + 'photos': photo_filenames, + 'discount': float(request.form['discount']) if request.form['discount'] else None + } + products.append(new_product) + + elif action == 'edit': + index = int(request.form['index']) + photos = request.files.getlist('photos') + photo_filenames = products[index].get('photos', []) # Keep existing photos + for photo in photos: + if photo and photo.filename: + filename = secure_filename(photo.filename) + api = HfApi() + api.upload_file( + path_or_fileobj=photo, + path_in_repo=f"photos/{filename}", + repo_id=REPO_ID, + repo_type="dataset", + token=HF_TOKEN_WRITE + ) + photo_filenames.append(filename) # Add new photos + + colors = request.form.getlist('colors') + colors = [color.strip() for color in colors if color.strip()] + + products[index] = { + 'name': request.form['name'], + 'price': float(request.form['price']), + 'description': request.form['description'], + 'category': request.form['category'], + 'colors': colors, + 'photos': photo_filenames, # Updated photo list + 'discount': float(request.form['discount']) if request.form['discount'] else None + } + + elif action == 'delete': + index = int(request.form['index']) + # Delete photos from Hugging Face Hub before deleting the product + product_to_delete = products[index] + if 'photos' in product_to_delete: + api = HfApi() + for photo in product_to_delete['photos']: + try: + api.delete_file( + path_in_repo=f"photos/{photo}", + repo_id=REPO_ID, + repo_type="dataset", + token=HF_TOKEN_WRITE + ) + except Exception as e: + logging.error(f"Error deleting photo {photo}: {e}") + products.pop(index) + + + elif action == 'add_category': + category = request.form['category_name'].strip() + if category and category not in categories: + categories.append(category) + + elif action == 'delete_category': + index = int(request.form['category_index']) + category_to_delete = categories[index] + categories.pop(index) + #Set category to "Без категории" for products in the deleted category + for product in products: + if product.get('category') == category_to_delete: + product['category'] = 'Без категории' + + save_data({'products': products, 'categories': categories}) + return redirect(url_for('admin')) + + admin_html = ''' + + + + + + Fire collection - Админ-панель + + + + +
+

Fire collection - Админ-панель

+ +
+

Добавить товар

+
+ + + + + + + + + + + + + + +
+
+ +
+
+ + +
+
+ +
+

Добавить категорию

+
+ + + + +
+
+ +
+ {% for category in categories %} +
+

{{ category }}

+
+ + + +
+
+ {% endfor %} +
+ +

Список товаров

+
+ +
+
+ {% for product in products %} +
+

{{ product['name'] }}

+

Цена: {{ product['price'] }} с

+ {% if product.get('discount') %} +

Скидка: {{ product['discount'] }}%

+ {% endif %} +

Описание: {{ product['description'] }}

+

Категория: {{ product.get('category', 'Без категории') }}

+

Цвета: {{ product.get('colors', ['Нет цветов'])|join(', ') }}

+ {% if product.get('photos') %} +

Фотографии:

+ {% for photo in product['photos'] %} + {{ product['name'] }} + {% endfor %} + {% endif %} +
+ Редактировать +
+ + + + + + + + + + + + + + + +
+ {% for color in product.get('colors', []) %} +
+ +
+ {% endfor %} +
+ + +
+
+
+ + + +
+
+ {% endfor %} +
+
+ + + + ''' + return render_template_string(admin_html, products=products, categories=categories, repo_id=REPO_ID) + +@app.route('/backup', methods=['POST']) +def backup(): + upload_db_to_hf() + return "Резервная копия создана.", 200 + +@app.route('/download', methods=['GET']) +def download(): + download_db_from_hf() + return "База данных скачана.", 200 + +if __name__ == '__main__': + backup_thread = threading.Thread(target=periodic_backup, daemon=True) + backup_thread.start() + try: + load_data() + except Exception as e: + logging.error(f"Не удалось загрузить базу данных: {e}") + app.run(debug=True, host='0.0.0.0', port=7860)