diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,4 +1,4 @@ -from flask import Flask, render_template_string, request, redirect, url_for +from flask import Flask, render_template_string, request, redirect, url_for, flash, send_file, jsonify import json import os import logging @@ -8,54 +8,87 @@ from datetime import datetime from huggingface_hub import HfApi, hf_hub_download from huggingface_hub.utils import RepositoryNotFoundError from werkzeug.utils import secure_filename +import uuid +import html +import random app = Flask(__name__) -DATA_FILE = 'data.json' +app.secret_key = os.getenv("FLASK_SECRET_KEY", "zzirix_secret_key_for_cart") +DATA_FILE = 'data_zzirix.json' -# Настройки Hugging Face -REPO_ID = "Kgshop/teplydom" +REPO_ID = "Kgshop/clients" HF_TOKEN_WRITE = os.getenv("HF_TOKEN") -HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") +HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE -# Ссылка на логотип -LOGO_URL = "https://huggingface.co/spaces/Teply/Dom/resolve/main/Picsart_25-04-13_13-18-35-318.jpg" +LOGO_URL = "https://huggingface.co/spaces/Kgshop/Zzirixadm/resolve/main/Picsart_25-03-20_15-38-36-600.jpg" -# Настройка логирования -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) + +def initialize_data_structure(data): + if not isinstance(data, dict): + data = {'categories': [], 'products': [], 'orders': {}} + + data.setdefault('categories', []) + data.setdefault('products', []) + data.setdefault('orders', {}) + + if any(p.get('category') == 'Без категории' for p in data['products']) and 'Без категории' not in data['categories']: + data['categories'].append('Без категории') + + for product in data['products']: + if 'id' not in product: + product['id'] = str(uuid.uuid4()) + + product.setdefault('name', 'Без названия') + product.setdefault('description', '') + product.setdefault('category', 'Без категории') + product.setdefault('price', 0.0) + product.setdefault('colors', []) + product.setdefault('models', []) + product.setdefault('photos', []) + product.setdefault('in_stock', True) + product.setdefault('is_top', False) + + if not product['photos'] and 'media' in product and product['media']: + product['photos'] = [m['filename'] for m in product['media'] if m['type'] == 'photo'] + + if 'media' in product: + del product['media'] + + data['categories'] = sorted(list(set(data['categories'])), key=lambda x: (x != 'Без категории', x)) + + return data 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': [] if not isinstance(data, list) else data} - 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': []} + if not os.path.exists(DATA_FILE) or os.path.getsize(DATA_FILE) == 0: + download_db_from_hf() + + if os.path.exists(DATA_FILE) and os.path.getsize(DATA_FILE) > 0: + with open(DATA_FILE, 'r', encoding='utf-8') as file: + data = json.load(file) + else: + data = {'products': [], 'categories': [], 'orders': {}} + + return initialize_data_structure(data) except Exception as e: - logging.error(f"Произошла ошибка при загрузке данных: {e}") - return {'products': [], 'categories': []} + return initialize_data_structure({}) def save_data(data): try: - with open(DATA_FILE, 'w', encoding='utf-8') as file: + temp_file = DATA_FILE + '.tmp' + with open(temp_file, 'w', encoding='utf-8') as file: json.dump(data, file, ensure_ascii=False, indent=4) - logging.info("Данные успешно сохранены в JSON") + os.replace(temp_file, DATA_FILE) upload_db_to_hf() except Exception as e: - logging.error(f"Ошибка при сохранении данных: {e}") + if os.path.exists(temp_file): + os.remove(temp_file) raise def upload_db_to_hf(): + if not HF_TOKEN_WRITE: + return try: api = HfApi() api.upload_file( @@ -64,13 +97,17 @@ def upload_db_to_hf(): repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, - commit_message=f"Автоматическое резервное копирование базы данных {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + commit_message=f"Backup {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) - logging.info("Резервная копия JSON базы успешно загружена на Hugging Face.") except Exception as e: - logging.error(f"Ошибка при загрузке резервной копии: {e}") + pass def download_db_from_hf(): + if not HF_TOKEN_READ: + if not os.path.exists(DATA_FILE): + with open(DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(initialize_data_structure({}), f) + return try: hf_hub_download( repo_id=REPO_ID, @@ -78,890 +115,1787 @@ def download_db_from_hf(): repo_type="dataset", token=HF_TOKEN_READ, local_dir=".", - local_dir_use_symlinks=False + local_dir_use_symlinks=False, + force_download=True ) - logging.info("JSON база успешно скачана из Hugging Face.") - except RepositoryNotFoundError as e: - logging.error(f"Репозиторий не найден: {e}") - raise except Exception as e: - logging.error(f"Ошибка при скачивании JSON базы: {e}") - raise + if not os.path.exists(DATA_FILE): + with open(DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(initialize_data_structure({}), f) def periodic_backup(): while True: - upload_db_to_hf() - time.sleep(800) + time.sleep(3600) + try: + data = load_data() + save_data(data) + except Exception as e: + pass -@app.route('/') -def catalog(): - data = load_data() - products = data['products'] - categories = data['categories'] - - catalog_html = ''' - - - - - - Теплый дом - отопительные системы , котлы , теплые полы , нержавейка - - - - - - -
-
+ + document.getElementById('productDetailModal').style.display = 'block'; + + if (detailSwiperInstance) { + detailSwiperInstance.destroy(true, true); + } + detailSwiperInstance = new Swiper('#productDetailModal .swiper-container', { + slidesPerView: 1, + spaceBetween: 20, + loop: true, + grabCursor: true, + pagination: { el: '#productDetailModal .swiper-pagination', clickable: true }, + navigation: { nextEl: '#productDetailModal .swiper-button-next', prevEl: '#productDetailModal .swiper-button-prev' }, + zoom: { maxRatio: 3 }, + on: { + init: function () { + this.slides.forEach(slide => { + const img = slide.querySelector('img'); + if (img && !img.complete) { + img.onload = () => detailSwiperInstance.update(); + } + const video = slide.querySelector('video'); + if (video) { + video.onloadedmetadata = () => detailSwiperInstance.update(); + } + }); + }, + resize: function() { + this.update(); + } + } + }); + detailSwiperInstance.update(); + } + + updateCartButton(); +''' + +@app.route('/') +def catalog(): + data = load_data() + products = data.get('products', []) + categories = data.get('categories', []) + products.sort(key=lambda p: p['name'].lower()) + + return render_template_string(''' + + + + + + ZZIRIX - сотовые аксессуары оптом + + + + + + +
+
+
-

Каталог

+

Каталог ZZIRIX

+
+
-
- - {% for category in categories %} - - {% endfor %} -
-
- -
-
- {% for product in products %} -
+
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} +
+ + {% for category in categories %} + + {% endfor %} +
+
+ +
+ - - - +
- -