"""Filing Poller: discover new 8-K/6-K filings via Stock Oracle.""" from __future__ import annotations import argparse import asyncio import datetime as dt import uuid from sqlalchemy import select from libs.common.config import get_settings from libs.common.ids import document_id as make_document_id from libs.common.ids import new_job_run_id from libs.common.logging import bind_job_run_id, configure_logging, get_logger from libs.db.models import Document, JobRun from libs.db.session import get_session from libs.oracle_client.client import make_oracle_client from libs.oracle_client.filings import FilingsService logger = get_logger(__name__) async def poll_filings( run_id: str, start_date: str | None = None, end_date: str | None = None, ) -> dict[str, int]: settings = get_settings() symbols = settings.get_symbols() app_config = settings.get_app_config() form_types = ",".join(app_config.get("pipeline", {}).get("form_types", ["8-K", "6-K"])) effective_start = start_date or (dt.date.today() - dt.timedelta(days=7)).isoformat() stats = {"seen": 0, "written": 0, "skipped": 0, "errors": 0} async with make_oracle_client() as client: svc = FilingsService(client) async with get_session() as session: # Record job start job = JobRun( job_run_id=uuid.UUID(run_id), job_name="filing_poller", source_name="oracle", run_date=dt.date.today(), status="running", ) session.add(job) await session.flush() for ticker in symbols: try: response = await svc.search_filings( ticker, form_type=form_types, start_date=effective_start, end_date=end_date, ) stats["seen"] += len(response.filings) for filing in response.filings: # Check for duplicate existing = await session.execute( select(Document).where( Document.accession_no == filing.accession_no, Document.form_type == filing.form_type, ) ) if existing.scalar_one_or_none() is not None: stats["skipped"] += 1 continue doc_id = make_document_id( "sec", f"TICKER::{ticker}", filing.filing_date, filing.accession_no, ) doc = Document( document_id=doc_id, source_name="sec", accession_no=filing.accession_no, form_type=filing.form_type, filing_date=dt.date.fromisoformat(filing.filing_date), accepted_at_utc=( dt.datetime.fromisoformat( filing.accepted_at.replace("Z", "+00:00") ) if filing.accepted_at else None ), primary_document_name=filing.primary_document, parsed_status="pending", ) session.add(doc) stats["written"] += 1 logger.info( "new_filing_discovered", ticker=ticker, accession_no=filing.accession_no, form_type=filing.form_type, filing_date=filing.filing_date, ) except Exception as exc: logger.error("poll_error", ticker=ticker, error=str(exc)) stats["errors"] += 1 # Update job record job.status = "succeeded" if stats["errors"] == 0 else "partial" job.finished_at_utc = dt.datetime.now(tz=dt.UTC) job.records_seen = stats["seen"] job.records_written = stats["written"] job.records_skipped = stats["skipped"] job.error_count = stats["errors"] logger.info("filing_poller_done", **stats) return stats def main() -> None: parser = argparse.ArgumentParser(description="Filing Poller") parser.add_argument("--run-id", default=new_job_run_id()) parser.add_argument( "--start-date", default=None, metavar="YYYY-MM-DD", help="Start date for filing search (default: 7 days ago)", ) parser.add_argument( "--end-date", default=None, metavar="YYYY-MM-DD", help="End date for filing search (default: today)", ) args = parser.parse_args() settings = get_settings() configure_logging(settings.log_level) bind_job_run_id(args.run_id) asyncio.run(poll_filings(args.run_id, start_date=args.start_date, end_date=args.end_date)) if __name__ == "__main__": main()