# database.py """ Модуль для работы с файловой базой данных (JSON) для хранения истории эмоций. ФИНАЛЬНАЯ ВЕРСИЯ: Решает проблему `PermissionError` на платформе Hugging Face Spaces, корректно обрабатывая права на примонтированную директорию `/data`. Принцип работы: 1. При запуске код проверяет, существует ли директория `/data` и доступна ли она для записи. 2. Если да, то база данных будет размещена в ней (постоянное хранилище). 3. Если нет (например, Persistent Storage не подключен), используется временная директория /tmp, чтобы избежать падения приложения. 4. В функции сохранения `save_emotion` более не делается попытка создать саму директорию `/data`, что и было причиной ошибки. """ import json import os import logging from datetime import datetime from typing import Any, Dict, List, Optional from filelock import FileLock logger = logging.getLogger(__name__) # --- Динамическая конфигурация путей к файлам --- DB_FILE = "history.json" LOCK_FILE = "history.json.lock" # Проверяем, запущено ли приложение на Hugging Face Spaces. if os.environ.get('SPACE_ID'): persistent_storage_path = "/data" # Проверяем, существует ли путь и есть ли у нас права на запись в него. if os.path.exists(persistent_storage_path) and os.access(persistent_storage_path, os.W_OK): # Если да, используем постоянное хранилище. DB_FILE = os.path.join(persistent_storage_path, "history.json") LOCK_FILE = "/tmp/history.json.lock" logger.info(f"Обнаружено доступное постоянное хранилище. База данных: {DB_FILE}") else: # Если нет, выводим предупреждение и используем временное хранилище. logger.warning("Постоянное хранилище в /data не найдено или недоступно. " "История будет теряться при перезапусках.") DB_FILE = "/tmp/history.json" LOCK_FILE = "/tmp/history.json.lock" def _read_data() -> Dict[str, Any]: """Вспомогательная функция для безопасного чтения данных из JSON-файла.""" if not os.path.exists(DB_FILE): return {} try: if os.path.getsize(DB_FILE) > 0: with open(DB_FILE, "r", encoding="utf-8") as f: return json.load(f) except (json.JSONDecodeError, FileNotFoundError): return {} return {} def save_emotion(user_id: int, emotion: str) -> None: """Сохраняет запись об эмоции пользователя в базу данных.""" lock = FileLock(LOCK_FILE, timeout=10) with lock: data = _read_data() user_id_str = str(user_id) if user_id_str not in data: data[user_id_str] = [] record = { "emotion": emotion, "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), } data[user_id_str].append(record) MAX_HISTORY_PER_USER = 200 if len(data[user_id_str]) > MAX_HISTORY_PER_USER: data[user_id_str] = data[user_id_str][-MAX_HISTORY_PER_USER:] try: # ИСПРАВЛЕНИЕ: Мы больше не пытаемся создать саму директорию /data. # Наша логика при запуске уже подтвердила, что она существует # и доступна. Мы можем сразу открывать файл для записи. # Функция open() сама создаст файл history.json, если его нет. with open(DB_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=4) except Exception as e: logger.error(f"КРИТИЧЕСКАЯ ОШИБКА: Не удалось сохранить данные в файл {DB_FILE}: {e}", exc_info=True) def get_user_history(user_id: int, limit: Optional[int] = 10) -> List[Dict[str, str]]: """Возвращает историю эмоций пользователя из базы данных.""" lock = FileLock(LOCK_FILE, timeout=10) with lock: data = _read_data() user_id_str = str(user_id) user_history = data.get(user_id_str, []) if limit is None: return user_history return user_history[-limit:]