Backfill: renormalize 1,069 historical oracle-fallback rows
Apply libs.parser.event_type_normalizer (added in commit 722e5cf) to
existing events whose parser_version LIKE 'oracle-fallback%' so historical
rows match the forward-going normalization wired into the parser.
Implementation:
- New renormalize_oracle_fallback_events() in apps/pipeline/event_parser/main.py
- SELECT filter Event.parser_version.like("oracle-fallback%") — broader
than a hardcoded IN list, so already-aligned values skip naturally and
future additions to _ORACLE_TO_STRATEGY get picked up automatically
- New --renormalize-oracle-fallback CLI flag, chainable with --reparse
- JobRun row written (job_name=event_parser_renormalize_oracle)
- Per-row renormalize_event_updated INFO log + final renormalize_done
summary with transition counters
Live DB run: seen=2937 / updated=1069 / skipped=1868 / errors=0.
Wall ~2 sec (pure DB UPDATEs, no Oracle calls).
Transitions:
earnings_result -> earnings_release : 412
shareholder_vote -> other_material_event : 409
regulation_fd -> guidance_update : 237
acquisition_disposition -> other_material_event : 7
other -> other_material_event : 4
Unmapped Oracle values (financial_obligation 225, articles_amendment 81,
contract_termination 48, etc.) preserved verbatim — honest filter-drop.
Stale-by-design (mirrors existing reparse_events convention):
- Event.event_id PK still embeds old raw event_type substring
- EventParse.output_json["event_type"] still carries raw Oracle value
Strategies read Event.event_type, not those fields. Avoids cascading
PK rewrites across event_parses/feature_snapshots/event_labels tables.
Integration test: tests/integration/test_renormalize_oracle_fallback.py
inserts 4 fixtures, drives _apply_oracle_renormalization() against the
rolled-back db_session, asserts updated/skipped/error counts and final
row state.
Snapshot rebuild not run — nightly auto-rebuild picks up normalized
values incrementally.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
main
parent
722e5cf6a9
commit
e46395bfeb
@ -0,0 +1,93 @@
|
||||
"""Integration test: --renormalize-oracle-fallback applies the normalizer in DB.
|
||||
|
||||
Drives ``_apply_oracle_renormalization()`` against the test session so the
|
||||
helper executes against a real Postgres while the surrounding transaction
|
||||
rolls back at the end of the test.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.asyncio
|
||||
async def test_renormalize_updates_oracle_fallback_event_type(db_session):
|
||||
from apps.pipeline.event_parser.main import _apply_oracle_renormalization
|
||||
from libs.db.models import Document, Event, IssuerMaster, SymbolMaster
|
||||
|
||||
issuer = IssuerMaster(
|
||||
issuer_id="ISSUER::RNRM::0000000001",
|
||||
issuer_name="Renormalize Test Co.",
|
||||
ticker="RNRM",
|
||||
)
|
||||
db_session.add(issuer)
|
||||
|
||||
symbol = SymbolMaster(
|
||||
symbol_id="SYM::RNRM::XNYS",
|
||||
issuer_id="ISSUER::RNRM::0000000001",
|
||||
ticker="RNRM",
|
||||
venue="XNYS",
|
||||
)
|
||||
db_session.add(symbol)
|
||||
|
||||
doc = Document(
|
||||
document_id="DOC::test::ISSUER::RNRM::2026-04-01::RNRMACC001",
|
||||
source_name="sec",
|
||||
form_type="8-K",
|
||||
filing_date=dt.date(2026, 4, 1),
|
||||
accession_no="RNRMACC001",
|
||||
parsed_status="succeeded",
|
||||
)
|
||||
db_session.add(doc)
|
||||
await db_session.flush()
|
||||
|
||||
# Three rows covering: a stale Oracle synonym (earnings_result),
|
||||
# an aggregation case (shareholder_vote), and an already-aligned row
|
||||
# that must NOT change.
|
||||
targets = [
|
||||
("EVT::test::earnings_result::0", "earnings_result", "earnings_release"),
|
||||
("EVT::test::shareholder_vote::0", "shareholder_vote", "other_material_event"),
|
||||
("EVT::test::other_material_event::0", "other_material_event", "other_material_event"),
|
||||
# Honest pass-through case — unmapped Oracle vocab stays as-is.
|
||||
("EVT::test::financial_obligation::0", "financial_obligation", "financial_obligation"),
|
||||
]
|
||||
|
||||
for evt_id, raw_type, _expected in targets:
|
||||
db_session.add(
|
||||
Event(
|
||||
event_id=evt_id,
|
||||
primary_document_id=doc.document_id,
|
||||
symbol_id="SYM::RNRM::XNYS",
|
||||
event_type=raw_type,
|
||||
event_direction="unknown",
|
||||
event_date=dt.date(2026, 4, 1),
|
||||
filed_at_utc=dt.datetime(2026, 4, 1, 22, 0, tzinfo=dt.UTC),
|
||||
parser_version="oracle-fallback-rule-1.0.0",
|
||||
status="pending",
|
||||
)
|
||||
)
|
||||
await db_session.flush()
|
||||
|
||||
stats = await _apply_oracle_renormalization(db_session)
|
||||
|
||||
assert stats["seen"] >= 4
|
||||
# 2 of our 4 fixture rows should flip.
|
||||
assert stats["updated"] >= 2
|
||||
# Both already-aligned and pass-through rows count as skipped.
|
||||
assert stats["skipped"] >= 2
|
||||
assert stats["errors"] == 0
|
||||
|
||||
transitions = stats["transitions"]
|
||||
assert transitions.get("earnings_result -> earnings_release", 0) >= 1
|
||||
assert transitions.get("shareholder_vote -> other_material_event", 0) >= 1
|
||||
|
||||
# Verify the actual row state in the DB matches expectations.
|
||||
for evt_id, _raw_type, expected in targets:
|
||||
row = await db_session.execute(select(Event).where(Event.event_id == evt_id))
|
||||
evt = row.scalar_one()
|
||||
assert evt.event_type == expected, (
|
||||
f"{evt_id}: expected {expected}, got {evt.event_type}"
|
||||
)
|
||||
Loading…
Reference in New Issue