WIP: list credits for all serving providers; pin 511NY imagery to https

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-10-04 17:56:09 -04:00
co-authored by Claude Opus 5.5
parent aa5b71d853
commit af00139079
3 changed files with 20 additions and 9 deletions
+5 -1
View File
@@ -62,9 +62,13 @@ class Ny511Provider(CameraProvider):
async def fetch_catalog(self) -> List[Camera]: async def fetch_catalog(self) -> List[Camera]:
rows = await ny511._fetch("getcameras", ny511.normalize_camera) rows = await ny511._fetch("getcameras", ny511.normalize_camera)
# Hosts for the 511x imagery are unverified, so pin the scheme at least:
# only https URLs reach the browser.
def https(u):
return u if isinstance(u, str) and u.startswith("https://") else None
out = [ out = [
make_camera("ny511", r["id"], r["lat"], r["lon"], name=r["name"], roadway=r["roadway"], make_camera("ny511", r["id"], r["lat"], r["lon"], name=r["name"], roadway=r["roadway"],
direction=r["direction"], image_url=r["image_url"], video_url=r["video_url"]) direction=r["direction"], image_url=https(r["image_url"]), video_url=https(r["video_url"]))
for r in rows for r in rows
] ]
return require_parsed(len(rows), out, "511NY") return require_parsed(len(rows), out, "511NY")
+8 -6
View File
@@ -181,18 +181,20 @@ async def cameras_in_bbox(bbox: BBox) -> dict:
"""Cameras from every SERVING provider whose coverage meets ``bbox``. """Cameras from every SERVING provider whose coverage meets ``bbox``.
Providers that are off are never fetched.""" Providers that are off are never fetched."""
conf = await get_settings() conf = await get_settings()
live = [pid for pid, cat in CATALOGS.items() serving = [pid for pid in CATALOGS if effective(PROVIDERS[pid], conf.get(pid))[0]]
if effective(PROVIDERS[pid], conf.get(pid))[0] and bbox_intersects(PROVIDERS[pid].meta.coverage, bbox)] live = [pid for pid in serving if bbox_intersects(PROVIDERS[pid].meta.coverage, bbox)]
await asyncio.gather(*(CATALOGS[pid].get() for pid in live)) await asyncio.gather(*(CATALOGS[pid].get() for pid in live))
cams: List[Camera] = [] cams: List[Camera] = []
providers = [] providers = []
for pid in live: # Every SERVING provider is listed (credits included) even when the bbox
# misses it, so a client holding one of its cameras from another view can
# still render the required attribution. Only intersecting ones are fetched.
for pid in serving:
cat = CATALOGS[pid] cat = CATALOGS[pid]
hits = in_bbox(cat.items, bbox) hits = in_bbox(cat.items, bbox) if pid in live else []
room = max(0, MAX_ITEMS - len(cams)) room = max(0, MAX_ITEMS - len(cams))
cams.extend(hits[:room]) cams.extend(hits[:room])
providers.append({"id": pid, "name": PROVIDERS[pid].meta.name, **credit(pid), providers.append({"id": pid, "name": PROVIDERS[pid].meta.name, **credit(pid),
"fetched_at": cat.fetched_at, "error": cat.error, "fetched_at": cat.fetched_at, "error": cat.error,
"total_in_bbox": len(hits), "truncated": len(hits) > room}) "total_in_bbox": len(hits), "truncated": len(hits) > room})
return {"cameras": cams, "providers": providers, return {"cameras": cams, "providers": providers, "serving_count": len(serving)}
"serving_count": sum(1 for pid in CATALOGS if effective(PROVIDERS[pid], conf.get(pid))[0])}
+7 -2
View File
@@ -148,8 +148,12 @@ def test_query_returns_attribution_and_skips_providers_outside_bbox(store):
patch.object(registry.PROVIDERS["tfl"], "fetch_catalog", tfl_fetch): patch.object(registry.PROVIDERS["tfl"], "fetch_catalog", tfl_fetch):
out = run(registry.cameras_in_bbox((-34.0, 151.0, -33.7, 151.4))) out = run(registry.cameras_in_bbox((-34.0, 151.0, -33.7, 151.4)))
assert [c["id"] for c in out["cameras"]] == ["nsw:x"] assert [c["id"] for c in out["cameras"]] == ["nsw:x"]
assert out["providers"][0]["attribution"] == "Transport for NSW, CC BY 4.0" by_id = {p["id"]: p for p in out["providers"]}
assert by_id["nsw"]["attribution"] == "Transport for NSW, CC BY 4.0"
assert tfl_fetch.await_count == 0 # London never fetched for a Sydney bbox assert tfl_fetch.await_count == 0 # London never fetched for a Sydney bbox
# ...but its credit is still listed, for cameras the client holds from another view
assert by_id["tfl"]["attribution"] == "Powered by TfL Open Data" and by_id["tfl"]["total_in_bbox"] == 0
assert out["serving_count"] == 2
# ── admin routes ───────────────────────────────────────────────────────────── # ── admin routes ─────────────────────────────────────────────────────────────
@@ -263,10 +267,11 @@ def test_ontario_needs_its_key(monkeypatch):
def test_ny511_provider_wraps_legacy_client_and_carries_daa_metadata(): def test_ny511_provider_wraps_legacy_client_and_carries_daa_metadata():
row = {"id": "NYSDOT-1", "lat": 41.03, "lon": -73.76, "name": "I-287", "roadway": "I-287", row = {"id": "NYSDOT-1", "lat": 41.03, "lon": -73.76, "name": "I-287", "roadway": "I-287",
"direction": "Northbound", "image_url": "https://x/1", "video_url": None} "direction": "Northbound", "image_url": "https://x/1", "video_url": "javascript:alert(1)"}
with patch.object(regional.ny511, "_fetch", AsyncMock(return_value=[row])): with patch.object(regional.ny511, "_fetch", AsyncMock(return_value=[row])):
cams = run(regional.Ny511Provider().fetch_catalog()) cams = run(regional.Ny511Provider().fetch_catalog())
assert cams[0]["id"] == "ny511:NYSDOT-1" and cams[0]["heading_deg"] == 0 assert cams[0]["id"] == "ny511:NYSDOT-1" and cams[0]["heading_deg"] == 0
assert cams[0]["video_url"] is None # non-https never reaches the browser
lic = regional.Ny511Provider.meta.licence lic = regional.Ny511Provider.meta.licence
assert lic.attribution == "powered by 511NY" and lic.attribution_url == "https://511ny.org" assert lic.attribution == "powered by 511NY" and lic.attribution_url == "https://511ny.org"
assert "with all faults" in lic.disclaimer and lic.gated assert "with all faults" in lic.disclaimer and lic.gated