from flask import Flask, render_template_string, request, redirect, url_for, send_file, flash, jsonify
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, HfHubHTTPError
from werkzeug.utils import secure_filename
from dotenv import load_dotenv
import requests
import uuid
load_dotenv()
app = Flask(__name__)
app.secret_key = 'your_unique_secret_key_soola_cosmetics_67890_no_login'
DATA_FILE = 'data.json'
SYNC_FILES = [DATA_FILE]
REPO_ID = "Kgshop/altynroller"
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
STORE_ADDRESSES = [
"ТЦ Новый Ялян: 2 этаж, сектор G, бутик 50-52",
]
CURRENCY_CODE = 'KZT'
CURRENCY_NAME = 'Казахстанский тенге'
DOWNLOAD_RETRIES = 3
DOWNLOAD_DELAY = 5
STATUS_MAP_RU = {
"new": "Новый",
"accepted": "Принят",
"prepared": "Собран",
"shipped": "Отправлен"
}
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def download_db_from_hf(specific_file=None, retries=DOWNLOAD_RETRIES, delay=DOWNLOAD_DELAY):
if not HF_TOKEN_READ and not HF_TOKEN_WRITE:
logging.warning("HF_TOKEN_READ/HF_TOKEN_WRITE not set. Download might fail for private repos.")
token_to_use = HF_TOKEN_READ if HF_TOKEN_READ else HF_TOKEN_WRITE
files_to_download = [specific_file] if specific_file else SYNC_FILES
logging.info(f"Attempting download for {files_to_download} from {REPO_ID}...")
all_successful = True
for file_name in files_to_download:
success = False
for attempt in range(retries + 1):
try:
logging.info(f"Downloading {file_name} (Attempt {attempt + 1}/{retries + 1})...")
local_path = hf_hub_download(
repo_id=REPO_ID,
filename=file_name,
repo_type="dataset",
token=token_to_use,
local_dir=".",
local_dir_use_symlinks=False,
force_download=True,
resume_download=False
)
logging.info(f"Successfully downloaded {file_name} to {local_path}.")
success = True
break
except RepositoryNotFoundError:
logging.error(f"Repository {REPO_ID} not found. Download cancelled for all files.")
return False
except HfHubHTTPError as e:
if e.response.status_code == 404:
logging.warning(f"File {file_name} not found in repo {REPO_ID} (404). Skipping this file.")
if attempt == 0 and not os.path.exists(file_name):
try:
if file_name == DATA_FILE:
with open(file_name, 'w', encoding='utf-8') as f:
json.dump({'products': [], 'categories': [], 'orders': {}}, f)
logging.info(f"Created empty local file {file_name} because it was not found on HF.")
except Exception as create_e:
logging.error(f"Failed to create empty local file {file_name}: {create_e}")
success = False
break
else:
logging.error(f"HTTP error downloading {file_name} (Attempt {attempt + 1}): {e}. Retrying in {delay}s...")
except requests.exceptions.RequestException as e:
logging.error(f"Network error downloading {file_name} (Attempt {attempt + 1}): {e}. Retrying in {delay}s...")
except Exception as e:
logging.error(f"Unexpected error downloading {file_name} (Attempt {attempt + 1}): {e}. Retrying in {delay}s...", exc_info=True)
if attempt < retries:
time.sleep(delay)
if not success:
logging.error(f"Failed to download {file_name} after {retries + 1} attempts.")
all_successful = False
logging.info(f"Download process finished. Overall success: {all_successful}")
return all_successful
def upload_db_to_hf(specific_file=None):
if not HF_TOKEN_WRITE:
logging.warning("HF_TOKEN (for writing) not set. Skipping upload to Hugging Face.")
return
try:
api = HfApi()
files_to_upload = [specific_file] if specific_file else SYNC_FILES
logging.info(f"Starting upload of {files_to_upload} to HF repo {REPO_ID}...")
for file_name in files_to_upload:
if os.path.exists(file_name):
try:
api.upload_file(
path_or_fileobj=file_name,
path_in_repo=file_name,
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
commit_message=f"Sync {file_name} {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
logging.info(f"File {file_name} successfully uploaded to Hugging Face.")
except Exception as e:
logging.error(f"Error uploading file {file_name} to Hugging Face: {e}")
else:
logging.warning(f"File {file_name} not found locally, skipping upload.")
logging.info("Finished uploading files to HF.")
except Exception as e:
logging.error(f"General error during Hugging Face upload initialization or process: {e}", exc_info=True)
def periodic_backup():
backup_interval = 1800
logging.info(f"Setting up periodic backup every {backup_interval} seconds.")
while True:
time.sleep(backup_interval)
logging.info("Starting periodic backup...")
upload_db_to_hf()
logging.info("Periodic backup finished.")
def load_data():
default_data = {'products': [], 'categories': [], 'orders': {}}
try:
with open(DATA_FILE, 'r', encoding='utf-8') as file:
data = json.load(file)
logging.info(f"Local data loaded successfully from {DATA_FILE}")
if not isinstance(data, dict):
logging.warning(f"Local {DATA_FILE} is not a dictionary. Attempting download.")
raise FileNotFoundError
if 'products' not in data: data['products'] = []
if 'categories' not in data: data['categories'] = []
if 'orders' not in data: data['orders'] = {}
return data
except FileNotFoundError:
logging.warning(f"Local file {DATA_FILE} not found. Attempting download from HF.")
except json.JSONDecodeError:
logging.error(f"Error decoding JSON in local {DATA_FILE}. File might be corrupt. Attempting download.")
if download_db_from_hf(specific_file=DATA_FILE):
try:
with open(DATA_FILE, 'r', encoding='utf-8') as file:
data = json.load(file)
logging.info(f"Data loaded successfully from {DATA_FILE} after download.")
if not isinstance(data, dict):
logging.error(f"Downloaded {DATA_FILE} is not a dictionary. Using default.")
return default_data
if 'products' not in data: data['products'] = []
if 'categories' not in data: data['categories'] = []
if 'orders' not in data: data['orders'] = {}
return data
except FileNotFoundError:
logging.error(f"File {DATA_FILE} still not found even after download reported success. Using default.")
return default_data
except json.JSONDecodeError:
logging.error(f"Error decoding JSON in downloaded {DATA_FILE}. Using default.")
return default_data
except Exception as e:
logging.error(f"Unknown error loading downloaded {DATA_FILE}: {e}. Using default.", exc_info=True)
return default_data
else:
logging.error(f"Failed to download {DATA_FILE} from HF after retries. Using empty default data structure.")
if not os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, 'w', encoding='utf-8') as f:
json.dump(default_data, f)
logging.info(f"Created empty local file {DATA_FILE} after failed download.")
except Exception as create_e:
logging.error(f"Failed to create empty local file {DATA_FILE}: {create_e}")
return default_data
def save_data(data):
try:
if not isinstance(data, dict):
logging.error("Attempted to save invalid data structure (not a dict). Aborting save.")
return
if 'products' not in data: data['products'] = []
if 'categories' not in data: data['categories'] = []
if 'orders' not in data: data['orders'] = {}
with open(DATA_FILE, 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=4)
logging.info(f"Data successfully saved to {DATA_FILE}")
upload_db_to_hf(specific_file=DATA_FILE)
except Exception as e:
logging.error(f"Error saving data to {DATA_FILE}: {e}", exc_info=True)
CATALOG_TEMPLATE = '''
ROLLER ALTYN - Каталог
ROLLER ALTYN
Наши адреса в г. Алматы
{% for address in store_addresses %}
{{ address }}
{% endfor %}
{% for category in categories %}
{% endfor %}
{% for product in products %}
{% if product.get('is_top', False) %}
Топ
{% endif %}
{% if product.get('photos') and product['photos']|length > 0 %}
{% else %}
{% endif %}
{{ product['name'] }}
{% if product.get('variants') and product.variants|length > 0 %}
от {{ "%.2f"|format(product.variants|map(attribute='price')|min) }} {{ currency_code }}
{% else %}
-
{% endif %}