diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,60 +1,528 @@ +# --- START OF FILE app (1) (9).py (MODIFIED) --- -from flask import Flask, render_template_string, request, redirect, url_for, send_file, flash, jsonify -import json import os +import uuid +from flask import Flask, request, jsonify, Response, send_from_directory +import google.generativeai as genai 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 +import json # Added for load_app_data/save_app_data in generated app -load_dotenv() +# Configure logging for the main generator app +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') app = Flask(__name__) -app.secret_key = 'your_unique_secret_key_soola_cosmetics_67890_no_login' -DATA_FILE = 'data.json' +# INTERNAL API KEY FOR THE GENERATOR ITSELF (FOR GOOGLE GEMINI) +API_KEY_INTERNAL = "AIzaSyArKidc4o0MwbaCFKStlb2q2AwNg6Pnqns" -SYNC_FILES = [DATA_FILE] +# DIRECTORY FOR STORING GENERATED PYTHON FLASK APPLICATIONS +GENERATED_APPS_DIR = 'generated_apps' -REPO_ID = "Kgshop/balluu" -HF_TOKEN_WRITE = os.getenv("HF_TOKEN") -HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") +# REPOSITORY ID FOR DATA SYNCHRONIZATION OF THE *GENERATED* FLASK APPS +GENERATED_APP_REPO_ID = "Kgshop/testsynk" -STORE_ADDRESS = "Рынок дордой , восток кишка , 235 контейнер " +if not os.path.exists(GENERATED_APPS_DIR): + os.makedirs(GENERATED_APPS_DIR) -CURRENCY_CODE = 'KGS' -CURRENCY_NAME = 'Кыргызский сом' +# HTML TEMPLATE FOR THE GENERATOR'S FRONTEND +html_template = """ + + + + + + EVA - Генератор Сайтов + + + +
+

EVA

+

Генератор Python приложений на базе ИИ

-DOWNLOAD_RETRIES = 3 -DOWNLOAD_DELAY = 5 +
+
+ + +
-logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +
+ +
+
+ + +
+
+
+
+
+ + + +""" + +# INJECTED HUGGING FACE SYNC CODE FOR THE *GENERATED* FLASK APPLICATIONS +_GENERATED_APP_HF_SYNC_SNIPPET = """ +# --- Hugging Face Sync System for this Generated App --- +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 dotenv import load_dotenv +import requests + +# Load environment variables for this generated app instance +load_dotenv() + +APP_REPO_ID = "{generated_app_repo_id}" +APP_DATA_FILE = 'app_data.json' # Data file specific to this generated app +APP_HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # Expect HF_TOKEN to be set in environment for this app +APP_HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") # Or use APP_HF_TOKEN_WRITE if READ isn't set + +APP_DOWNLOAD_RETRIES = 3 +APP_DOWNLOAD_DELAY = 5 + +# Configure logging for the generated app +app_logger = logging.getLogger(__name__) +app_logger.setLevel(logging.INFO) +# Prevent adding multiple handlers if this code is reloaded/executed multiple times (e.g. in dev) +if not app_logger.handlers: + handler = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + app_logger.addHandler(handler) + +def _app_download_db_from_hf(specific_file=None, retries=APP_DOWNLOAD_RETRIES, delay=APP_DOWNLOAD_DELAY): + # Ensure tokens are available + token_to_use = APP_HF_TOKEN_READ if APP_HF_TOKEN_READ else APP_HF_TOKEN_WRITE + if not token_to_use: + app_logger.warning("HF_TOKEN_READ/HF_TOKEN not set for generated app. Download might fail for private repos.") + + files_to_download = [specific_file] if specific_file else [APP_DATA_FILE] + app_logger.info(f"Generated app: Attempting download for {files_to_download} from {{APP_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})...") + app_logger.info(f"Generated app: Downloading {{file_name}} (Attempt {{attempt + 1}}/{{retries + 1}})...") local_path = hf_hub_download( - repo_id=REPO_ID, + repo_id=APP_REPO_ID, filename=file_name, repo_type="dataset", token=token_to_use, @@ -63,50 +531,51 @@ def download_db_from_hf(specific_file=None, retries=DOWNLOAD_RETRIES, delay=DOWN force_download=True, resume_download=False ) - logging.info(f"Successfully downloaded {file_name} to {local_path}.") + app_logger.info(f"Generated app: 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.") + app_logger.error(f"Generated app: Repository {{APP_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.") + app_logger.warning(f"Generated app: File {{file_name}} not found in repo {{APP_REPO_ID}} (404). Skipping this file.") if attempt == 0 and not os.path.exists(file_name): try: - if file_name == DATA_FILE: + # Create an empty default JSON file if it's the data file and not found on HF + if file_name == APP_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.") + json.dump({{}}, f) # Empty dictionary as default for app data + app_logger.info(f"Generated app: 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}") + app_logger.error(f"Generated app: Failed to create empty local file {{file_name}}: {{create_e}}") success = False - break + break # Don't retry 404s for the specific file else: - logging.error(f"HTTP error downloading {file_name} (Attempt {attempt + 1}): {e}. Retrying in {delay}s...") + app_logger.error(f"Generated app: 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...") + app_logger.error(f"Generated app: 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) + app_logger.error(f"Generated app: 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.") + app_logger.error(f"Generated app: Failed to download {{file_name}} after {{retries + 1}} attempts.") all_successful = False - logging.info(f"Download process finished. Overall success: {all_successful}") + app_logger.info(f"Generated app: 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.") +def _app_upload_db_to_hf(specific_file=None): + if not APP_HF_TOKEN_WRITE: + app_logger.warning("Generated app: 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}...") + files_to_upload = [specific_file] if specific_file else [APP_DATA_FILE] + app_logger.info(f"Generated app: Starting upload of {{files_to_upload}} to HF repo {{APP_REPO_ID}}...") for file_name in files_to_upload: if os.path.exists(file_name): @@ -114,1679 +583,238 @@ def upload_db_to_hf(specific_file=None): api.upload_file( path_or_fileobj=file_name, path_in_repo=file_name, - repo_id=REPO_ID, + repo_id=APP_REPO_ID, repo_type="dataset", - token=HF_TOKEN_WRITE, - commit_message=f"Sync {file_name} {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + token=APP_HF_TOKEN_WRITE, + commit_message=f"Sync {{file_name}} from generated app {{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}}" ) - logging.info(f"File {file_name} successfully uploaded to Hugging Face.") + app_logger.info(f"Generated app: File {{file_name}} successfully uploaded to Hugging Face.") except Exception as e: - logging.error(f"Error uploading file {file_name} to Hugging Face: {e}") + app_logger.error(f"Generated app: 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.") + app_logger.warning(f"Generated app: File {{file_name}} not found locally, skipping upload.") + app_logger.info("Generated app: 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) + app_logger.error(f"Generated app: 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': {}} +def load_app_data(): + default_data = {{}} # AI will assume this is an empty dictionary to start with try: - with open(DATA_FILE, 'r', encoding='utf-8') as file: + with open(APP_DATA_FILE, 'r', encoding='utf-8') as file: data = json.load(file) - logging.info(f"Local data loaded successfully from {DATA_FILE}") + app_logger.info(f"Generated app: Local app data loaded successfully from {{APP_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'] = {} + app_logger.warning(f"Generated app: Local {{APP_DATA_FILE}} is not a dictionary. Using default empty data.") + return default_data 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): + except (FileNotFoundError, json.JSONDecodeError): + app_logger.warning(f"Generated app: Local file {{APP_DATA_FILE}} not found or corrupted. Using default empty data.") + # If file not found or corrupted, create an empty one locally 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}") + with open(APP_DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(default_data, f) + app_logger.info(f"Generated app: Created empty local file {{APP_DATA_FILE}} for corrupted/missing file.") + except Exception as create_e: + app_logger.error(f"Generated app: Failed to create empty local file {{APP_DATA_FILE}}: {{create_e}}") return default_data -def save_data(data): +def save_app_data(data): try: if not isinstance(data, dict): - logging.error("Attempted to save invalid data structure (not a dict). Aborting save.") + app_logger.error("Generated app: 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: + with open(APP_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) + app_logger.info(f"Generated app: App data successfully saved to {{APP_DATA_FILE}}") + _app_upload_db_to_hf(specific_file=APP_DATA_FILE) except Exception as e: - logging.error(f"Error saving data to {DATA_FILE}: {e}", exc_info=True) - - - -CATALOG_TEMPLATE = ''' - - - - - - Brand_Baaluu - Каталог - - - - - - -
-
-
- Brand_Baaluu Logo -

Brand_Baaluu

-
- -
- -
Наш адрес: {{ store_address }}
- -
- - {% 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 %} - {{ product['name'] }} - {% else %} - No Image - {% endif %} -
-
-

{{ product['name'] }}

-
{{ "%.2f"|format(product['price']) }} {{ currency_code }}
-

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

-
-
- - -
-
- {% endfor %} - {% if not products %} -

Товары пока не добавлены.

- {% endif %} -
-
- - - - - - - - - -
- - - - - -''' - -PRODUCT_DETAIL_TEMPLATE = ''' -
-

{{ product['name'] }}

-
-
- {% if product.get('photos') and product['photos']|length > 0 %} - {% for photo in product['photos'] %} -
-
- {{ product['name'] }} - фото {{ loop.index }} -
-
- {% endfor %} - {% else %} -
- Изображение отсутствует -
- {% endif %} -
- {% if product.get('photos') and product['photos']|length > 1 %} -
-
-
- {% endif %} -
- -
-

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

-

Цена: {{ "%.2f"|format(product['price']) }} {{ currency_code }}

-

Описание:
{{ product.get('description', 'Описание отсутствует.')|replace('\\n', '
')|safe }}

- {% set colors = product.get('colors', []) %} - {% if colors and colors|select('ne', '')|list|length > 0 %} -

Доступные цвета/варианты: {{ colors|select('ne', '')|join(', ') }}

- {% endif %} -
-
-''' - -ORDER_TEMPLATE = ''' - - - - - - Заказ №{{ order.id }} - Brand_Baaluu - - - - - -
- {% if order %} -

Ваш Заказ №{{ order.id }}

-

Дата создания: {{ order.created_at }}

- -

Товары в заказе

-
- {% for item in order.cart %} -
- {{ item.name }} -
- {{ item.name }} {% if item.color != 'N/A' %}({{ item.color }}){% endif %} - {{ "%.2f"|format(item.price) }} {{ currency_code }} × {{ item.quantity }} -
-
- {{ "%.2f"|format(item.price * item.quantity) }} {{ currency_code }} -
-
- {% endfor %} -
- -
-

Общая сумма товаров: {{ "%.2f"|format(order.total_price) }} {{ currency_code }}

-

ИТОГО К ОПЛАТЕ: {{ "%.2f"|format(order.total_price) }} {{ currency_code }}

-
- -
-

Статус заказа

-

Этот заказ был оформлен без входа в систему.

-

Пожалуйста, свяжитесь с нами по WhatsApp для подтверждения и уточнения деталей.

-
- -
- -
- - ← Вернуться в каталог - - - - {% else %} -

Ошибка

-

Заказ с таким ID не найден.

- ← Вернуться в каталог - {% endif %} -
- - -''' - -ADMIN_TEMPLATE = ''' - - - - - - Админ-панель - Brand_Baaluu - - - - - -
-
-
- Brand_Baaluu Logo -

Админ-панель Brand_Baaluu

-
- Перейти в каталог -
- - - {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} -
{{ message }}
- {% endfor %} - {% endif %} - {% endwith %} - -
-

Синхронизация с Датацентром

-
-
- -
-
- -
-
-

Резервное копирование происходит автоматически каждые 30 минут, а также после каждого сохранения данных. Используйте эти кнопки для немедленной синхронизации.

-
- -
-
-
-

Управление категориями

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

Существующие категории:

- {% if categories %} -
- {% for category in categories %} -
- {{ category }} -
- - - -
-
- {% endfor %} -
- {% else %} -

Категорий пока нет.

- {% endif %} -
-
- -
-
-

Информация

-

Управление пользователями отключено, так как сайт не требует входа.

-

Заказы создаются анонимно и должны быть подтверждены через WhatsApp.

-
-
-
- -
-

Управление товарами

-
- Добавить новый товар -
-
- - - - - - - - - - - - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
-
-
- -

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

- {% if products %} -
- {% for product in products %} -
-
-
- {% if product.get('photos') %} - - Фото - - {% else %} - Нет фото - {% endif %} -
-
-

- {{ product['name'] }} - {% if product.get('in_stock', True) %} - В наличии - {% else %} - Нет в наличии - {% endif %} - {% if product.get('is_top', False) %} - Топ - {% endif %} -

-

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

-

Цена: {{ "%.2f"|format(product['price']) }} {{ currency_code }}

-

Описание: {{ product.get('description', 'N/A')[:150] }}{% if product.get('description', '')|length > 150 %}...{% endif %}

- {% set colors = product.get('colors', []) %} -

Цвета/Вар-ты: {{ colors|select('ne', '')|join(', ') if colors|select('ne', '')|list|length > 0 else 'Нет' }}

- {% if product.get('photos') and product['photos']|length > 1 %} -

(Всего фото: {{ product['photos']|length }})

- {% endif %} -
-
- -
- -
- - - -
-
- -
-

Редактирование: {{ product['name'] }}

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

Текущие фото:

-
- {% for photo in product['photos'] %} - Фото {{ loop.index }} - {% endfor %} -
- {% endif %} - -
- {% set current_colors = product.get('colors', []) %} - {% if current_colors and current_colors|select('ne', '')|list|length > 0 %} - {% for color in current_colors %} - {% if color.strip() %} -
- - -
- {% endif %} - {% endfor %} - {% else %} -
- - -
- {% endif %} -
- -
-
- - -
-
- - -
-
- -
-
-
- {% endfor %} -
- {% else %} -

Товаров пока нет.

- {% endif %} -
- -
- - - - -''' - - - -@app.route('/') -def catalog(): - data = load_data() - all_products = data.get('products', []) - categories = sorted(data.get('categories', [])) - - products_in_stock = [p for p in all_products if p.get('in_stock', True)] - products_sorted = sorted(products_in_stock, key=lambda p: (not p.get('is_top', False), p.get('name', '').lower())) - - return render_template_string( - CATALOG_TEMPLATE, - products=products_sorted, - categories=categories, - repo_id=REPO_ID, - store_address=STORE_ADDRESS, - currency_code=CURRENCY_CODE - ) +def app_periodic_backup(): + backup_interval = 1800 # 30 minutes + app_logger.info(f"Generated app: Setting up periodic backup every {{backup_interval}} seconds.") + while True: + time.sleep(backup_interval) + app_logger.info("Generated app: Starting periodic backup...") + _app_upload_db_to_hf(specific_file=APP_DATA_FILE) + app_logger.info("Generated app: Periodic backup finished.") +# --- END Hugging Face Sync System --- +""".format(generated_app_repo_id=GENERATED_APP_REPO_ID) -@app.route('/product/') -def product_detail(index): - data = load_data() - all_products = data.get('products', []) - products_in_stock = [p for p in all_products if p.get('in_stock', True)] - products_sorted = sorted(products_in_stock, key=lambda p: (not p.get('is_top', False), p.get('name', '').lower())) +def generate_flask_app_code(user_prompt): try: - product = products_sorted[index] - except IndexError: - logging.warning(f"Attempted access to non-existent or out-of-stock product with index {index}") - return "Товар не найден или отсутствует в наличии.", 404 - - return render_template_string( - PRODUCT_DETAIL_TEMPLATE, - product=product, - repo_id=REPO_ID, - currency_code=CURRENCY_CODE + genai.configure(api_key=API_KEY_INTERNAL) + except Exception as e: + logging.error(f"Error configuring GenAI: {e}") + raise ValueError(f"Не удалось настроить Google AI. Проблема с конфигурацией. Ошибка: {e}") + + if not user_prompt or not user_prompt.strip(): + raise ValueError("Текстовый запрос (промпт) не может быть пустым.") + + system_instruction = ( + "You are an expert Python Flask developer. Your task is to generate the core logic " + "for a single-file Python Flask application based on the user's request. " + "The application should strictly adhere to the following:\n" + "1. Define a Flask application instance: `app = Flask(__name__)`.\n" + "2. Implement basic routes (e.g., `/`, `/add`, `/delete/`) as needed by the user's request.\n" + "3. Manage all application data using two provided functions: `load_app_data()` to read data " + " from `app_data.json`, and `save_app_data(data)` to write data back to `app_data.json`. " + " Assume `load_app_data()` returns an empty dictionary `{}` if the file is new or empty. " + " Your code should define the structure of this dictionary (e.g., `{'items': []}`).\n" + "4. All HTML, CSS, and JavaScript must be embedded directly within Python strings " + " and rendered using `render_template_string()`. Do NOT use `render_template()` with external files, " + " or external CSS/JS links. All visual styles should be in `