feat: enable safe community auto publishing

This commit is contained in:
ik
2026-09-07 13:00:58 +07:00
parent c4d1c8b87b
commit 7217356594
10 changed files with 130 additions and 21 deletions
+39 -2
View File
@@ -8,7 +8,8 @@ from urllib.parse import urlparse
from sqlalchemy import select
from sqlalchemy.orm import Session
from .models import DataSource, ExternalObservation
from .community_review import publish_observation
from .models import DataSource, ExternalEntityAlias, ExternalObservation
SOURCE_DEFAULTS = {
@@ -36,6 +37,7 @@ def stage_observations(
) -> tuple[int, int]:
fetched_at = fetched_at or datetime.now(timezone.utc)
created = updated = 0
touched: list[ExternalObservation] = []
for raw in records:
payload = _json_payload(raw)
source_system = _required(payload, "source_system", 50)
@@ -45,7 +47,7 @@ def stage_observations(
source = session.get(DataSource, source_system)
if source is None:
name, base_url, confidence = SOURCE_DEFAULTS[source_system]
source = DataSource(key=source_system, name=name, base_url=base_url, default_confidence=confidence, enabled=False)
source = DataSource(key=source_system, name=name, base_url=base_url, default_confidence=confidence, enabled=True)
session.add(source)
session.flush()
observation = session.scalar(select(ExternalObservation).where(
@@ -78,10 +80,45 @@ def stage_observations(
for key, value in values.items():
setattr(observation, key, value)
updated += 1
touched.append(observation)
session.commit()
for observation in touched:
_auto_publish(session, observation)
return created, updated
def _auto_publish(session: Session, observation: ExternalObservation) -> bool:
"""Publish only complete observations covered by previously reviewed aliases."""
if (
observation.status not in {"staged", "mapped", "ready"}
or not observation.source.enabled
or observation.fish_external_id is None
or observation.waterbody_external_id is None
or observation.x is None
or observation.y is None
or observation.weight_g is None
):
return False
fish_alias = session.scalar(select(ExternalEntityAlias).where(
ExternalEntityAlias.source_system == observation.source_system,
ExternalEntityAlias.entity_type == "fish",
ExternalEntityAlias.external_id == observation.fish_external_id,
))
waterbody_alias = session.scalar(select(ExternalEntityAlias).where(
ExternalEntityAlias.source_system == observation.source_system,
ExternalEntityAlias.entity_type == "waterbody",
ExternalEntityAlias.external_id == observation.waterbody_external_id,
))
if fish_alias is None or fish_alias.fish is None or waterbody_alias is None or waterbody_alias.waterbody is None:
return False
observation.fish = fish_alias.fish
observation.waterbody = waterbody_alias.waterbody
observation.status = "ready"
observation.review_note = "Automatically matched by previously reviewed source aliases"
publish_observation(session, observation)
return True
def _json_payload(raw: dict[str, Any]) -> dict[str, Any]:
if not isinstance(raw, dict):
raise CommunityImportError("each observation must be an object")