diff --git a/.env.example b/.env.example index 09bdbab..c87c2b1 100644 --- a/.env.example +++ b/.env.example @@ -7,7 +7,7 @@ BRICKSET_API_KEY = "your_brickset_api_key" SQUARE_ENV = "sandbox" # or "production" # Public webhook URL that Square will POST events to -SQUARE_WEBHOOK_URL = "https://yourdomain.com/hook" +SQUARE_WEBHOOK_URL = "https://yourdomain.com/webhook" # Sandbox credentials - test environment (fake data, safe for development) SANDBOX_SQUARE_APP_ID = "your_sandbox_app_id" diff --git a/pyproject.toml b/pyproject.toml index 3256a07..a3d524d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ dependencies = [ "fastapi>=0.128.0", "uvicorn>=0.40.0", "gunicorn>=23.0.0", + "async-lru>=2.0.4", ] [build-system] diff --git a/src/abricksetapi.py b/src/abricksetapi.py index 317f1ee..295393c 100644 --- a/src/abricksetapi.py +++ b/src/abricksetapi.py @@ -11,6 +11,7 @@ import math import dotenv from typing import Dict, Any, Optional from datetime import datetime, timedelta +from async_lru import alru_cache from . import throttler Throttler = throttler.GlobalThrottler @@ -155,9 +156,10 @@ class AsyncBricksetAPI: self.logger.info("Successfully authenticated with Brickset API") return result + @alru_cache(maxsize=1000, ttl=604800) # Cache 1000 sets, 7 days TTL async def get_set(self, set_number: int) -> Dict[str, Any]: """ - Fetch LEGO set information asynchronously + Fetch LEGO set information asynchronously (cached for 7 days) Args: set_number (str): LEGO set number diff --git a/src/stats.py b/src/stats.py index d159fc3..d7d2380 100644 --- a/src/stats.py +++ b/src/stats.py @@ -7,6 +7,7 @@ import json from typing import Dict, Any from square import AsyncSquare from . import get_sq_env +from . import abricksetapi # Initialize Square client client = AsyncSquare(token=get_sq_env.SQUARE_TOKEN, environment=get_sq_env.environment) @@ -101,12 +102,62 @@ async def get_daily_stats() -> Dict[str, Any]: } -async def get_total_items() -> int: +async def get_brickset_cache_stats() -> Dict[str, Any]: """ - Count total items in LEGO category. + Get Brickset API cache statistics. Returns: - int: Total number of items + dict: { + "cache_enabled": bool, + "cache_hits": int, + "cache_misses": int, + "cache_size": int, + "cache_maxsize": int, + "hit_rate": float (0-100%) + } + """ + # Create a temporary client instance to access cache info + temp_client = abricksetapi.AsyncBricksetAPI() + + # Check if get_set has cache_info (it should with @alru_cache) + if hasattr(temp_client.get_set, "cache_info"): + info = await temp_client.get_set.cache_info() + hits = info.hits + misses = info.misses + total = hits + misses + hit_rate = (hits / total * 100) if total > 0 else 0.0 + + return { + "cache_enabled": True, + "cache_hits": hits, + "cache_misses": misses, + "cache_size": info.currsize, + "cache_maxsize": info.maxsize, + "hit_rate": round(hit_rate, 2), + } + else: + return { + "cache_enabled": False, + "cache_hits": 0, + "cache_misses": 0, + "cache_size": 0, + "cache_maxsize": 0, + "hit_rate": 0.0, + } + + +async def get_daily_stats() -> Dict[str, Any]: + """ + Gather all metrics for daily_stats table in a single pass. + + Returns: + dict: { + "total_items": int, + "enriched_items": int, + "missing_upcs": int, + "missing_images": int, + "total_catalog_value": float + } """ result = await client.catalog.search( object_types=["ITEM"], diff --git a/studspider.py b/studspider.py index 87cca51..00efa79 100644 --- a/studspider.py +++ b/studspider.py @@ -67,10 +67,11 @@ async def get_stats(): """ Get daily statistics for LEGO inventory. - Returns metrics about enrichment progress, missing data, and catalog value. + Returns metrics about enrichment progress, missing data, catalog value, and cache stats. """ try: daily_stats = await stats.get_daily_stats() + cache_stats = await stats.get_brickset_cache_stats() # Calculate completion percentage if daily_stats["total_items"] > 0: @@ -84,6 +85,7 @@ async def get_stats(): "status": "success", "stats": daily_stats, "completion_rate": round(completion_rate, 1), + "brickset_cache": cache_stats, } except Exception as e: return JSONResponse(