Skip to content

feat: Add backend-driven page events with runtime lookup and maintenance task fixes

Lakshy Yarlagadda requested to merge mr-199-event-service into develop

Summary

This MR adds backend-driven runtime events for tool pages and fixes adjacent backend issues discovered during integration.

The main goal is to let the frontend resolve page-specific review filters from the backend using GET /api/v1/events/current and the X-Page-Route header, instead of relying only on client-side hardcoded defaults.

In addition to the event feature, this MR also includes:

  • timezone-safe cleanup logic in maintenance tasks
  • a test-safe fix for create_categories.py
  • unit coverage for the new event behavior and cleanup regressions

What Changed

1. Added backend event subsystem

This MR introduces a new Event model and associated API endpoints.

New event fields:

  • uid
  • name
  • description
  • page_context
  • filters
  • limit
  • is_active
  • start_date
  • end_date
  • created_by
  • created_at
  • updated_at

The filters field stores a JSONB representation of RecordReviewFilters. This allows the backend to persist the same review-filter shape used by the record review endpoint.

2. Added event endpoints

New routes under /api/v1/events:

  • POST /
  • GET /
  • GET /{event_id}
  • PUT /{event_id}
  • DELETE /{event_id}
  • GET /current

Behavior:

  • CRUD is admin-only
  • GET /current is runtime-facing
  • it reads X-Page-Route
  • it matches that value against Event.page_context
  • it returns the single active in-window event for that page
  • returns null when no event matches
  • returns 409 when multiple active events match the same page

3. Added filter serialization for stored event filters

RecordReviewFilters can contain enums, UUIDs, and dates, so this MR adds explicit JSON-safe serialization before persistence.

Examples:

  • category_ids are stored as strings
  • media_type, release_rights, and extraction_type are stored as enum values
  • published_date is ISO-serialized

This ensures the event payload can be stored in JSONB and returned consistently.

4. Added and restored Alembic migration chain

This branch now uses the restored multi-step event migration chain instead of a single collapsed migration.

Included:

  • c4d5e6f7a8b9_merge_event_and_language_heads.py
  • d5e6f7a8b9c0_add_event_service_tables.py

This was restored because the earlier collapsed version would have been unsafe for environments that had already seen the older event migration history.

5. Fixed maintenance cleanup datetime handling

app/tasks/maintenance.py previously compared timezone-aware cutoff values against naive timestamps from:

  • DB rows
  • file mtimes
  • chunk mtimes

This caused failures in cleanup tasks. The MR normalizes those values to UTC before comparison.

6. Removed import-time dotenv side effect in category utility

app/utils/create_categories.py no longer calls dotenv.load_dotenv() at import time. The load now happens in main(), which prevents unit-test failures caused by import-time file access.

Why This Change

This MR moves page-level review configuration into the backend so we can:

  • change review filters without redeploying the frontend
  • configure page-specific review campaigns centrally
  • support time-bound events
  • make the frontend runtime behavior more predictable and testable

The adjacent maintenance and utility fixes were included because they were exposed while validating the event flow and CI.

Files Changed

  • alembic/versions/c4d5e6f7a8b9_merge_event_and_language_heads.py
  • alembic/versions/d5e6f7a8b9c0_add_event_service_tables.py
  • app/api/v1/api.py
  • app/api/v1/endpoints/events.py
  • app/models/event.py
  • app/models/__init__.py
  • app/schemas/event.py
  • app/tasks/maintenance.py
  • app/utils/create_categories.py
  • tests/unit/api/v1/endpoints/test_events.py
  • tests/unit/tasks/test_maintenance_v2.py

Testing

Validated locally with:

uv run pytest tests/unit/api/v1/endpoints/test_events.py
uv run pytest tests/unit/tasks/test_maintenance_v2.py

Coverage added for:

  • missing header -> null
  • active matching event
  • future event ignored
  • expired event ignored
  • duplicate active matches -> 409
  • review-filter serialization
  • naive timestamp handling in maintenance cleanup

Notes

  • GET /api/v1/events/current already matches X-Page-Route directly against Event.page_context
  • if start_date and end_date are omitted, the event has no time restriction
  • the current validation is structural/schema-level; business rules like "only one active event per page" are enforced at runtime, not at DB-schema level
Edited by Lakshy Yarlagadda

Merge request reports

Loading