# ============================================================ # Project: EDGAR Research Database # Description: SEC EDGAR Research Infrastructure # # Authors: # - Manel Kammoun — manel.kammoun@uqo.ca # - Djerrycharli Tandjambianda — djerrycharli.tandjambianda@uqo.ca # - Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca # # Institution: # Université du Québec en Outaouais (UQO) # # License: # Research / Academic Use # ============================================================ from __future__ import annotations from abc import ABC, abstractmethod from datetime import datetime from pathlib import Path from typing import TYPE_CHECKING import pandas as pd from src.utils.constants import PROJECT_ROOT from src.utils.logging import get_logger if TYPE_CHECKING: from src.embeddings.manager import EmbeddingManager from src.search.semantic import SemanticSearch from src.storage.database import DatabaseManager logger = get_logger("research.base") class ResearchModule(ABC): def __init__( self, db: DatabaseManager, embedding_manager: EmbeddingManager | None = None, search: SemanticSearch | None = None, ) -> None: self.db = db self.emb = embedding_manager self.search = search @property @abstractmethod def name(self) -> str: ... @property @abstractmethod def description(self) -> str: ... @abstractmethod def compute( self, tickers: list[str] | None = None, start_year: int | None = None, end_year: int | None = None, ) -> pd.DataFrame: ... def save_results( self, results: pd.DataFrame, output_dir: Path | None = None, ) -> None: if output_dir is None: output_dir = PROJECT_ROOT / "outputs" / self.name output_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") parquet_path = output_dir / f"{timestamp}.parquet" csv_path = output_dir / f"{timestamp}.csv" results.to_parquet(parquet_path, index=False, compression="zstd") results.to_csv(csv_path, index=False) logger.info( "Saved %d rows for module '%s' to %s", len(results), self.name, output_dir, ) def load_results(self, output_dir: Path | None = None) -> pd.DataFrame: if output_dir is None: output_dir = PROJECT_ROOT / "outputs" / self.name parquet_files = sorted(output_dir.glob("*.parquet")) if not parquet_files: raise FileNotFoundError( f"No results found for module '{self.name}' in {output_dir}" ) latest = parquet_files[-1] logger.info("Loading results from %s", latest) return pd.read_parquet(latest)