emotion_art_bot_v2 / database.py
0xArctic's picture
Update
16bccf2 verified
# 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:]