Implemented the full clean rewrite for Gitea
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
class ConfigurationError(ValueError):
|
||||
"""Raised when application configuration is missing or invalid."""
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class Config:
|
||||
bargheman_token: str
|
||||
eitaayar_token: str
|
||||
chat_id: str
|
||||
bill_ids: tuple[str, ...]
|
||||
state_file: Path
|
||||
lookahead_days: int = 5
|
||||
request_timeout_seconds: float = 15.0
|
||||
max_events_per_bill: int = 25
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> Config:
|
||||
load_dotenv(override=False)
|
||||
|
||||
values = {
|
||||
"BARGHEMAN_TOKEN": os.getenv("BARGHEMAN_TOKEN", "").strip(),
|
||||
"EITAAYAR_TOKEN": os.getenv("EITAAYAR_TOKEN", "").strip(),
|
||||
"CHAT_ID": os.getenv("CHAT_ID", "").strip(),
|
||||
}
|
||||
missing = [name for name, value in values.items() if not value]
|
||||
|
||||
raw_bill_ids = os.getenv("BILL_IDS", "")
|
||||
bill_ids = tuple(
|
||||
dict.fromkeys(part.strip() for part in raw_bill_ids.split(",") if part.strip())
|
||||
)
|
||||
if not bill_ids:
|
||||
missing.append("BILL_IDS")
|
||||
elif invalid := [bill_id for bill_id in bill_ids if not bill_id.isdecimal()]:
|
||||
raise ConfigurationError(
|
||||
f"BILL_IDS must contain only decimal IDs: {', '.join(invalid)}"
|
||||
)
|
||||
|
||||
if missing:
|
||||
raise ConfigurationError(
|
||||
f"Missing required environment variables: {', '.join(missing)}"
|
||||
)
|
||||
|
||||
return cls(
|
||||
bargheman_token=values["BARGHEMAN_TOKEN"],
|
||||
eitaayar_token=values["EITAAYAR_TOKEN"],
|
||||
chat_id=values["CHAT_ID"],
|
||||
bill_ids=bill_ids,
|
||||
state_file=Path(os.getenv("STATE_FILE", "state/outages.json").strip()),
|
||||
lookahead_days=_integer_setting("LOOKAHEAD_DAYS", 5, minimum=1, maximum=30),
|
||||
request_timeout_seconds=_float_setting(
|
||||
"REQUEST_TIMEOUT_SECONDS", 15.0, minimum=1.0, maximum=120.0
|
||||
),
|
||||
max_events_per_bill=_integer_setting("MAX_EVENTS_PER_BILL", 25, minimum=1, maximum=100),
|
||||
)
|
||||
|
||||
|
||||
def _integer_setting(name: str, default: int, *, minimum: int, maximum: int) -> int:
|
||||
raw_value = os.getenv(name, str(default)).strip()
|
||||
try:
|
||||
value = int(raw_value)
|
||||
except ValueError as error:
|
||||
raise ConfigurationError(f"{name} must be an integer") from error
|
||||
if not minimum <= value <= maximum:
|
||||
raise ConfigurationError(f"{name} must be between {minimum} and {maximum}")
|
||||
return value
|
||||
|
||||
|
||||
def _float_setting(name: str, default: float, *, minimum: float, maximum: float) -> float:
|
||||
raw_value = os.getenv(name, str(default)).strip()
|
||||
try:
|
||||
value = float(raw_value)
|
||||
except ValueError as error:
|
||||
raise ConfigurationError(f"{name} must be a number") from error
|
||||
if not minimum <= value <= maximum:
|
||||
raise ConfigurationError(f"{name} must be between {minimum:g} and {maximum:g}")
|
||||
return value
|
||||
Reference in New Issue
Block a user