Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
File size: 4,852 Bytes
064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c e74e470 064643c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
import asyncio
import json
import logging
from datetime import datetime, timedelta
from typing import List, Optional
import dropbox
from modules.dropbox.client import dbx
# Logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# Cache: key = folder_path, value = {"timestamp": datetime, "data": List[dict]}
_discourse_cache: dict[str, dict] = {}
CACHE_TTL = timedelta(hours=1)
FOLDER_PATH = "/_discourses"
async def fetch_discourses_from_dropbox() -> List[dict]:
"""
Fetch all discourse JSONs for a scripture from Dropbox with caching.
Expects files in "/_discourses/".
"""
loop = asyncio.get_running_loop()
folder_path = FOLDER_PATH
# Check cache
cache_entry = _discourse_cache.get(folder_path)
if cache_entry:
age = datetime.now() - cache_entry["timestamp"]
if age < CACHE_TTL:
logger.info(f"Using cached discourses for '{folder_path}' (age={age})")
return cache_entry["data"]
logger.info(f"Fetching discourses from Dropbox folder '{folder_path}'")
discourses: List[dict] = []
try:
# List folder contents (synchronously in executor)
res = await loop.run_in_executor(None, dbx.files_list_folder, folder_path)
for entry in res.entries:
if isinstance(entry, dropbox.files.FileMetadata) and entry.name.lower().endswith(".json"):
metadata, fres = await loop.run_in_executor(
None, dbx.files_download, f"{folder_path}/{entry.name}"
)
data = fres.content.decode("utf-8")
discourses.append(json.loads(data))
# Update cache
_discourse_cache[folder_path] = {"timestamp": datetime.now(), "data": discourses}
logger.info(f"Cached {len(discourses)} discourses for '{folder_path}'")
return discourses
except Exception as e:
logger.error(f"Error fetching discourses from '{folder_path}'", exc_info=e)
# fallback to cached data if available
if cache_entry:
logger.warning(f"Returning stale cached discourses for '{folder_path}'")
return cache_entry["data"]
else:
logger.warning(f"No cached discourses available for '{folder_path}'")
return []
async def get_discourse_summaries(page: int = 1, per_page: int = 10):
"""
Returns paginated summaries: id, topic_name, thumbnail_url.
Sorted by topic_name.
"""
all_discourses = await fetch_discourses_from_dropbox()
# Build summaries
summaries = [
{
"id": d.get("id"),
"topic_name": d.get("topic_name"),
"thumbnail_url": d.get("thumbnail_url"),
}
for d in all_discourses
]
summaries.sort(key=lambda x: (x.get("topic_name") or "").lower())
# Pagination
total_items = len(summaries)
total_pages = (total_items + per_page - 1) // per_page
if page < 1 or page > total_pages:
logger.warning(f"Invalid page {page}. Must be between 1 and {total_pages}")
return {"page": page, "per_page": per_page, "total_pages": total_pages, "total_items": total_items, "data": []}
start = (page - 1) * per_page
end = start + per_page
paginated = summaries[start:end]
return {
"page": page,
"per_page": per_page,
"total_pages": total_pages,
"total_items": total_items,
"data": paginated,
}
async def get_discourse_by_id(topic_id: int) -> Optional[dict]:
"""
Fetch a single discourse JSON by topic_id from Dropbox.
Uses in-memory caching per file.
"""
loop = asyncio.get_running_loop()
file_path = f"{FOLDER_PATH}/{topic_id}.json"
# Check cache
cache_entry = _discourse_cache.get(file_path)
if cache_entry:
age = datetime.now() - cache_entry["timestamp"]
if age < CACHE_TTL:
logger.info(f"Using cached discourse for topic {topic_id} (age={age})")
return cache_entry["data"]
try:
logger.info(f"Fetching discourse {topic_id} from Dropbox: {file_path}")
metadata, res = await loop.run_in_executor(None, dbx.files_download, file_path)
data = res.content.decode("utf-8")
discourse = json.loads(data)
# Update cache
_discourse_cache[file_path] = {"timestamp": datetime.now(), "data": discourse}
return discourse
except dropbox.exceptions.HttpError as e:
logger.error(f"Dropbox file not found: {file_path}", exc_info=e)
return None
except Exception as e:
logger.error(f"Error fetching discourse {topic_id}", exc_info=e)
# fallback to cached data if available
if cache_entry:
logger.warning(f"Returning stale cached discourse for topic {topic_id}")
return cache_entry["data"]
return None |