sync
This commit is contained in:
+32
-2
@@ -109,6 +109,17 @@ def baits(db: Db, limit: int = Query(200, ge=1, le=500), offset: int = Query(0,
|
||||
return list(db.scalars(select(Bait).order_by(Bait.name, Bait.id).offset(offset).limit(limit)))
|
||||
|
||||
|
||||
@app.get("/api/v1/public-spot-pages")
|
||||
def public_spot_pages(db: Db, limit: int = Query(500, ge=1, le=500), offset: int = Query(0, ge=0)) -> list[str]:
|
||||
rows = db.execute(select(Waterbody.slug, Spot.x, Spot.y, Fish.slug)
|
||||
.select_from(CatchReport).join(Spot, CatchReport.spot_id == Spot.id)
|
||||
.join(Waterbody, Spot.waterbody_id == Waterbody.id).join(Fish, CatchReport.fish_id == Fish.id)
|
||||
.where(CatchReport.moderation_status == ModerationStatus.approved, CatchReport.deleted_at.is_(None))
|
||||
.distinct().order_by(Waterbody.slug, Spot.x, Spot.y, Fish.slug).offset(offset).limit(limit))
|
||||
return [path for water, x, y, fish in rows for path in
|
||||
(f"/spots/{water}-{x}x{y}", f"/waterbodies/{water}/{fish}")]
|
||||
|
||||
|
||||
@app.get("/api/v1/activity", response_model=list[ActivityOut])
|
||||
def activity(
|
||||
db: Db, response: Response, hours: int = Query(24),
|
||||
@@ -119,7 +130,8 @@ def activity(
|
||||
) -> list[ActivityOut]:
|
||||
if hours not in {6, 12, 24, 72}:
|
||||
raise HTTPException(status_code=422, detail="hours must be one of: 6, 12, 24, 72")
|
||||
response.headers["Cache-Control"] = f"public, max-age={settings.public_cache_seconds}"
|
||||
response.headers["Cache-Control"] = "no-store"
|
||||
generation = public_cache.generation()
|
||||
cache_key = ("activity", hours, waterbody, fish, method, sort, limit, offset)
|
||||
cached = public_cache.get(cache_key, settings.public_cache_seconds)
|
||||
if cached is not None:
|
||||
@@ -133,7 +145,7 @@ def activity(
|
||||
}
|
||||
rows.sort(key=keys[sort], reverse=True)
|
||||
response.headers["X-Cache"] = "MISS"
|
||||
return public_cache.set(cache_key, rows[offset:offset + limit])
|
||||
return public_cache.set(cache_key, rows[offset:offset + limit], generation=generation)
|
||||
|
||||
|
||||
def _spot_or_404(db: Session, spot_id: UUID) -> Spot:
|
||||
@@ -174,6 +186,24 @@ def spot_catches(spot_id: UUID, db: Db, limit: int = Query(50, ge=1, le=100), of
|
||||
return [CatchOut(id=r.id, fish=r.fish.name_ru, weight_g=r.weight_g, bait=r.bait.name if r.bait else None, player_name=r.player_name, caught_at=r.caught_at, reported_at=r.reported_at, retrieve_method=r.retrieve_method, retrieve_speed=r.retrieve_speed, source_system=_report_source(r), source_url=r.source_url) for r in reports]
|
||||
|
||||
|
||||
@app.get("/api/v1/spots/{spot_id}/timeline")
|
||||
def spot_timeline(spot_id: UUID, db: Db) -> list[dict]:
|
||||
_spot_or_404(db, spot_id)
|
||||
now = datetime.now(timezone.utc)
|
||||
buckets = []
|
||||
for index in range(6):
|
||||
start = now - timedelta(hours=(6 - index) * 12)
|
||||
end = start + timedelta(hours=12)
|
||||
count = db.scalar(select(func.count()).select_from(CatchReport).where(
|
||||
CatchReport.spot_id == spot_id,
|
||||
CatchReport.moderation_status == ModerationStatus.approved,
|
||||
CatchReport.deleted_at.is_(None),
|
||||
CatchReport.reported_at >= start, CatchReport.reported_at < end,
|
||||
)) or 0
|
||||
buckets.append({"start": start.isoformat(), "end": end.isoformat(), "count": count})
|
||||
return buckets
|
||||
|
||||
|
||||
def _report_source(report: CatchReport) -> str:
|
||||
provenance = (report.raw_payload or {}).get("provenance", {})
|
||||
if isinstance(provenance, dict) and provenance.get("source_system"):
|
||||
|
||||
Reference in New Issue
Block a user