diff --git a/prototypes/t_qt_2.py b/prototypes/t_qt_2.py index 05afe5e..399946a 100644 --- a/prototypes/t_qt_2.py +++ b/prototypes/t_qt_2.py @@ -183,8 +183,8 @@ class FlatBaseModel(BaseModel): # 1. Die aktuelle Ebene dieses Dictionaries ent-flachen unflattened_level = {} for key, value in data.items(): - if "__" in key: - parts = key.split("__") + 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): @@ -219,14 +219,14 @@ class FlatBaseModel(BaseModel): return final_nested_data - def to_db(self) -> dict[str, Any]: + def to_db(self, *args, **kwargs) -> dict[str, Any]: """Ausgang für die DB: Flach, Listen sind JSON-Strings.""" - nested = super().model_dump() + nested = super().model_dump(*args, **kwargs) return self.__flatten_dict(nested, serialize_lists=True) - def to_gui(self) -> dict[str, Any]: + def to_gui(self, *args, **kwargs) -> dict[str, Any]: """Ausgang für die GUI: Flach, aber Listen bleiben Python-Listen.""" - nested = super().model_dump() + nested = super().model_dump(*args, **kwargs) return self.__flatten_dict(nested, serialize_lists=False) @classmethod @@ -236,7 +236,7 @@ class FlatBaseModel(BaseModel): """Rekursiver Helfer zum Abflachen von Strukturen.""" items = [] for k, v in nested_dict.items(): - new_key = f"{parent_key}__{k}" if parent_key else k + 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()) @@ -901,11 +901,18 @@ def update_sub_forms( del widget_registry[key] +@dc.dataclass(slots=True) +class FormData: + data: dict[str, Any] + ignored_keys: Iterable[str] + + def get_form_data( widget_registry: WidgetRegistry, filter_keys: Container[str] = tuple(), ) -> dict[str, Any]: - raw_data = {} + raw_data: dict[str, Any] = {} + ignored_keys: list[str] = [] for key, registry_entry in widget_registry.items(): value: Any | None = None @@ -917,6 +924,7 @@ def get_form_data( widget = registry_entry["widget"] form_field = registry_entry["form_field"] if form_field.ignore_get_data: + ignored_keys.append(key) continue if isinstance(widget, QLineEdit): @@ -1672,6 +1680,7 @@ class AutoForm(QWidget): self, form_fields: Sequence[FormField], add_buttons: bool = True, + ignored_keys: Iterable[str] = tuple(), ) -> None: super().__init__() self.setStyleSheet(""" @@ -1761,6 +1770,7 @@ class AutoForm(QWidget): self.reset_btn.clicked.connect(self.reset_form) self.layout_btn.addWidget(self.reset_btn) + self.ignored_keys = ignored_keys # test button # self.main_layout.addSpacing(10) # self.main_layout.addWidget(self.test_button) @@ -1819,7 +1829,7 @@ class AutoForm(QWidget): validated_data = Grunderfassung_Unternehmen(**form_data) # # TODO remove # validated_data.Metadaten_erstellung = datetime.datetime.now() - logger.debug("%s", pformat(validated_data.model_dump())) + # logger.debug("%s", pformat(validated_data.model_dump())) except ValidationError as e: # catch errors and show them in GUI fehler_texte = [] @@ -1848,9 +1858,17 @@ class AutoForm(QWidget): save_pydantic_model_dict_db(validated_data) # TODO save data to database logger.info( - "\n\n>>>>>>>>>>>>> The following data muste be saved in the database:\n%s", + "\n\n>>>>>>>>>>>>> Form data without 'exlude':\n%s", pformat(validated_data.to_db()), ) + + logger.info( + ( + "\n\n>>>>>>>>>>>>> Form data with 'exlude' " + "(must be saved in the database):\n%s" + ), + pformat(validated_data.to_db(exclude=self.ignored_keys)), + ) logger_get_data.info("Data saved successfully") finally: # always re-enable save, even if error occurred @@ -1985,7 +2003,7 @@ class DynamicListWidget(QWidget): return errors - def get_form_data(self): + def get_form_data(self) -> list[dict[str, Any]]: # raw_data = get_form_data(self.widget_registry) # form_data = get_form_data(self.widget_registry) # logger_get_data.debug( @@ -1997,6 +2015,10 @@ class DynamicListWidget(QWidget): # pprint_registry(sub_form.registry) form_data = [get_form_data(sub.registry) for sub in self.sub_forms] + # ignored_keys: list[str] = [] + # for d in form_data: + # ignored_keys.extend(d.ignored_keys) + # data = [d.data for d in form_data] logger_get_data.debug("##################") logger_get_data.debug("Form data:\n%s", pformat(form_data)) @@ -2168,7 +2190,7 @@ class DynamicDropdownWidget(QWidget): return errors - def get_form_data(self): + def get_form_data(self) -> dict[str, Any]: # raw_data = get_form_data(self.widget_registry) form_data = get_form_data(self.widget_registry) @@ -3357,7 +3379,15 @@ class SearchFormPage(QWidget): title.setStyleSheet("font-size: 14px; font-style: italic;") # font-weight: bold; container_layout.addWidget(title) # container_layout.addWidget(MyFormPart(FORM_FIELD_DEF, "Test-Gruppe")) - container_layout.addWidget(AutoForm(FORM_FIELDS)) + container_layout.addWidget( + AutoForm( + FORM_FIELDS, + ignored_keys=( + "Metadaten_erstellung", + "Metadaten_aktualisierung", + ), + ) + ) container_layout.addSpacing(30) diff --git a/src/wce_crm/constants.py b/src/wce_crm/constants.py new file mode 100644 index 0000000..505de27 --- /dev/null +++ b/src/wce_crm/constants.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Final + +LIB_PATH: Final[Path] = Path(__file__).parent diff --git a/src/wce_crm/db.py b/src/wce_crm/db.py index 46e5efa..3bb6259 100644 --- a/src/wce_crm/db.py +++ b/src/wce_crm/db.py @@ -1,8 +1,8 @@ from __future__ import annotations +import datetime import os import re -from datetime import datetime from pathlib import Path import polars as pl @@ -26,7 +26,7 @@ class SafeDateTime(TypeDecorator): clean_value = re.sub(r"[a-zA-Z]+$", "", value).replace(",", ".") try: - return datetime.fromisoformat(clean_value) + return datetime.datetime.fromisoformat(clean_value) except ValueError: # Fallback if it's still weird return None @@ -302,6 +302,20 @@ grunderfassung_unternehmen: sql.Table = Table( "grunderfassung_unternehmen", md_main, Column("erfassung_id", sql.Integer, nullable=False, unique=True, autoincrement=True), + Column( + "Metadaten_erstellung", + sql.DateTime(timezone=True), + nullable=True, + default=lambda: datetime.datetime.now(datetime.UTC), + ), + Column( + "Metadaten_aktualisierung", + sql.DateTime(timezone=True), + nullable=True, + default=lambda: datetime.datetime.now(datetime.UTC), + onupdate=lambda: datetime.datetime.now(datetime.UTC), + ), + Column("Metadaten_nutzer", sql.String(20), nullable=True), Column("Arbeitserfahrung", sql.Text, nullable=True), Column("Grunderfassung_fallnummer", sql.Text, nullable=True), Column("Grunderfassung_notiz", sql.Text, nullable=True), @@ -316,9 +330,8 @@ grunderfassung_unternehmen: sql.Table = Table( Column("Kontaktperson__KP_name_partner", sql.Text, nullable=True), Column("Kontaktperson__KP_titel", sql.Text, nullable=True), Column("Kontaktperson__KP_vorname", sql.Text, nullable=True), - Column("Metadaten_aktualisierung", sql.Text, nullable=True), - Column("Metadaten_erstellung", sql.Text, nullable=True), - Column("Metadaten_nutzer", sql.String(20), nullable=True), + # Column("Metadaten_aktualisierung", sql.Text, nullable=True), + # Column("Metadaten_erstellung", sql.Text, nullable=True), Column("Partnersuche__kanal_aufmerksamkeit", sql.Text, nullable=True), Column("Partnersuche__person_suche", sql.Text, nullable=True), Column("Partnersuche__un_suche", sql.Text, nullable=True),