from pathlib import Path from typing import List, Optional from pydantic import BaseModel UNKNOWN_CATEGORY = "account2" class CsvConfig(BaseModel): """ Class to define how to parse a certain CSV file. We use the file_match_regex attribute to decide whether to apply a config for a file. If multiple configs match a single file we raise an exception. """ class Config: extra = "forbid" account1: str file_match_regex: str fields: List[str] input_date_format: str = "%m/%d/%Y" output_date_format: str = "%Y/%m/%d" skip: int = 1 delimiter: str = "," quotechar: str = '"' currency: str = "USD" class Config(BaseModel): """ Configuration class for managing file search and data processing settings. Attributes: input_directory (Path): Where to search for 'ldg' and 'csv' files. mappings_file (Path): The path to a 'json' file that contains account2 mappings. output_file (Path): Location to which to write the output 'ldg' file. csv_configs: List of CsvConfig which explains how to handle specific CSV files. categories (List[str]): A list of account2s. An account has to be defined here before it can be used in a mapping. Otherwise, ledger will complain. commodities (List[str]): A list of commodities relevant to the data processing. """ class Config: extra = "forbid" input_directory: Path mappings_file: Path output_file: Path = Path("output.ldg") csv_configs: List[CsvConfig] categories: List[str] model: Path = Path("transaction_classifier.pkl") class Mapping(BaseModel): """Class for transaction mapping from mappings file.""" class Config: extra = "forbid" account2: str count: int = 1 narration: Optional[str] = None payee: Optional[str] = None tags: Optional[List[str]] = None class Transaction(BaseModel): """Class for ledger transaction to render into ldg file.""" class Config: extra = "forbid" currency: str debit: str credit: str date: str account1: str account2: str description: str csv_file: str row: str index: int mapping: Optional[Mapping] = None def key(self): return self.csv_file + ", " + self.row