Files
thejayman77 ed814c97b9 Daily Art engine: museum-guide blurb (grounded LLM) + extracted palette
- daily_art gains blurb + palette columns (idempotent migration).
- art._palette: Pillow median-cut to ~5 hex colors from the cached image (best-
  effort → [] on any failure). art._blurb: a warm 2-3 sentence "what you're
  looking at" note grounded in the Met catalogue (title/artist/bio/date/medium/
  classification/culture/tags). Prompt leans on context/significance and the
  title+tags for subject — explicitly NOT asserting literal composition (figure
  counts/poses) it can't see, since the model can't view the image. Markdown
  stripped from the output.
- pick_daily generates both (client optional → blurb skipped when absent); cycle
  + art CLI pass an LLM client. /api/art/today exposes blurb + palette.
- Backfilled the last 3 days on host (Veteran / Magnolia Vase / Bierstadt).
- scripts/art_blurb_palette_backfill.py for in-place backfill (no re-pick).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:12:54 -04:00

50 lines
2.2 KiB
Python

"""/api/art image + today endpoints: GET and HEAD both work (HEAD must not 404 — the
share /a/{id} HEAD gap bit us once), and the today payload carries the lightbox's
full-res URL."""
import pytest
from fastapi.testclient import TestClient
@pytest.fixture
def client(tmp_path, monkeypatch):
db = tmp_path / "t.sqlite3"
cache = tmp_path / "art_cache"
monkeypatch.setenv("GOODNEWS_DB", str(db))
monkeypatch.setenv("GOODNEWS_ART_CACHE", str(cache))
monkeypatch.setenv("GOODNEWS_PUBLIC_BASE_URL", "https://upbeatbytes.com")
import importlib
import goodnews.api as api
importlib.reload(api)
from goodnews.db import connect, init_db
c = connect(str(db)); init_db(c)
c.execute(
"INSERT INTO daily_art (art_date, source, object_id, title, artist, date_text, "
"medium, department, credit, source_url, image_file, image_url_full, is_public_domain) "
"VALUES ('2026-06-21','met',10154,'Lander''s Peak','Bierstadt','1863','Oil','Paintings',"
"'Gift','https://met/10154','10154.jpg','https://met/full.jpg',1)"
)
c.execute("UPDATE daily_art SET blurb=?, palette=? WHERE object_id=10154",
("A luminous western vista.", '["#7fb4cf", "#c79a3c"]'))
c.commit(); c.close()
cache.mkdir(parents=True, exist_ok=True)
(cache / "10154.jpg").write_bytes(b"\xff\xd8\xff" + b"x" * 5000) # web-large display copy
return api.create_app()
def test_image_get_and_head(client):
tc = TestClient(client)
assert tc.get("/api/art/image/10154").status_code == 200
assert tc.head("/api/art/image/10154").status_code == 200 # HEAD must not 404
# ?size=full falls back to the display copy when no -full is cached
assert tc.get("/api/art/image/10154?size=full").status_code == 200
assert tc.get("/api/art/image/9999").status_code == 404
def test_today_exposes_full_res_url(client):
a = TestClient(client).get("/api/art/today").json()
assert a["image_url"] == "/api/art/image/10154"
assert a["image_url_large"] == "/api/art/image/10154?size=full"
assert a["license"] == "Public Domain (CC0)" and a["museum"] == "The Met"
assert a["blurb"] == "A luminous western vista."
assert a["palette"] == ["#7fb4cf", "#c79a3c"] # parsed from stored JSON