feat(filings): add accepted_at field to filing search response

Expose SEC ACCEPTANCE-DATETIME so downstream consumers (filing_poller)
can populate Document.accepted_at_utc and Event.filed_at_utc.

- SECFiling model: add nullable accepted_at TIMESTAMP column
- main.py startup: ALTER TABLE migration for existing SQLite DBs
- sec_filings_service: extract acceptanceDateTime from SEC JSON, store on
  INSERT and force_refresh UPDATE
- FilingSummary schema: add Optional[str] accepted_at field
- filings endpoint: map accepted_at as ISO 8601 string in response
- CHANGELOG: v3.0.1 entry

Existing rows have accepted_at=NULL; backfill with force_refresh=true.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
I Luk Kim 7 months ago
parent 29fc870b37
commit 76e154eccd

@ -2,6 +2,16 @@
All notable changes to Stock Oracle API will be documented in this file.
## [3.0.1] - 2026-03-14
### Added
- **Filing Search `accepted_at` field**: `GET /filings/search/{ticker}` response now includes `accepted_at` (ISO 8601 timestamp) in each `FilingSummary`
- Sourced from `acceptanceDateTime` in SEC EDGAR submissions JSON — no additional HTTP requests needed
- `SECFiling` DB model gains a nullable `accepted_at TIMESTAMP` column
- SQLite auto-migration: `ALTER TABLE sec_filings ADD COLUMN accepted_at TIMESTAMP` runs at startup (idempotent)
- Existing rows have `accepted_at = NULL`; re-index with `force_refresh=true` to backfill
- Enables downstream consumers (e.g. `filing_poller`) to populate `Document.accepted_at_utc` and `Event.filed_at_utc`
## [3.0.0] - 2026-03-12
### Added

@ -98,6 +98,7 @@ async def search_filings(
accession_number=f.accession_number,
form_type=f.form_type,
filing_date=f.filing_date.strftime("%Y-%m-%d"),
accepted_at=f.accepted_at.isoformat() if f.accepted_at else None,
primary_document=f.primary_document,
filing_description=f.filing_description,
documents_count=docs_count,

@ -21,6 +21,13 @@ async def lifespan(app: FastAPI):
# Startup - ensure tables exist
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# SQLite migration: add accepted_at column if missing
try:
await conn.exec_driver_sql(
"ALTER TABLE sec_filings ADD COLUMN accepted_at TIMESTAMP"
)
except Exception:
pass # Column already exists
# Start overlay batch scheduler (graceful no-op if apscheduler not installed)
try:
from app.services.overlay.scheduler import start_scheduler

@ -18,6 +18,7 @@ class SECFiling(Base):
accession_number = Column(String(30), nullable=False, unique=True, index=True)
form_type = Column(String(20), nullable=False, index=True)
filing_date = Column(TIMESTAMP(timezone=True), nullable=False)
accepted_at = Column(TIMESTAMP(timezone=True))
primary_document = Column(String(256))
primary_document_url = Column(String(512))
filing_description = Column(String(512))

@ -19,6 +19,7 @@ class FilingSummary(BaseModel):
accession_number: str
form_type: str
filing_date: str
accepted_at: Optional[str] = None
primary_document: Optional[str] = None
filing_description: Optional[str] = None
documents_count: Optional[int] = None

@ -65,6 +65,7 @@ class SECFilingsService:
accessions = block.get("accessionNumber", [])
primary_docs = block.get("primaryDocument", [])
descriptions = block.get("primaryDocDescription", [])
accepted_dates = block.get("acceptanceDateTime", [])
for i, (form, dt_str, acc) in enumerate(zip(forms, dates, accessions)):
if form not in target_forms:
continue
@ -76,6 +77,14 @@ class SECFilingsService:
)
except Exception:
continue
accepted_at = None
if i < len(accepted_dates) and accepted_dates[i]:
try:
accepted_at = datetime.fromisoformat(
accepted_dates[i].replace("Z", "+00:00")
)
except Exception:
pass
acc_clean = acc.replace("-", "")
pri_doc_url = None
if pri_doc:
@ -88,6 +97,7 @@ class SECFilingsService:
"accession_number": acc,
"form_type": form,
"filing_date": filing_date,
"accepted_at": accepted_at,
"primary_document": pri_doc,
"primary_document_url": pri_doc_url,
"filing_description": desc,
@ -127,6 +137,7 @@ class SECFilingsService:
existing.cik = rf["cik"]
existing.form_type = rf["form_type"]
existing.filing_date = rf["filing_date"]
existing.accepted_at = rf["accepted_at"]
existing.primary_document = rf["primary_document"]
existing.primary_document_url = rf["primary_document_url"]
existing.filing_description = rf["filing_description"]
@ -140,6 +151,7 @@ class SECFilingsService:
accession_number=rf["accession_number"],
form_type=rf["form_type"],
filing_date=rf["filing_date"],
accepted_at=rf["accepted_at"],
primary_document=rf["primary_document"],
primary_document_url=rf["primary_document_url"],
filing_description=rf["filing_description"],

Loading…
Cancel
Save