new dual linked dictionary and usage as feature map

This commit is contained in:
Florian Förster 2025-03-12 15:58:30 +01:00
parent 46182855a4
commit e53709e3fb
2 changed files with 86 additions and 10 deletions

View File

@ -1,7 +1,7 @@
import enum import enum
from typing import Final from typing import Final
from delta_barth.types import HttpContentHeaders from delta_barth.types import DualDict, HttpContentHeaders
# ** error handling # ** error handling
DEFAULT_INTERNAL_ERR_CODE: Final[int] = 100 DEFAULT_INTERNAL_ERR_CODE: Final[int] = 100
@ -20,13 +20,14 @@ class KnownDelBarApiErrorCodes(enum.Enum):
# ** API response parsing # ** API response parsing
# ** column mapping [API-Response --> Target-Features] # ** column mapping [API-Response --> Target-Features]
COL_MAP_SALES_PROGNOSIS: Final[dict[str, str]] = { COL_MAP_SALES_PROGNOSIS: Final[DualDict[str, str]] = DualDict(
"artikelId": "artikel_refid", artikelId="artikel_refid",
"firmaId": "firma_refid", firmaId="firma_refid",
"betrag": "betrag", betrag="betrag",
"menge": "menge", menge="menge",
"buchungsDatum": "buchungs_datum", buchungsDatum="buchungs_datum",
} )
FEATURES_SALES_PROGNOSIS: Final[frozenset[str]] = frozenset( FEATURES_SALES_PROGNOSIS: Final[frozenset[str]] = frozenset(
( (
"firma_refid", "firma_refid",
@ -34,3 +35,5 @@ FEATURES_SALES_PROGNOSIS: Final[frozenset[str]] = frozenset(
"buchungs_datum", "buchungs_datum",
) )
) )
MIN_NUMBER_DATAPOINTS: Final[int] = 100

View File

@ -2,13 +2,51 @@ from __future__ import annotations
import enum import enum
import typing as t import typing as t
from collections.abc import Iterator, MutableMapping
from dataclasses import dataclass, field from dataclasses import dataclass, field
import pandas as pd import pandas as pd
from pydantic import BaseModel, ConfigDict, SkipValidation from pydantic import BaseModel, ConfigDict, SkipValidation
# ** Data Structures
K = t.TypeVar("K")
V = t.TypeVar("V")
class DualDict(MutableMapping[K, V]):
def __init__(self, **kwargs: V):
self._store: dict[K, V] = dict(**kwargs)
self._inverted = self._calc_inverted()
@property
def inverted(self) -> dict[V, K]:
return self._inverted
def _calc_inverted(self) -> dict[V, K]:
return {val: key for key, val in self.items()}
def __setitem__(self, key: K, value: V) -> None:
self._store[key] = value
self._inverted[value] = key
def __getitem__(self, key: K) -> V:
return self._store[key]
def __delitem__(self, key: K) -> None:
value = self._store[key]
del self._store[key]
del self._inverted[value]
def __iter__(self) -> Iterator[K]:
return iter(self._store)
def __len__(self) -> int:
return len(self._store)
# ** Pipeline state management # ** Pipeline state management
StatusDescription: t.TypeAlias = tuple[str, int, str] StatusDescription: t.TypeAlias = tuple[str, int, str]
R = t.TypeVar("R", bound="ExportResponse")
class IError(t.Protocol): class IError(t.Protocol):
@ -24,6 +62,15 @@ class Status(BaseModel):
api_server_error: SkipValidation[DelBarApiError | None] = None api_server_error: SkipValidation[DelBarApiError | None] = None
class ResponseType(BaseModel):
pass
class ExportResponse(BaseModel):
response: ResponseType
status: Status
@dataclass(slots=True) @dataclass(slots=True)
class DataPipeStates: class DataPipeStates:
SUCCESS: Status SUCCESS: Status
@ -32,9 +79,35 @@ class DataPipeStates:
@dataclass(slots=True) @dataclass(slots=True)
class PipeResult: class PipeResult(t.Generic[R]):
status: Status
data: pd.DataFrame | None data: pd.DataFrame | None
status: Status
results: R | None = None
def success(
self,
data: pd.DataFrame,
status: Status,
) -> None:
self.data = data
self.status = status
self.results = None
def fail(
self,
status: Status,
) -> None:
self.data = None
self.status = status
self.results = None
def export(
self,
response: R,
) -> None:
self.data = None
self.status = response.status
self.results = response
# ** API # ** API