improved dynamic dropdown behaviour

This commit is contained in:
2026-05-28 09:21:53 +02:00
parent 5289579d93
commit c83c201a13

View File

@@ -5,7 +5,6 @@ import dataclasses as dc
import datetime import datetime
import enum import enum
import json import json
import logging
import pickle import pickle
import re import re
import sys import sys
@@ -18,7 +17,6 @@ from typing import Annotated, Any, Final, Protocol, TypeAlias, TypedDict, TypeVa
from typing_extensions import override from typing_extensions import override
import babel import babel
from dopt_basics.logging import BASE_LOGGER, setup_logging
from pydantic import ( from pydantic import (
AwareDatetime, AwareDatetime,
BaseModel, BaseModel,
@@ -62,26 +60,38 @@ from PySide6.QtWidgets import (
QWidget, QWidget,
) )
import wce_crm.constants
from wce_crm.backend import backend as be_init_rec from wce_crm.backend import backend as be_init_rec
from wce_crm.logging import (
logger_auto_form,
logger_get_data,
logger_gui,
)
K = TypeVar("K") K = TypeVar("K")
V = TypeVar("V") V = TypeVar("V")
setup_logging(enable_stderr=True)
DEBUG: Final[bool] = True DEBUG: bool = True
DEBUG_SEARCH_WIDGET: Final[bool] = False DEBUG_SEARCH_WIDGET: bool = False
DEBUG_NO_DATABASE: Final[bool] = True DEBUG_NO_DATABASE: bool = False
DEBUG_GET_SET: Final[bool] = True DEBUG_GET_SET: bool = False
logger = BASE_LOGGER.getChild("wce") if not wce_crm.constants.Config.DEVELOPMENT_STATE:
logger.setLevel(logging.DEBUG) DEBUG = False
logger_search_widget = logger.getChild("search_widget") DEBUG_SEARCH_WIDGET = False
logger_search_widget.setLevel(logging.DEBUG) DEBUG_NO_DATABASE = False
logger_get_data = logger.getChild("get_data") DEBUG_GET_SET = False
logger_get_data.setLevel(logging.DEBUG)
logger_auto_form = logger.getChild("get_data_auto_form") # TODO remove
logger_auto_form.setLevel(logging.DEBUG) # logger_gui = BASE_LOGGER.getChild("wce")
# logger_gui.setLevel(logging.DEBUG)
# logger_search_widget = logger_gui.getChild("search_widget")
# logger_search_widget.setLevel(logging.DEBUG)
# logger_get_data = logger_gui.getChild("get_data")
# logger_get_data.setLevel(logging.DEBUG)
# logger_auto_form = logger_gui.getChild("get_data_auto_form")
# logger_auto_form.setLevel(logging.DEBUG)
QSS = """ QSS = """
*[styleClass="stempel"] { *[styleClass="stempel"] {
@@ -422,12 +432,13 @@ class FormField:
init_label: str = dc.field(init=False) init_label: str = dc.field(init=False)
ignore_get_data: bool = False ignore_get_data: bool = False
trigger_value: str = "" trigger_value: str = ""
enable_uuid_key: bool = False
def __post_init__( def __post_init__(
self, self,
options: Sequence[tuple[str, Any]], options: Sequence[tuple[str, Any]],
) -> None: ) -> None:
if not self.key: if not self.key and self.enable_uuid_key:
self.key = str(uuid.uuid4()) self.key = str(uuid.uuid4())
self.label = self.label.strip() self.label = self.label.strip()
@@ -546,7 +557,13 @@ def _build_ui_recursively(
no_scroll_filter = NoScrollFilter(parent_layout) no_scroll_filter = NoScrollFilter(parent_layout)
keys: list[str] = [] keys: list[str] = []
for field in schema: for field in schema:
full_key = f"{prefix}{COLUMN_SEP}{field.key}" if prefix else field.key if prefix and field.key:
full_key = f"{prefix}{COLUMN_SEP}{field.key}" if prefix else field.key
elif prefix and not field.key:
full_key = f"{prefix}"
else:
full_key = field.key
widget: QWidget widget: QWidget
match field.type: match field.type:
@@ -1095,8 +1112,13 @@ def set_form_data(
): ):
value = data value = data
else: else:
if key not in data:
logger_gui.debug("---- Key not in data: %s")
logger_gui.debug("-------- Data: %s", pformat(data))
value = data[key] value = data[key]
logger_gui.debug("---- Key set: %s", key)
set_widget_value(widget, value) set_widget_value(widget, value)
@@ -1184,7 +1206,7 @@ class Grunderfassung_Unternehmen(FlatBaseModel):
Grunderfassung_notiz: str | None Grunderfassung_notiz: str | None
Partnersuche: Grunderfassung_PartnerSuche Partnersuche: Grunderfassung_PartnerSuche
Projektrelevanz: Grunderfassung_ProjektrelevanzStatus Projektrelevanz: Grunderfassung_Projektrelevanz
Kontaktperson: Grunderfassung_Kontaktperson Kontaktperson: Grunderfassung_Kontaktperson
Stammdaten: Grunderfassung_Stammdaten Stammdaten: Grunderfassung_Stammdaten
WeitereInfos: Grunderfassung_WeitereInfos WeitereInfos: Grunderfassung_WeitereInfos
@@ -1202,32 +1224,49 @@ class Grunderfassung_PartnerSuche(BaseModel):
kanal_aufmerksamkeit: str | None kanal_aufmerksamkeit: str | None
class Grunderfassung_ProjektrelevanzStatus(BaseModel): class Grunderfassung_Projektrelevanz(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True) model_config = ConfigDict(str_strip_whitespace=True)
status: Grunderfassung_ProjektrelevanzStatus_Status relevanz: str
foerderperiode: str | None = None
# @field_validator("relevanz", 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_ProjektrelevanzStatus_Status(BaseModel): # TODO remove
model_config = ConfigDict(str_strip_whitespace=True) # class Grunderfassung_ProjektrelevanzStatus_Status(BaseModel):
# model_config = ConfigDict(str_strip_whitespace=True)
relevanz: bool # relevanz: bool
foerderperiode: list[str | None] | None = None # foerderperiode: list[str | None] | None = None
@field_validator("relevanz", mode="before") # @field_validator("relevanz", mode="before")
@classmethod # @classmethod
def str_to_bool(cls, value: Any) -> Any: # def str_to_bool(cls, value: Any) -> Any:
if isinstance(value, str): # if isinstance(value, str):
value = value.strip().lower() # value = value.strip().lower()
if value == "ja": # if value == "ja":
return True # return True
elif value == "nein": # elif value == "nein":
return False # return False
raise ValueError("Wert muss 'ja', 'nein', True oder False sein.") # raise ValueError("Wert muss 'ja', 'nein', True oder False sein.")
return value # return value
class Grunderfassung_Kontaktperson(BaseModel): class Grunderfassung_Kontaktperson(BaseModel):
@@ -1350,8 +1389,8 @@ class Grunderfassung_Sprachen(BaseModel):
SP_sprache: str | None SP_sprache: str | None
SP_niveau: str | None SP_niveau: str | None
SP_nachweis: str | None SP_nachweis: str | None
SP_art_nachweis: str | None SP_art_nachweis: str | None = None
SP_datum_nachweis: datetime.date | None SP_datum_nachweis: datetime.date | None = None
class Grunderfassung_SuchWidget(CustomWidget): class Grunderfassung_SuchWidget(CustomWidget):
@@ -1764,7 +1803,7 @@ class AutoForm(QWidget):
except ValueError: except ValueError:
index = -1 index = -1
self.current_id = index self.current_id = index
logger.debug("Set index to %d", self.current_id) logger_gui.debug("Set index to %d", self.current_id)
def _disable_save(self) -> None: def _disable_save(self) -> None:
self.save_btn.setEnabled(False) self.save_btn.setEnabled(False)
@@ -1808,7 +1847,7 @@ class AutoForm(QWidget):
logger_auto_form.debug("Convert to GUI structure...") logger_auto_form.debug("Convert to GUI structure...")
form_data = model.to_gui() form_data = model.to_gui()
logger_auto_form.debug("Set form data...") logger_auto_form.debug("Set form data...")
# logger_get_data_auto_form.debug("Form data:\n%s", pformat(form_data)) logger_auto_form.debug("Form data:\n%s", pformat(form_data))
self.set_form_data(form_data) self.set_form_data(form_data)
self.current_id = lookup_id self.current_id = lookup_id
@@ -1845,7 +1884,7 @@ class AutoForm(QWidget):
# Pydantic detailed error list # Pydantic detailed error list
for error in e.errors(): for error in e.errors():
logger.error("Error during validation phase:\n%s", pformat(error)) logger_gui.error("Error during validation phase:\n%s", pformat(error))
error_field = str(error["loc"][0]) error_field = str(error["loc"][0])
reason = error["msg"] reason = error["msg"]
@@ -1882,7 +1921,7 @@ class AutoForm(QWidget):
logger_auto_form.info("Data saved successfully") logger_auto_form.info("Data saved successfully")
self.save_clicked_form.emit() self.save_clicked_form.emit()
self.reset_form() # self.reset_form() # TODO check if this behaviour is expected
finally: finally:
# always re-enable save, even if error occurred # always re-enable save, even if error occurred
self._enable_save() self._enable_save()
@@ -2421,6 +2460,7 @@ class DynamicDropdownWidgetOption(QWidget):
value = data[key] value = data[key]
set_widget_value(widget, value) set_widget_value(widget, value)
# TODO need to get the correct index # TODO need to get the correct index
logger_get_data.debug("Value: %s", value)
if value is None or value != self.trigger_value: if value is None or value != self.trigger_value:
num_subforms = 0 num_subforms = 0
else: else:
@@ -2435,7 +2475,7 @@ class DynamicDropdownWidgetOption(QWidget):
logger_get_data.debug(">>>>>>>>> Call set_form_data for DynamicDropdown") logger_get_data.debug(">>>>>>>>> Call set_form_data for DynamicDropdown")
logger_get_data.debug("Data before set of subforms:%s", pformat(data)) logger_get_data.debug("Data before set of subforms:%s", pformat(data))
assert len(self.sub_forms) == num_subforms assert len(self.sub_forms) == num_subforms, f"{len(self.sub_forms)=}"
if not self.sub_forms: if not self.sub_forms:
return return
@@ -3223,72 +3263,101 @@ FORM_FIELDS_LANGUAGES = [
), ),
FormField( FormField(
"Nachweis", "Nachweis",
FormFieldType.DROPDOWN, FormFieldType.DYNAMIC_DROPDOWN_OPTION,
required=False, key="",
key="SP_nachweis", trigger_value="vorhanden",
options=[ children=[
("vorhanden", None), FormField(
("nicht vorhanden", None), "Nachweis",
FormFieldType.DROPDOWN,
required=False,
options=[("vorhanden", None), ("nicht vorhanden", None)],
key="SP_nachweis",
children=[
FormField(
"Art des Nachweises",
FormFieldType.TEXT,
required=False,
key="SP_art_nachweis",
),
FormField(
"Datum des Nachweises",
FormFieldType.DATE,
required=False,
key="SP_datum_nachweis",
),
],
),
], ],
), ),
FormField( # FormField(
"Art des Nachweises (NUR WENN VORHANDEN)", # "Nachweis",
FormFieldType.TEXT, # FormFieldType.DROPDOWN,
required=False, # required=False,
key="SP_art_nachweis", # key="SP_nachweis",
), # options=[
FormField( # ("vorhanden", None),
"Datum des Nachweises (NUR WENN VORHANDEN)", # ("nicht vorhanden", None),
FormFieldType.DATE, # ],
required=False, # ),
key="SP_datum_nachweis", # FormField(
), # "Art des Nachweises (NUR WENN VORHANDEN)",
# FormFieldType.TEXT,
# required=False,
# key="SP_art_nachweis",
# ),
# FormField(
# "Datum des Nachweises (NUR WENN VORHANDEN)",
# FormFieldType.DATE,
# required=False,
# key="SP_datum_nachweis",
# ),
] ]
FORM_FIELDS = [ FORM_FIELDS = [
# FormField( FormField(
# "Ersteintrag Datum", "Ersteintrag Datum",
# FormFieldType.TEXT_DATETIME, FormFieldType.TEXT_DATETIME,
# required=False, required=False,
# key="Metadaten_erstellung", key="Metadaten_erstellung",
# readonly=True, readonly=True,
# ignore_get_data=True, ignore_get_data=True,
# ), ),
# FormField( FormField(
# "Aktualisierung Datum", "Aktualisierung Datum",
# FormFieldType.TEXT_DATETIME, FormFieldType.TEXT_DATETIME,
# required=False, required=False,
# key="Metadaten_aktualisierung", key="Metadaten_aktualisierung",
# readonly=True, readonly=True,
# ignore_get_data=True, ignore_get_data=True,
# ), ),
# FormField( FormField(
# "Aktualisierung Nutzer", "Aktualisierung Nutzer",
# FormFieldType.TEXT, FormFieldType.TEXT,
# required=False, required=False,
# key="Metadaten_nutzer", key="Metadaten_nutzer",
# readonly=True, readonly=True,
# ), ),
# FormField( FormField(
# "Fallnummer", "Fallnummer",
# FormFieldType.TEXT, FormFieldType.TEXT,
# required=True, required=True,
# key="Grunderfassung_fallnummer", key="Grunderfassung_fallnummer",
# ), ),
# FormField( FormField(
# "Notizen", "Notizen",
# FormFieldType.LONGTEXT, FormFieldType.LONGTEXT,
# required=False, required=False,
# key="Grunderfassung_notiz", key="Grunderfassung_notiz",
# ), ),
# FormField( FormField(
# "Suche", "Suche",
# FormFieldType.CUSTOM, FormFieldType.CUSTOM,
# custom_widget="grunderfassung_suche", custom_widget="grunderfassung_suche",
# key="Partnersuche", key="Partnersuche",
# children=FORM_FIELDS_SEARCH_HEAD, children=FORM_FIELDS_SEARCH_HEAD,
# ), ),
FormField( FormField(
"Status && Projektrelevanz", "Status && Projektrelevanz",
FormFieldType.GROUP, FormFieldType.GROUP,
@@ -3297,7 +3366,7 @@ FORM_FIELDS = [
FormField( FormField(
"Projektrelevanz", "Projektrelevanz",
FormFieldType.DYNAMIC_DROPDOWN_OPTION, FormFieldType.DYNAMIC_DROPDOWN_OPTION,
key="status", key="",
trigger_value="ja", trigger_value="ja",
children=[ children=[
FormField( FormField(
@@ -3310,56 +3379,54 @@ FORM_FIELDS = [
FormField( FormField(
"Förderperiode", FormFieldType.TEXT, key="foerderperiode" "Förderperiode", FormFieldType.TEXT, key="foerderperiode"
), ),
FormField("Feld 2", FormFieldType.TEXT, key="feld_2"),
FormField("Feld 3", FormFieldType.TEXT, key="feld_3"),
], ],
), ),
], ],
), ),
], ],
), ),
# FormField( FormField(
# "Daten Kontaktperson", "Daten Kontaktperson",
# FormFieldType.GROUP, FormFieldType.GROUP,
# key="Kontaktperson", key="Kontaktperson",
# children=FORM_FIELDS_CONTACT_PERSON, children=FORM_FIELDS_CONTACT_PERSON,
# ), ),
# FormField( FormField(
# "Stammdaten", "Stammdaten",
# FormFieldType.GROUP, FormFieldType.GROUP,
# key="Stammdaten", key="Stammdaten",
# children=FORM_FIELDS_MASTER_DATA, children=FORM_FIELDS_MASTER_DATA,
# ), ),
# FormField( FormField(
# "Weitere Informationen", "Weitere Informationen",
# FormFieldType.GROUP, FormFieldType.GROUP,
# key="WeitereInfos", key="WeitereInfos",
# children=FORM_FIELDS_ADDITIONAL_DATA, children=FORM_FIELDS_ADDITIONAL_DATA,
# ), ),
# FormField( FormField(
# "Schulbildung", "Schulbildung",
# FormFieldType.DYNAMIC_LIST, FormFieldType.DYNAMIC_LIST,
# children=FORM_FIELDS_SCHOOL, children=FORM_FIELDS_SCHOOL,
# key="Schulbildung", key="Schulbildung",
# ), ),
# FormField( FormField(
# "Studium/Ausbildung", "Studium/Ausbildung",
# FormFieldType.DYNAMIC_LIST, FormFieldType.DYNAMIC_LIST,
# children=FORM_FIELDS_HIGHER_EDUCATION, children=FORM_FIELDS_HIGHER_EDUCATION,
# key="HoehereBildung", key="HoehereBildung",
# ), ),
# FormField( FormField(
# "Arbeitserfahrung", "Arbeitserfahrung",
# FormFieldType.DYNAMIC_LIST, FormFieldType.DYNAMIC_LIST,
# children=FORM_FIELDS_WORK_EXPERIENCE, children=FORM_FIELDS_WORK_EXPERIENCE,
# key="Arbeitserfahrung", key="Arbeitserfahrung",
# ), ),
# FormField( FormField(
# "Sprachkenntnisse", "Sprachkenntnisse",
# FormFieldType.DYNAMIC_LIST, FormFieldType.DYNAMIC_LIST,
# children=FORM_FIELDS_LANGUAGES, children=FORM_FIELDS_LANGUAGES,
# key="Sprachkenntnisse", key="Sprachkenntnisse",
# ), ),
] ]
CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig( CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig(