fix(sec): name aged filings in deferred warnings
This commit is contained in:
+31
-16
@@ -33,7 +33,7 @@ from dataclasses import dataclass, field
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
from typing import Any, Protocol, runtime_checkable
|
||||
|
||||
from sqlalchemy import select, text
|
||||
from sqlalchemy import exists, select, text
|
||||
from sqlalchemy.engine import Engine # noqa: F401 (typing only)
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
|
||||
|
||||
@@ -74,6 +74,7 @@ class ValidationResult:
|
||||
# when ok=False.
|
||||
retryable: bool = False
|
||||
deferred_alert_after_days: int | None = None
|
||||
deferred_alert_messages: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
@@ -129,17 +130,22 @@ async def _last_promoted_revision(db: AsyncSession, source: str) -> str | None:
|
||||
return row.scalar_one_or_none()
|
||||
|
||||
|
||||
async def _has_promoted_since(db: AsyncSession, source: str, cutoff: datetime) -> bool:
|
||||
row = await db.execute(
|
||||
select(DataImportRun.id)
|
||||
.where(
|
||||
DataImportRun.source == source,
|
||||
DataImportRun.status == STATUS_PROMOTED,
|
||||
DataImportRun.started_at >= cutoff,
|
||||
)
|
||||
.limit(1)
|
||||
async def _promotion_state_since(
|
||||
db: AsyncSession, source: str, cutoff: datetime
|
||||
) -> str:
|
||||
promoted = (
|
||||
DataImportRun.source == source,
|
||||
DataImportRun.status == STATUS_PROMOTED,
|
||||
)
|
||||
return row.scalar_one_or_none() is not None
|
||||
ever, recent = (
|
||||
await db.execute(
|
||||
select(
|
||||
exists().where(*promoted),
|
||||
exists().where(*promoted, DataImportRun.started_at >= cutoff),
|
||||
)
|
||||
)
|
||||
).one()
|
||||
return "recent" if recent else "stale" if ever else "never"
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
@@ -246,16 +252,25 @@ async def run_import(
|
||||
if alert_days is not None:
|
||||
alert_days = max(1, alert_days)
|
||||
cutoff = run.started_at - timedelta(days=alert_days)
|
||||
if not await _has_promoted_since(session, source, cutoff):
|
||||
promotion_state = await _promotion_state_since(
|
||||
session, source, cutoff
|
||||
)
|
||||
if promotion_state != "recent":
|
||||
history = (
|
||||
f"{source} import has never promoted successfully"
|
||||
if promotion_state == "never"
|
||||
else f"{source} import has not promoted successfully "
|
||||
f"within {alert_days} day(s)"
|
||||
)
|
||||
await _alert(
|
||||
session,
|
||||
source,
|
||||
"deferred_stale",
|
||||
[
|
||||
f"No successful promotion for at least "
|
||||
f"{alert_days} day(s); deferred source lag may "
|
||||
f"now hide aged-out unresolved items: "
|
||||
f"{run.error_details or 'validation deferred'}"
|
||||
f"{history}; import remains deferred",
|
||||
*result.deferred_alert_messages,
|
||||
f"Current deferral: "
|
||||
f"{run.error_details or 'validation deferred'}",
|
||||
],
|
||||
severity="warning",
|
||||
dedup_hours=alert_days * 24,
|
||||
|
||||
@@ -362,6 +362,7 @@ class SecFundamentalsImporter:
|
||||
# the filings: "which ones" has to be in the alert itself, not merely
|
||||
# reconstructible by re-walking the index.
|
||||
blocking = _within_retry_window(staged.missing_xbrl)
|
||||
aged_out = _past_retry_window(staged.missing_xbrl)
|
||||
if blocking:
|
||||
messages.append(
|
||||
f"{len(blocking)} tracked XBRL filing(s) unresolved within the "
|
||||
@@ -422,6 +423,16 @@ class SecFundamentalsImporter:
|
||||
and all(m.get("reason") == "not_in_companyfacts" for m in blocking)
|
||||
),
|
||||
deferred_alert_after_days=MISSING_XBRL_RETRY_DAYS,
|
||||
deferred_alert_messages=(
|
||||
[
|
||||
f"{len(aged_out)} tracked SEC filing(s) remain unresolved past "
|
||||
f"the {MISSING_XBRL_RETRY_DAYS}-day retry window and risk being "
|
||||
f"promoted around without automatic retry: "
|
||||
f"{_missing_detail(aged_out)}"
|
||||
]
|
||||
if aged_out
|
||||
else []
|
||||
),
|
||||
)
|
||||
|
||||
async def promote(self, db, staged: StagedFundamentals, run_id: int) -> dict[str, int]:
|
||||
|
||||
Reference in New Issue
Block a user