"""Filing-related Oracle service methods.""" from __future__ import annotations import re from libs.oracle_client.client import OracleClient from libs.oracle_client.models import ( ExhibitDocument, ExhibitResponse, FilingDocumentsResponse, FilingEntry, FilingEventEntry, FilingEventsResponse, FilingSearchResponse, ) # Mapping from SGML ITEM INFORMATION descriptions to SEC 8-K item numbers. _ITEM_DESC_TO_NUMBER: dict[str, str] = { "entry into a material definitive agreement": "1.01", "termination of a material definitive agreement": "1.02", "bankruptcy or receivership": "1.03", "mine safety": "1.04", "completion of acquisition or disposition of assets": "2.01", "results of operations and financial condition": "2.02", "creation of a direct financial obligation": "2.03", "triggering events that accelerate": "2.04", "costs associated with exit or disposal": "2.05", "material impairments": "2.06", "notice of delisting": "3.01", "unregistered sales of equity securities": "3.02", "material modification to rights": "3.03", r"changes in registrant.s certifying accountant": "4.01", "non-reliance on previously issued": "4.02", "changes in control of registrant": "5.01", "departure of directors or certain officers": "5.02", "amendments to articles of incorporation or bylaws": "5.03", "temporary suspension of trading": "5.04", r"amendments to the registrant.s code of ethics": "5.05", "change in shell company status": "5.06", "submission of matters to a vote": "5.07", "shareholder nominations": "5.08", "regulation fd disclosure": "7.01", "other events": "8.01", "financial statements and exhibits": "9.01", } def extract_items_from_sgml_header(content: str) -> list[str]: """Extract SEC 8-K item numbers from SGML header ITEM INFORMATION fields.""" items_found: list[str] = [] matches = re.findall(r"ITEM INFORMATION:\s*(.+)", content) for desc in matches: desc_lower = desc.strip().lower() for key, item_num in _ITEM_DESC_TO_NUMBER.items(): if re.search(key, desc_lower): items_found.append(item_num) break return list(dict.fromkeys(items_found)) class FilingsService: def __init__(self, client: OracleClient) -> None: self._client = client async def search_filings( self, ticker: str, form_type: str | None = None, start_date: str | None = None, end_date: str | None = None, ) -> FilingSearchResponse: params: dict[str, str] = {} if form_type: params["form_type"] = form_type if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date data = await self._client.get(f"/api/v1/filings/search/{ticker}", params=params) # Real Oracle: {"ticker": ..., "filings": [{accession_number, form_type, ...}], "total_count": ...} filings = [ FilingEntry( accession_no=f["accession_number"], form_type=f["form_type"], filing_date=f["filing_date"], accepted_at=f.get("accepted_at"), primary_document=f.get("primary_document"), description=f.get("filing_description"), items=f.get("items", []), ) for f in data.get("filings", []) ] return FilingSearchResponse( ticker=data.get("ticker", ticker), filings=filings, total=data.get("total_count", len(filings)), ) async def get_documents(self, accession_no: str) -> FilingDocumentsResponse: data = await self._client.get(f"/api/v1/filings/documents/{accession_no}") exhibits = [ExhibitDocument.model_validate(e) for e in data.get("exhibits", [])] return FilingDocumentsResponse( accession_no=data.get("accession_number", accession_no), exhibits=exhibits, ) async def get_exhibit( self, accession_no: str, exhibit_type: str = "EX-99.1" ) -> ExhibitResponse: params = {"exhibit_type": exhibit_type} data = await self._client.get(f"/api/v1/filings/exhibit/{accession_no}", params=params) # Real Oracle: {"accession_number": ..., "exhibit_type": ..., "content": ...} return ExhibitResponse( accession_no=data.get("accession_number", accession_no), exhibit_type=data.get("exhibit_type", exhibit_type), content=data.get("content", ""), ) async def get_filing_events( self, ticker: str, start_date: str | None = None, end_date: str | None = None, accession_no: str | None = None, ) -> FilingEventsResponse: """Fetch pre-parsed filing events from Oracle API.""" params: dict[str, str] = {} if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date data = await self._client.get(f"/api/v1/filings/events/{ticker}", params=params) events = [FilingEventEntry.model_validate(e) for e in data.get("events", [])] if accession_no: events = [e for e in events if e.accession_number == accession_no] return FilingEventsResponse( ticker=data.get("ticker", ticker), events=events, total_count=len(events), ) async def get_filing_items(self, accession_no: str) -> list[str]: """Extract SEC 8-K item numbers from the complete submission text. Fetches the full submission text (exhibit_type="") which contains the SGML header with ITEM INFORMATION fields, then parses item numbers. """ try: data = await self._client.get( f"/api/v1/filings/exhibit/{accession_no}", params={"exhibit_type": ""} ) content = data.get("content", "") if not content: return [] return extract_items_from_sgml_header(content) except Exception: return []