feat: expose explicit source conflicts

This commit is contained in:
ik
2026-09-21 07:31:17 +07:00
parent bbed832eb8
commit 36d704d854
11 changed files with 41 additions and 11 deletions
+11
View File
@@ -80,6 +80,7 @@ def activity_rows(
sources=coordinate_sources,
coordinate_precision=coordinate_precision,
coordinate_sources=coordinate_sources,
source_conflicts=_source_conflicts(items),
))
return sorted(result, key=lambda row: (row.activity_score, row.last_confirmed_at), reverse=True)
@@ -95,6 +96,16 @@ def _source_system(report: CatchReport) -> str:
return "manual-import"
def _source_conflicts(reports: list[CatchReport]) -> list[str]:
conflicts: set[str] = set()
for report in reports:
provenance = (report.raw_payload or {}).get("provenance", {})
values = provenance.get("conflicts", []) if isinstance(provenance, dict) else []
if isinstance(values, list):
conflicts.update(str(value).strip() for value in values if str(value).strip())
return sorted(conflicts)
def _coordinate_precision(report: CatchReport) -> str:
provenance = (report.raw_payload or {}).get("provenance", {})
value = provenance.get("coordinate_precision") if isinstance(provenance, dict) else None
+2 -2
View File
@@ -7,7 +7,7 @@ from fastapi import APIRouter, HTTPException, Query, Response
from sqlalchemy import func, select
from sqlalchemy.orm import Session, joinedload, selectinload
from ..activity import activity_rows
from ..activity import _source_conflicts, activity_rows
from ..config import settings
from ..dependencies import Db
from ..models import CatchReport, ModerationStatus, SourceType, Spot, Waterbody
@@ -76,7 +76,7 @@ def spot_detail(spot_id: UUID, db: Db) -> SpotOut:
precisions = [item.get("coordinate_precision") for item in provenance]
precision = max((value for value in precisions if value in {"exact", "approximate", "area", "missing"}), key={"exact": 0, "approximate": 1, "area": 2, "missing": 3}.get, default="exact")
sources = sorted({str(item.get("source_system")) for item in provenance if item.get("source_system")}) or ["players"]
return SpotOut(id=spot.id, waterbody_slug=spot.waterbody.slug, waterbody=spot.waterbody.name_ru, x=spot.x, y=spot.y, description=spot.description, catches_24h=count_since(timedelta(hours=24)), catches_3d=count_since(timedelta(days=3)), catches_7d=count_since(timedelta(days=7)), top_baits=[name for name, _ in bait_counts.most_common(5)], coordinate_precision=precision, coordinate_sources=sources)
return SpotOut(id=spot.id, waterbody_slug=spot.waterbody.slug, waterbody=spot.waterbody.name_ru, x=spot.x, y=spot.y, description=spot.description, catches_24h=count_since(timedelta(hours=24)), catches_3d=count_since(timedelta(days=3)), catches_7d=count_since(timedelta(days=7)), top_baits=[name for name, _ in bait_counts.most_common(5)], coordinate_precision=precision, coordinate_sources=sources, source_conflicts=_source_conflicts(reports))
def _report_source(report: CatchReport) -> str:
+2
View File
@@ -102,6 +102,7 @@ class ActivityOut(BaseModel):
sources: list[str]
coordinate_precision: str
coordinate_sources: list[str]
source_conflicts: list[str] = Field(default_factory=list)
class PaginatedActivityOut(BaseModel):
@@ -160,6 +161,7 @@ class SpotOut(BaseModel):
top_baits: list[str]
coordinate_precision: str
coordinate_sources: list[str]
source_conflicts: list[str] = Field(default_factory=list)
class OfficialRecordOut(BaseModel):