diff --git a/apps/api/alembic/versions/48094a7d1b92_add_import_record_event_table.py b/apps/api/alembic/versions/48094a7d1b92_add_import_record_event_table.py new file mode 100644 index 0000000..0b64565 --- /dev/null +++ b/apps/api/alembic/versions/48094a7d1b92_add_import_record_event_table.py @@ -0,0 +1,27 @@ +"""add_import_record_event_table + +Revision ID: 48094a7d1b92 +Revises: 0013 +""" +from typing import Sequence, Union +from alembic import op +import sqlalchemy as sa + + +revision: str = '48094a7d1b92' +down_revision: Union[str, None] = '0013' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + +def upgrade() -> None: + op.create_table( + "import_record_event", + sa.Column("id", sa.Uuid(), server_default=sa.func.gen_random_uuid(), primary_key=True), + sa.Column("catch_report_id", sa.Uuid(), sa.ForeignKey("catch_report.id"), nullable=False, index=True), + sa.Column("import_run_id", sa.Uuid(), sa.ForeignKey("official_record_import.id"), nullable=False, index=True), + sa.Column("event_type", sa.String(20), nullable=False), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + ) + +def downgrade() -> None: + op.drop_table("import_record_event") diff --git a/apps/api/app/importer.py b/apps/api/app/importer.py index ba7b887..426b8b6 100644 --- a/apps/api/app/importer.py +++ b/apps/api/app/importer.py @@ -13,7 +13,7 @@ from sqlalchemy.orm import Session from rf4_research.official_parser import RecordsContractError, parse_official_records from .models import ( - Bait, BaitKind, CatchReport, Fish, ImportStatus, ModerationStatus, + Bait, BaitKind, CatchReport, Fish, ImportRecordEvent, ImportStatus, ModerationStatus, OfficialRecordImport, SourceType, Waterbody, ) @@ -185,12 +185,16 @@ def _import_records_locked(session: Session, *, url: str, region: str, category: bait = _bait(session, raw.bait) if raw.bait else None payload = asdict(raw) | {"record_date": raw.record_date.isoformat()} caught = datetime.combine(raw.record_date, time(), tzinfo=timezone.utc) + now = datetime.now(timezone.utc) if report is None: - session.add(CatchReport(fish=fish, waterbody=waterbody, bait=bait, spot=None, weight_g=raw.weight_g, caught_at=caught, reported_at=datetime.now(timezone.utc), player_name=raw.player, source_type=SourceType.official_record, source_url=url, source_external_id=key, source_confidence=100, moderation_status=ModerationStatus.approved, raw_payload=payload)) + report = CatchReport(fish=fish, waterbody=waterbody, bait=bait, spot=None, weight_g=raw.weight_g, caught_at=caught, reported_at=now, player_name=raw.player, source_type=SourceType.official_record, source_url=url, source_external_id=key, source_confidence=100, moderation_status=ModerationStatus.approved, raw_payload=payload) + session.add(report) + session.add(ImportRecordEvent(catch_report=report, import_run=run, event_type="created", created_at=now)) run.rows_created += 1 else: report.raw_payload = payload report.source_url = url + session.add(ImportRecordEvent(catch_report=report, import_run=run, event_type="updated", created_at=now)) run.rows_updated += 1 run.status = ImportStatus.success run.finished_at = datetime.now(timezone.utc) diff --git a/apps/api/app/models.py b/apps/api/app/models.py index 4068654..b3a44a2 100644 --- a/apps/api/app/models.py +++ b/apps/api/app/models.py @@ -212,3 +212,15 @@ class ExternalEntityAlias(Base): updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) fish: Mapped[Fish | None] = relationship() waterbody: Mapped[Waterbody | None] = relationship() + + +class ImportRecordEvent(Base): + """Track per-record import events for D09 revision history.""" + __tablename__ = "import_record_event" + id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) + catch_report_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("catch_report.id"), index=True) + import_run_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("official_record_import.id"), index=True) + event_type: Mapped[str] = mapped_column(String(20)) # created/updated/deleted + created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) + catch_report: Mapped[CatchReport] = relationship() + import_run: Mapped[OfficialRecordImport] = relationship()