generated from dopt-python/py311
major refactoring
This commit is contained in:
9
src/wce_crm/custom_widget_registry.py
Normal file
9
src/wce_crm/custom_widget_registry.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final
|
||||
|
||||
CUSTOM_WIDGET_NAMES: Final[frozenset] = frozenset(
|
||||
[
|
||||
"grunderfassung_suche",
|
||||
]
|
||||
)
|
||||
297
src/wce_crm/data_models.py
Normal file
297
src/wce_crm/data_models.py
Normal file
@@ -0,0 +1,297 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import json
|
||||
from typing import Annotated, Any, Final
|
||||
|
||||
from pydantic import (
|
||||
AwareDatetime,
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
EmailStr,
|
||||
Field,
|
||||
field_validator,
|
||||
model_validator,
|
||||
)
|
||||
|
||||
ValidAge = Annotated[int, Field(ge=0, le=99)]
|
||||
|
||||
|
||||
def _parse_json(value: Any) -> str:
|
||||
if isinstance(value, datetime.date):
|
||||
return value.isoformat()
|
||||
elif isinstance(value, datetime.datetime):
|
||||
return value.isoformat()
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
|
||||
COLUMN_SEP: Final[str] = "__"
|
||||
|
||||
|
||||
class FlatBaseModel(BaseModel):
|
||||
"""
|
||||
Optimised Pydantic base class, which parses JSON strings and column
|
||||
separators recursively and correctly
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _recursive_parse_json(
|
||||
cls,
|
||||
data: Any,
|
||||
) -> Any:
|
||||
"""look for JSON list strings and parse them"""
|
||||
if isinstance(data, str) and data.startswith("[") and data.endswith("]"):
|
||||
try:
|
||||
parsed = json.loads(data)
|
||||
# Falls die Liste selbst wieder konvertiert werden muss (z.B. Sub-Dicts)
|
||||
return cls._recursive_parse_json(parsed)
|
||||
except json.JSONDecodeError:
|
||||
return data
|
||||
elif isinstance(data, dict):
|
||||
return {k: cls._recursive_parse_json(v) for k, v in data.items()}
|
||||
elif isinstance(data, list):
|
||||
return [cls._recursive_parse_json(item) for item in data]
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def _recursive_unflatten(
|
||||
cls,
|
||||
data: Any,
|
||||
) -> Any:
|
||||
"""building nested structure using column spearator sequence"""
|
||||
if isinstance(data, dict):
|
||||
unflattened_level = {}
|
||||
for key, value in data.items():
|
||||
if COLUMN_SEP in key:
|
||||
parts = key.split(COLUMN_SEP)
|
||||
aktuell = unflattened_level
|
||||
for part in parts[:-1]:
|
||||
if part not in aktuell or not isinstance(aktuell[part], dict):
|
||||
aktuell[part] = {}
|
||||
aktuell = aktuell[part]
|
||||
aktuell[parts[-1]] = value
|
||||
else:
|
||||
unflattened_level[key] = value
|
||||
|
||||
return {k: cls._recursive_unflatten(v) for k, v in unflattened_level.items()}
|
||||
|
||||
elif isinstance(data, list):
|
||||
return [cls._recursive_unflatten(item) for item in data]
|
||||
|
||||
return data
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def __unflatten_input(
|
||||
cls,
|
||||
data: Any,
|
||||
) -> Any: # type: ignore
|
||||
"""entry control: prepare flat DB/GUI data for Pydantic"""
|
||||
if not isinstance(data, dict):
|
||||
return data
|
||||
|
||||
# setp 1: convert all JSON-Strings to lists
|
||||
json_parsed_data = cls._recursive_parse_json(data)
|
||||
# step 2: build nested structure based on defined separator sequence
|
||||
final_nested_data = cls._recursive_unflatten(json_parsed_data)
|
||||
|
||||
return final_nested_data
|
||||
|
||||
def to_db(self, *args, **kwargs) -> dict[str, Any]:
|
||||
"""output for DB: flat, lists as JSON-Strings"""
|
||||
nested = super().model_dump(*args, **kwargs)
|
||||
return self.__flatten_dict(nested, serialize_lists=True)
|
||||
|
||||
def to_gui(self, *args, **kwargs) -> dict[str, Any]:
|
||||
"""output for GUI: flat, but lists remain Python lists"""
|
||||
nested = super().model_dump(*args, **kwargs)
|
||||
return self.__flatten_dict(nested, serialize_lists=False)
|
||||
|
||||
@classmethod
|
||||
def __flatten_dict(
|
||||
cls,
|
||||
nested_dict: dict,
|
||||
parent_key: str = "",
|
||||
serialize_lists: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
"""recursive function to flatten the structure (for outputs)"""
|
||||
items = []
|
||||
for k, v in nested_dict.items():
|
||||
new_key = f"{parent_key}{COLUMN_SEP}{k}" if parent_key else k
|
||||
|
||||
if isinstance(v, dict):
|
||||
items.extend(cls.__flatten_dict(v, new_key, serialize_lists).items())
|
||||
elif isinstance(v, list):
|
||||
processed_list = []
|
||||
for item in v:
|
||||
if isinstance(item, dict):
|
||||
processed_list.append(
|
||||
cls.__flatten_dict(item, serialize_lists=serialize_lists)
|
||||
)
|
||||
else:
|
||||
processed_list.append(item)
|
||||
|
||||
if serialize_lists:
|
||||
items.append((new_key, json.dumps(processed_list, default=_parse_json)))
|
||||
else:
|
||||
items.append((new_key, processed_list))
|
||||
else:
|
||||
items.append((new_key, v))
|
||||
return dict(items)
|
||||
|
||||
|
||||
class Grunderfassung(FlatBaseModel):
|
||||
# default in SQLAlchemy with lambda and timezone-aware datetime
|
||||
Metadaten_erstellung: AwareDatetime | None = None
|
||||
Metadaten_aktualisierung: AwareDatetime | None = None # see above
|
||||
Metadaten_nutzer: str | None
|
||||
Metadaten_wiedereintrittsdatum: datetime.date | None = None
|
||||
Grunderfassung_fallnummer: str
|
||||
Grunderfassung_notiz: str | None
|
||||
|
||||
Partnersuche: Grunderfassung_PartnerSuche | None = None
|
||||
Projektrelevanz: Grunderfassung_Projektrelevanz
|
||||
Kontaktperson: Grunderfassung_Kontaktperson
|
||||
Stammdaten: Grunderfassung_Stammdaten
|
||||
WeitereInfos: Grunderfassung_WeitereInfos
|
||||
Schulbildung: list[Grunderfassung_Schulbildung]
|
||||
HoehereBildung: list[Grunderfassung_HoehereBildung]
|
||||
Arbeitserfahrung: list[Grunderfassung_Arbeitserfahrung]
|
||||
Sprachkenntnisse: list[Grunderfassung_Sprachen]
|
||||
|
||||
|
||||
class Grunderfassung_PartnerSuche(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
un_suche: int | None
|
||||
person_suche: int | None
|
||||
kanal_aufmerksamkeit: str | None
|
||||
|
||||
|
||||
class Grunderfassung_Projektrelevanz(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
relevanz: str
|
||||
foerderperiode: str | None = None
|
||||
|
||||
|
||||
class Grunderfassung_Kontaktperson(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
KP_name_partner: str | None
|
||||
KP_titel: str | None
|
||||
KP_anrede_anschrift: str | None
|
||||
KP_name: str | None
|
||||
KP_vorname: str | None
|
||||
KP_festnetznummer: str | None
|
||||
KP_mobilfunknummer: str | None
|
||||
KP_email: EmailStr | None
|
||||
KP_funktion_beziehung: str | None
|
||||
KP_adresse: str | None
|
||||
|
||||
|
||||
class Grunderfassung_Stammdaten(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
titel: str | None
|
||||
anrede_anschrift: str
|
||||
name: str
|
||||
vorname: str | None
|
||||
geburtsdatum: datetime.date | None
|
||||
herkunftsland: str
|
||||
staatsangehoerigkeit: str | None
|
||||
rueckkehrer: bool | None
|
||||
aufenthaltsort: str | None
|
||||
strasse: str | None
|
||||
hausnummer: str | None
|
||||
PLZ: str | None
|
||||
ort: str | None
|
||||
bundesland: str | None
|
||||
land: str | None
|
||||
festnetznummer: str | None
|
||||
mobilfunknummer: str | None
|
||||
email: EmailStr | None
|
||||
familienstand: str | None
|
||||
anzahl_kinder: Grunderfassung_Stammdaten_AnzahlKinder
|
||||
|
||||
@field_validator("rueckkehrer", mode="before")
|
||||
@classmethod
|
||||
def str_to_bool(cls, value: Any) -> Any:
|
||||
if isinstance(value, str):
|
||||
value = value.strip().lower()
|
||||
|
||||
if value == "ja":
|
||||
return True
|
||||
elif value == "nein":
|
||||
return False
|
||||
|
||||
raise ValueError("Wert muss 'ja', 'nein', True oder False sein.")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class Grunderfassung_Stammdaten_AnzahlKinder(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
anzahl: int | None
|
||||
alter: list[ValidAge | None] | None = None
|
||||
|
||||
|
||||
class Grunderfassung_WeitereInfos(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
WI_deutsch_sprache: str | None
|
||||
WI_aufenthaltstitel: str | None
|
||||
WI_gueltigkeit_aufenthaltstitel: datetime.date | None
|
||||
WI_arbeitsstatus: str | None
|
||||
WI_meldung_institution: str | None
|
||||
|
||||
|
||||
class Grunderfassung_Schulbildung(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
SB_abschluss: str | None
|
||||
SB_abschlussgrad: str | None
|
||||
SB_schule: str | None
|
||||
SB_ort: str | None
|
||||
SB_land: str | None
|
||||
SB_abschlussjahr: str | None
|
||||
SB_bemerkungsfeld: str | None
|
||||
|
||||
|
||||
class Grunderfassung_HoehereBildung(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
HB_anerkennung: str | None
|
||||
HB_abschlussgrad: str | None
|
||||
HB_abschlussgrad_dokument: str | None
|
||||
HB_organisation: str | None
|
||||
HB_beruf: str | None
|
||||
HB_land: str | None
|
||||
HB_ort: str | None
|
||||
HB_abschlussjahr: str | None
|
||||
HB_bemerkungsfeld: str | None
|
||||
|
||||
|
||||
class Grunderfassung_Arbeitserfahrung(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
AE_branche: str | None
|
||||
AE_bezeichnung: str | None
|
||||
AE_funktion: str | None
|
||||
AE_unternehmen: str | None
|
||||
AE_land: str | None
|
||||
AE_zeitspanne: str | None
|
||||
AE_beschaeftigungsart: str | None
|
||||
AE_bemerkungsfeld: str | None
|
||||
|
||||
|
||||
class Grunderfassung_Sprachen(BaseModel):
|
||||
model_config = ConfigDict(str_strip_whitespace=True)
|
||||
|
||||
SP_sprache: str | None
|
||||
SP_niveau: str | None
|
||||
SP_nachweis: str | None
|
||||
SP_art_nachweis: str | None = None
|
||||
SP_datum_nachweis: datetime.date | None = None
|
||||
1109
src/wce_crm/form_defs.py
Normal file
1109
src/wce_crm/form_defs.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user