From 5d4937e57a1e9eb8adc12c3d0ee691dd214a1628 Mon Sep 17 00:00:00 2001 From: Matthew Ryan Dillon Date: Thu, 19 Jun 2025 08:38:55 -0400 Subject: [PATCH] caching --- README.md | 35 +++++++++++++++++++++++++++++++++++ main.py | 23 +++++++++++++++++++++-- pyproject.toml | 1 + uv.lock | 11 +++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e69de29..aed623e 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,35 @@ +# TRMNL Weather & Pollen Report + +A custom TRMNL plugin that fetches and displays weather and pollen data. + +## Setup + +1. Set up a virtual environment and install dependencies: + ```bash + python -m venv .venv + source .venv/bin/activate + uv sync + ``` + +2. Set required environment variables: + ```bash + export WEATHER_API_KEY="your_openweathermap_api_key" + export AUTH_TOKEN="your_chosen_auth_token" + ``` + +3. Run the application: + ```bash + fastapi run main.py --port 8887 + ``` + +## Docker + +Build and run with Docker: +```bash +docker build -t trmnl-report . +docker run -p 8887:8887 -e WEATHER_API_KEY=your_key -e AUTH_TOKEN=your_token trmnl-report +``` + +## API + +Access the API at `http://localhost:8887/?token=your_auth_token` \ No newline at end of file diff --git a/main.py b/main.py index 7233928..b9a42d9 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ import pprint import zoneinfo from fastapi import FastAPI, HTTPException +from cachetools import TTLCache import httpx @@ -16,6 +17,9 @@ eastern = zoneinfo.ZoneInfo("America/New_York") app = FastAPI() +# Initialize caches for 15 minutes +weather_cache = TTLCache(maxsize=100, ttl=900) # 15 minutes +pollen_cache = TTLCache(maxsize=100, ttl=900) # 15 minutes CONFIG = { # salem, ma @@ -89,6 +93,10 @@ def fallback_handler(func): @fallback_handler async def fetch_pollen(zipcode): + # Check the cache first + if zipcode in pollen_cache: + return pollen_cache[zipcode] + url = f"https://www.pollen.com/api/forecast/current/pollen/{zipcode}" headers = { "User-Agent": ( @@ -104,7 +112,7 @@ async def fetch_pollen(zipcode): response = await client.get(url, headers=headers) response.raise_for_status() data = response.json() - return [ + result = [ { "forecast_date": format_datetime( datetime.fromisoformat(data["ForecastDate"]) @@ -119,17 +127,25 @@ async def fetch_pollen(zipcode): ], } ] + # Cache the result + pollen_cache[zipcode] = result + return result @fallback_handler async def fetch_weather(lat, lon, weather_api_key): + # Check the cache first + cache_key = (lat, lon) + if cache_key in weather_cache: + return weather_cache[cache_key] + url = f"https://api.openweathermap.org/data/3.0/onecall?lat={lat}&lon={lon}&appid={weather_api_key}&units=imperial" async with httpx.AsyncClient() as client: response = await client.get(url) response.raise_for_status() data = response.json() current, periods = data["current"], data["daily"][:2] - return [ + result = [ { "forecast_date": format_datetime(datetime.fromtimestamp(current["dt"])), "current_temp": int(round(current["temp"])), @@ -154,6 +170,9 @@ async def fetch_weather(lat, lon, weather_api_key): ], } ] + # Cache the result + weather_cache[cache_key] = result + return result @app.get("/") diff --git a/pyproject.toml b/pyproject.toml index 621c953..30a8326 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ requires-python = ">=3.13" dependencies = [ "fastapi[standard]>=0.115.12", "httpx>=0.28.1", + "cachetools>=5.0.0", ] [tool.pyright] diff --git a/uv.lock b/uv.lock index 7fb8c1c..6008a3a 100644 --- a/uv.lock +++ b/uv.lock @@ -24,6 +24,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, ] +[[package]] +name = "cachetools" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/89/817ad5d0411f136c484d535952aef74af9b25e0d99e90cdffbe121e6d628/cachetools-6.1.0.tar.gz", hash = "sha256:b4c4f404392848db3ce7aac34950d17be4d864da4b8b66911008e430bc544587", size = 30714, upload-time = "2025-06-16T18:51:03.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/f0/2ef431fe4141f5e334759d73e81120492b23b2824336883a91ac04ba710b/cachetools-6.1.0-py3-none-any.whl", hash = "sha256:1c7bb3cf9193deaf3508b7c5f2a79986c13ea38965c5adcff1f84519cf39163e", size = 11189, upload-time = "2025-06-16T18:51:01.514Z" }, +] + [[package]] name = "certifi" version = "2025.4.26" @@ -390,12 +399,14 @@ name = "trmnl-report" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "cachetools" }, { name = "fastapi", extra = ["standard"] }, { name = "httpx" }, ] [package.metadata] requires-dist = [ + { name = "cachetools", specifier = ">=5.0.0" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" }, { name = "httpx", specifier = ">=0.28.1" }, ]