"""Alias maps, normalization, and CSV parsing helpers for domain enums. The enum types themselves (ContractType, RenewalType, PeriodType) are defined in schemas.graphql — they originate from the GraphQL schema. """ from enum import Enum from typing import Optional, TypeVar, overload from schemas.graphql import ContractType, PeriodType, RenewalType E = TypeVar('E', bound=Enum) class BooleanValue(Enum): """Boolean value parsed from CSV input.""" __FALSY = {'0', 'f', 'false', 'n', 'no', 'off'} __TRUTHY = {'1', 'on', 't', 'true', 'y', 'yes'} FALSE = 'false' TRUE = 'true' @overload @classmethod def parse(cls, value: str, default: None = ...) -> 'BooleanValue': ... @overload @classmethod def parse( cls, value: Optional[str], default: 'BooleanValue' = ... ) -> 'BooleanValue': ... @overload @classmethod def parse(cls, value: None, default: None = ...) -> None: ... @classmethod def parse( cls, value: Optional[str], default: Optional['BooleanValue'] = None ) -> Optional['BooleanValue']: """Parse a string value into a BooleanValue. Returns the default for None or empty/whitespace-only input. Raises ValueError for unrecognized non-empty values. """ if value is None: return default v = value.strip().lower() if not v: return default if v in cls.__TRUTHY: return cls.TRUE if v in cls.__FALSY: return cls.FALSE raise ValueError(f'Invalid boolean value: {value!r}') def to_bool(self) -> bool: """Convert to a native bool.""" return self is BooleanValue.TRUE # --------------------------------------------------------------------------- # Alias maps (CSV synonyms -> enum members) # --------------------------------------------------------------------------- CONTRACT_TYPE_ALIASES: dict[str, ContractType] = { 'legacy distribution': ContractType.LEGACY_DISTRIBUTION, 'neighbouring rights': ContractType.NEIGHBOURING_RIGHTS, } RENEWAL_TYPE_ALIASES: dict[str, RenewalType] = { 'continuously active': RenewalType.CONTINUOUSLY_ACTIVE, 'renew after certain date': RenewalType.RENEW_AFTER_CERTAIN_DATE, 'renew periodically': RenewalType.RENEW_PERIODICALLY, } PERIOD_TYPE_ALIASES: dict[str, PeriodType] = { 'days': PeriodType.DAY, 'days after month end': PeriodType.DAY, 'months': PeriodType.MONTH, 'years': PeriodType.YEAR, } def resolve_enum(value: str, enum_cls: type[E], aliases: dict[str, E]) -> E | None: """Resolve a string to an enum member via alias lookup or direct match. Returns the enum member, or None if the value cannot be resolved. """ lowered = value.lower().strip() if not lowered or lowered == 'n/a': return None if lowered in aliases: return aliases[lowered] try: return enum_cls(lowered) except ValueError: return None