Source code for kaxanuk.attribution_analysis.input_handlers.csv_input

"""
CSV input handler for market data files.

Read per-ticker CSV files, parse columns as strings for consistent type
inference, then delegate to the shared processing pipeline for decimal
casting, date parsing, and date-range filtering.
"""

import logging
from pathlib import Path

import pyarrow as pa
import pyarrow.csv as pv

from kaxanuk.attribution_analysis.entities.configuration import Configuration
from kaxanuk.attribution_analysis.exceptions import ReadingCSVError
from kaxanuk.attribution_analysis.input_handlers import input_handlers_helpers
from kaxanuk.attribution_analysis.input_handlers.input_handler_interface import InputHandlerInterface
from kaxanuk.attribution_analysis.market_data_pipeline.field_definitions import STANDARD_FIELDS_ORDER

logger = logging.getLogger(__name__)


[docs] class CsvInput(InputHandlerInterface): """ A CSV input handler for reading and processing market data from CSV files. This class implements the InputHandlerInterface to provide functionality for reading CSV files containing market data, validating their existence, and processing them into PyArrow tables with proper data type conversions and date range filtering. The handler supports column mapping through equivalence dictionaries and performs data validation including empty value checks and decimal precision inference for numerical columns. Attributes ---------- input_dir : str The base directory path where input files are located. """ def __init__( self, *, input_dir: str, ) -> None: """ Initialize the CSV input handler with directory configuration. Parameters ---------- input_dir : str The base directory path where input files are stored. Must be a valid directory path accessible by the application. Notes ----- The complete path to CSV files will be constructed as: {input_dir}/{ticker}.csv """ super().__init__() self.input_dir = input_dir
[docs] def file_exists( self, *, ticker: str, configuration: Configuration, ) -> bool: """ Check if a CSV file exists for the specified ticker without loading it into memory. This method performs a lightweight existence check by constructing the expected file path and verifying if the file exists on the filesystem. It does not validate the file content or structure. Parameters ---------- ticker : str The ticker symbol for which to check file existence. The method will look for a file named '{ticker}.csv' in the configured input directory. configuration : Configuration The configuration object containing parameters. While not directly used in this method, it maintains interface consistency. Returns ------- bool True if the CSV file exists at the expected path, False otherwise. Notes ----- The file path is constructed as: {input_dir}/{ticker}.csv """ input_file_path = Path(self.input_dir) / f"{ticker}.csv" return input_file_path.exists()
[docs] def input_data( self, *, ticker: str, configuration: Configuration, equivalence_dict: dict[str, str], ) -> pa.Table: """ Read and process CSV input data for a specified ticker symbol. This method reads market data from a CSV file and delegates all processing operations (type conversion, date parsing, decimal inference, validation, and filtering) to a common helper function. The CSV-specific aspect is reading all columns as strings initially to allow for flexible parsing. Parameters ---------- ticker : str The ticker symbol for which to load market data. The method will look for a file named '{ticker}.csv' in the configured input directory. configuration : Configuration Configuration object containing: - start_date: Start date for data filtering - end_date: End date for data filtering equivalence_dict : dict[str, str] Dictionary mapping STANDARD field names (e.g. 'vwap', 'adjusted_close') to actual CSV column names used in the files. Returns ------- pa.Table A PyArrow table containing the processed market data with: - Properly typed columns (dates as timestamps, numbers as decimals) - Filtered data within the specified date range - Standardized column names as per STANDARD_FIELDS_ORDER - Validated data with no empty values in critical columns Raises ------ ReadingCSVError Raised in the following scenarios: - FileNotFoundError: When the CSV file for the ticker doesn't exist - pa.lib.ArrowInvalid: When the CSV format is invalid or corrupted - KeyError: When column mapping fails due to missing keys - Exception: For any other unexpected errors during processing Notes ----- Unlike ParquetInput which can rely on stored types, CSV files are read with all columns as strings to ensure consistent parsing behavior before type conversion. """ input_file_path = f"{self.input_dir}/{ticker}.csv" try: # Get columns to read in standard order csv_columns_in_order = [ equivalence_dict[std_name] for std_name in STANDARD_FIELDS_ORDER ] unique_csv_columns = list(dict.fromkeys(csv_columns_in_order)) # Read CSV with all columns as strings for consistent parsing column_types = {col: pa.string() for col in unique_csv_columns} table = pv.read_csv( input_file_path, convert_options=pv.ConvertOptions( include_columns=unique_csv_columns, column_types=column_types, ), ) # Delegate all processing to common helper return input_handlers_helpers.process_table_columns( table=table, ticker=ticker, configuration=configuration, equivalence_dict=equivalence_dict, ) except FileNotFoundError as error: raise ReadingCSVError( f"File not found for ticker '{ticker}': {input_file_path}" ) from error except pa.lib.ArrowInvalid as error: raise ReadingCSVError( f"Invalid CSV format for ticker '{ticker}': {error}" ) from error except pa.lib.ArrowTypeError as error: raise ReadingCSVError( f"Type conversion error for ticker '{ticker}': {error}" ) from error except KeyError as error: raise ReadingCSVError( f"Column mapping error for ticker '{ticker}': {error}. " f"Check your equivalence_dict and STANDARD_FIELDS_ORDER." ) from error except ValueError as error: raise ReadingCSVError( f"Value error reading CSV for ticker '{ticker}': {error}" ) from error except OSError as error: raise ReadingCSVError( f"I/O error reading CSV for ticker '{ticker}': {error}" ) from error