feat: add bulk external observation mapping
This commit is contained in:
@@ -21,7 +21,7 @@ class ExternalReviewError(ValueError):
|
|||||||
|
|
||||||
def map_observation(
|
def map_observation(
|
||||||
session: Session, observation: ExternalObservation, fish: Fish, waterbody: Waterbody,
|
session: Session, observation: ExternalObservation, fish: Fish, waterbody: Waterbody,
|
||||||
*, note: str | None = None,
|
*, note: str | None = None, commit: bool = True,
|
||||||
) -> ExternalObservation:
|
) -> ExternalObservation:
|
||||||
if observation.status == "published":
|
if observation.status == "published":
|
||||||
raise ExternalReviewError("published observation cannot be remapped")
|
raise ExternalReviewError("published observation cannot be remapped")
|
||||||
@@ -43,7 +43,8 @@ def map_observation(
|
|||||||
observation.status = "ready" if _complete(observation) else "mapped"
|
observation.status = "ready" if _complete(observation) else "mapped"
|
||||||
_save_alias(session, observation, "fish", observation.fish_external_id or observation.fish_name, fish=fish)
|
_save_alias(session, observation, "fish", observation.fish_external_id or observation.fish_name, fish=fish)
|
||||||
_save_alias(session, observation, "waterbody", observation.waterbody_external_id or observation.waterbody_name, waterbody=waterbody)
|
_save_alias(session, observation, "waterbody", observation.waterbody_external_id or observation.waterbody_name, waterbody=waterbody)
|
||||||
session.commit()
|
if commit:
|
||||||
|
session.commit()
|
||||||
return observation
|
return observation
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from rf4_research.media_assets import publish_quality_upgrades, rollback_quality
|
|||||||
from ..media_catalog import MEDIA_ROOT, review_assets, review_file
|
from ..media_catalog import MEDIA_ROOT, review_assets, review_file
|
||||||
from ..models import CatchReport, CommunityImportRun, DataSource, ExternalObservation, Fish, ModerationEvent, ModerationStatus, OfficialRecordImport, SourceType, Waterbody
|
from ..models import CatchReport, CommunityImportRun, DataSource, ExternalObservation, Fish, ModerationEvent, ModerationStatus, OfficialRecordImport, SourceType, Waterbody
|
||||||
from ..public_cache import public_cache
|
from ..public_cache import public_cache
|
||||||
from ..schemas import AdminCatchReportOut, AdminMediaDecision, AdminMediaReviewOut, AdminMediaRollback, AdminModerationHistoryOut, AdminSourceStatusOut, CatchReportCreated, ExternalAliasSuggestionOut, ExternalObservationAction, ExternalObservationBulkDecision, ExternalObservationBulkResult, ExternalObservationDecision, ExternalObservationMapping, ExternalObservationOut, ExternalObservationPublished, ImportRunOut, ModerationBulkResult, ModerationBulkUpdate, ModerationUpdate
|
from ..schemas import AdminCatchReportOut, AdminMediaDecision, AdminMediaReviewOut, AdminMediaRollback, AdminModerationHistoryOut, AdminSourceStatusOut, CatchReportCreated, ExternalAliasSuggestionOut, ExternalObservationAction, ExternalObservationBulkDecision, ExternalObservationBulkMapping, ExternalObservationBulkResult, ExternalObservationDecision, ExternalObservationMapping, ExternalObservationOut, ExternalObservationPublished, ImportRunOut, ModerationBulkResult, ModerationBulkUpdate, ModerationUpdate
|
||||||
from ..storage import delete_screenshot, signed_screenshot_url
|
from ..storage import delete_screenshot, signed_screenshot_url
|
||||||
from ..time_utils import aware
|
from ..time_utils import aware
|
||||||
|
|
||||||
@@ -343,6 +343,34 @@ def admin_map_external_observation(
|
|||||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/api/v1/admin/external-observations/bulk-mapping", response_model=ExternalObservationBulkResult)
|
||||||
|
def admin_map_external_observations_bulk(
|
||||||
|
payload: ExternalObservationBulkMapping, db: Db, _: Annotated[str, Depends(_admin)],
|
||||||
|
) -> ExternalObservationBulkResult:
|
||||||
|
ids = [item.id for item in payload.items]
|
||||||
|
observations = list(db.scalars(select(ExternalObservation).where(ExternalObservation.id.in_(ids)).with_for_update()))
|
||||||
|
by_id = {item.id: item for item in observations}
|
||||||
|
expected = {item.id: item.expected_version for item in payload.items}
|
||||||
|
if len(by_id) != len(ids) or any(
|
||||||
|
item.status in {"published", "withdrawn"} or item.moderation_version != expected[item.id]
|
||||||
|
for item in observations
|
||||||
|
):
|
||||||
|
raise HTTPException(status_code=409, detail="one or more observations changed; reload the queue")
|
||||||
|
fish = db.scalar(select(Fish).where(Fish.slug == payload.fish_slug))
|
||||||
|
waterbody = db.scalar(select(Waterbody).where(Waterbody.slug == payload.waterbody_slug))
|
||||||
|
if fish is None or waterbody is None:
|
||||||
|
raise HTTPException(status_code=422, detail="unknown fish or waterbody")
|
||||||
|
try:
|
||||||
|
for item in observations:
|
||||||
|
item.moderation_version += 1
|
||||||
|
map_observation(db, item, fish, waterbody, note=payload.note, commit=False)
|
||||||
|
db.commit()
|
||||||
|
except ExternalReviewError as exc:
|
||||||
|
db.rollback()
|
||||||
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||||
|
return ExternalObservationBulkResult(updated=len(observations))
|
||||||
|
|
||||||
|
|
||||||
@router.post("/api/v1/admin/external-observations/{observation_id}/publish", response_model=ExternalObservationPublished)
|
@router.post("/api/v1/admin/external-observations/{observation_id}/publish", response_model=ExternalObservationPublished)
|
||||||
def admin_publish_external_observation(
|
def admin_publish_external_observation(
|
||||||
observation_id: UUID, payload: ExternalObservationAction, db: Db, _: Annotated[str, Depends(_admin)],
|
observation_id: UUID, payload: ExternalObservationAction, db: Db, _: Annotated[str, Depends(_admin)],
|
||||||
|
|||||||
@@ -413,6 +413,13 @@ class ExternalObservationBulkDecision(BaseModel):
|
|||||||
items: list[ExternalObservationBulkItem] = Field(min_length=1, max_length=50)
|
items: list[ExternalObservationBulkItem] = Field(min_length=1, max_length=50)
|
||||||
|
|
||||||
|
|
||||||
|
class ExternalObservationBulkMapping(BaseModel):
|
||||||
|
fish_slug: str
|
||||||
|
waterbody_slug: str
|
||||||
|
note: str | None = Field(default=None, max_length=1000)
|
||||||
|
items: list[ExternalObservationBulkItem] = Field(min_length=1, max_length=50)
|
||||||
|
|
||||||
|
|
||||||
class ExternalObservationBulkResult(BaseModel):
|
class ExternalObservationBulkResult(BaseModel):
|
||||||
updated: int
|
updated: int
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user