added new data model principle to auto forms

This commit is contained in:
2026-07-20 15:48:36 +02:00
parent a40213a1ed
commit c1ba3cf6e7
3 changed files with 97 additions and 66 deletions
+11 -14
View File
@@ -14,7 +14,7 @@ from wce_crm.constants import TIMEZONE_CEST
from wce_crm.data_models import (
Beratungsgespraech_Einzelgespraech,
Beratungsgespraech_Vorgang,
Initrec_FromDb,
Initrec,
)
from wce_crm.logging import logger_back as logger
from wce_crm.types import (
@@ -28,7 +28,6 @@ from wce_crm.types import (
)
if TYPE_CHECKING:
from wce_crm.data_models import AutoForm_State, Initrec
from wce_crm.types import ConsId, ExtAnId, ExtMaId, RecId
@@ -153,7 +152,7 @@ def initrec_company_to_db(
) -> Initrec:
logger.debug("[AutoForm -- backend] Call database saving routine...")
dump_data = copy.deepcopy(auto_form_data.form_data)
dump_data = copy.deepcopy(auto_form_data.db_data)
dump_data["geloescht"] = auto_form_data.geloescht
with db.ENGINE.begin() as conn:
@@ -174,7 +173,7 @@ def initrec_company_to_db(
data_from_db = from_db[0]
auto_form_data.rec_id = data_from_db["un_id"]
auto_form_data.geloescht = data_from_db["geloescht"]
auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[
auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[
"Metadaten_aktualisierung"
]
@@ -183,7 +182,7 @@ def initrec_company_to_db(
else:
logger.debug("[AutoForm -- backend] Update...")
stmt = (
db.beratung_vorgang.update()
db.grunderfassung_unternehmen.update()
.where(db.grunderfassung_unternehmen.c.un_id == auto_form_data.rec_id)
.returning(
db.grunderfassung_unternehmen.c.Metadaten_aktualisierung,
@@ -196,8 +195,7 @@ def initrec_company_to_db(
assert len(from_db) == 1, "expected excatly one returned row"
data_from_db = from_db[0]
auto_form_data.geloescht = data_from_db["geloescht"]
auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[
auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[
"Metadaten_aktualisierung"
]
@@ -229,7 +227,7 @@ def initrec_company_from_db(
return Initrec(
rec_id=id_,
geloescht=geloescht,
form_data=data_from_db,
db_data=data_from_db,
)
@@ -238,7 +236,7 @@ def initrec_person_to_db(
) -> Initrec:
logger.debug("[AutoForm -- backend] Call database saving routine...")
dump_data = copy.deepcopy(auto_form_data.form_data)
dump_data = copy.deepcopy(auto_form_data.db_data)
dump_data["geloescht"] = auto_form_data.geloescht
with db.ENGINE.begin() as conn:
@@ -259,7 +257,7 @@ def initrec_person_to_db(
data_from_db = from_db[0]
auto_form_data.rec_id = data_from_db["un_id"]
auto_form_data.geloescht = data_from_db["geloescht"]
auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[
auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[
"Metadaten_aktualisierung"
]
@@ -268,7 +266,7 @@ def initrec_person_to_db(
else:
logger.debug("[AutoForm -- backend] Update...")
stmt = (
db.beratung_vorgang.update()
db.grunderfassung_personen.update()
.where(db.grunderfassung_personen.c.pers_id == auto_form_data.rec_id)
.returning(
db.grunderfassung_personen.c.Metadaten_aktualisierung,
@@ -281,8 +279,7 @@ def initrec_person_to_db(
assert len(from_db) == 1, "expected excatly one returned row"
data_from_db = from_db[0]
auto_form_data.geloescht = data_from_db["geloescht"]
auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[
auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[
"Metadaten_aktualisierung"
]
@@ -314,7 +311,7 @@ def initrec_person_from_db(
return Initrec(
rec_id=id_,
geloescht=geloescht,
form_data=data_from_db,
db_data=data_from_db,
)
+10 -10
View File
@@ -225,10 +225,10 @@ class AutoFormConfig:
model: type[FlatBaseModel]
to_db: AutoFormToDb
from_db: AutoFormFromDb
data_insert: AutoFormInsert
data_update: AutoFormUpdate
data_get: AutoFormGet
data_delete: AutoFormDelete
# data_insert: AutoFormInsert
# data_update: AutoFormUpdate
# data_get: AutoFormGet
# data_delete: AutoFormDelete
form_fields: Sequence[FormField]
ignored_keys: Iterable[str] = tuple()
add_buttons: bool = True
@@ -412,17 +412,17 @@ class FlatBaseModel(BaseModel):
# ** InitRec
class Initrec_FromDb(BaseModel):
rec_id: RecId
geloescht: AwareDatetime | None
Metadaten_aktualisierung: AwareDatetime
form_data: dict[str, Any]
# class Initrec_FromDb(BaseModel):
# rec_id: RecId
# geloescht: AwareDatetime | None
# Metadaten_aktualisierung: AwareDatetime
# form_data: dict[str, Any]
class Initrec(BaseModel):
rec_id: RecId | None
geloescht: AwareDatetime | None = None
form_data: dict[str, Any]
db_data: dict[str, Any]
class Grunderfassung_Unternehmen(FlatBaseModel):
+76 -42
View File
@@ -1532,9 +1532,17 @@ class AutoForm(QWidget):
def _delete_data(self) -> None:
assert self.STATE.rec_id is not None, "deletion initialised despite no index set"
self.cfg.data_delete(self.STATE.rec_id)
confirm = get_user_confirmation(
self,
"Löschen bestätigen",
"Soll der Eintrag wirklich gelöscht werden?",
)
if not confirm:
return
self.STATE.geloescht = True
self.save_data()
self.update_triggered.emit()
self.reset_form()
def _load_from_id(
self,
@@ -1544,28 +1552,26 @@ class AutoForm(QWidget):
return
logger_auto_form.info("[Auto-Form] Load data method...")
logger_auto_form.debug("[Auto-Form] Lookup ID: %s", id_)
self.reset_form()
logger_auto_form.debug("[Auto-Form] Lookup ID: %s", id_)
if id_ is not None:
logger_auto_form.debug("Load from DB:")
returned = self.cfg.from_db(id_)
# loaded_data = self.cfg.data_get(lookup_id)
# TODO add path to avoid unnecessary loading (where the same ID is loaded
# twice in a row) -- use an internal marker (not state-related)
else:
if id_ is None:
logger_auto_form.debug("[Auto-Form] Loading: Lookup ID NULL. Do nothing!")
return
logger_auto_form.debug("Load from DB:")
returned = self.cfg.from_db(id_)
# TODO add path to avoid unnecessary loading (where the same ID is loaded
# twice in a row) -- use an internal marker (not state-related)
# logger_auto_form.debug(
# "[Auto-Form] Loaded data dict:\n%s Passing to Pydantic...", pformat(loaded_data)
# )
logger_auto_form.debug(
"[Auto-Form] Loaded data dict:\n%s Passing to Pydantic...",
pformat(returned.form_data),
pformat(returned.db_data),
)
model = self.cfg.model(**returned.form_data)
model = self.cfg.model(**returned.db_data)
logger_auto_form.debug("[Auto-Form] Loaded to Pydantic.")
logger_auto_form.debug("[Auto-Form] Convert to GUI structure...")
form_data = model.to_gui()
@@ -1573,13 +1579,17 @@ class AutoForm(QWidget):
logger_auto_form.debug("[Auto-Form] Form data:\n%s", pformat(form_data))
self._set_form_data(form_data)
self.STATE.rec_id = id_
self._activate_delete()
self.STATE.form_data = self._get_form_data()
logger_auto_form.debug(
"####### model dump vs get form data:\n%s\n\n%s",
pformat(model.model_dump(mode="python")),
pformat(self.STATE.form_data),
)
def reset_form(self) -> None:
reset_form(self.widget_registry)
self.STATE.rec_id = None
self.STATE.form_data = None
self._activate_delete()
def lock_form(self) -> None:
self.STATE.locked = True
@@ -1614,17 +1624,21 @@ class AutoForm(QWidget):
section: Literal["form", "locking"] | None,
) -> None:
if section is None or section == "form":
self._load_from_id(self.STATE.rec_id)
if not self.STATE.geloescht:
self._load_from_id(self.STATE.rec_id)
else:
self.reset_form()
if section is None or section == "locking":
if self.STATE.locked:
if self.edit_buttons is not None:
self.edit_buttons.set_modify_state(False)
self.edit_buttons.set_modify_state(False, set_delete_btn=True)
lock_form(self.widget_registry)
else:
if self.edit_buttons is not None:
self.edit_buttons.set_modify_state(True)
self.edit_buttons.set_modify_state(True, set_delete_btn=False)
unlock_form(self.widget_registry)
self._activate_delete()
def _sync_GUI_to_state(self) -> None:
form_data = self._get_form_data()
@@ -1647,14 +1661,46 @@ class AutoForm(QWidget):
return self.STATE
def _load_from_pydantic(self, data: Initrec) -> None:
def _load_from_pydantic(
self,
data: Initrec,
) -> None:
self.STATE.rec_id = data.rec_id
self.STATE.form_data = data.form_data
self.STATE.form_data = None
self.STATE.geloescht = True if data.geloescht else False
self._sync_state_to_GUI(None) # TODO change to correct loading
def save_data(self) -> None:
if self.STATE.rec_id is None and self.STATE.geloescht:
logger_auto_form.debug(
"[Auto-Form] This was never saved in the database "
"and is marked for deletion - ignore."
)
return
elif self.STATE.rec_id is not None and self.STATE.geloescht:
logger_auto_form.debug(
"[Auto-Form] This was saved in the database "
"and is marked for deletion - fast path."
)
deleted_datetime = datetime.datetime.now(datetime.UTC)
assert self.STATE.form_data, "cannot delete entry with uninitialised form data"
to_db = Initrec(
rec_id=self.STATE.rec_id,
geloescht=deleted_datetime,
db_data={},
)
returned = self.cfg.to_db(to_db)
self._load_from_pydantic(returned)
msg_box = get_message_box(
QMessageBox.Icon.Information,
"Löschen erfolgreich",
"Der Eintrag wurde erfolgreich gelöscht",
)
msg_box.exec()
return
self._disable_save()
errors = self.validate()
@@ -1699,23 +1745,12 @@ class AutoForm(QWidget):
to_db = Initrec(
rec_id=self.STATE.rec_id,
geloescht=deleted_datetime,
form_data=db_data,
db_data=db_data,
)
returned = self.cfg.to_db(to_db)
self._load_from_pydantic(returned)
# if self.STATE.rec_id is None:
# logger_auto_form.debug("[Auto-Form] Insert triggered...")
# init_rec_id = self.cfg.data_insert(db_data)
# assert isinstance(init_rec_id, int)
# self.STATE.rec_id = init_rec_id
# else:
# logger_auto_form.debug("[Auto-Form] Update triggered...")
# self.cfg.data_update(self.STATE.rec_id, db_data)
self.update_triggered.emit()
self._activate_delete()
except Exception as err:
msg_box = get_message_box(
QMessageBox.Icon.Warning,
@@ -2790,6 +2825,11 @@ class MainPage_ClickableCell(ClickableCell):
super().__init__(text=text)
self.data_record = data_record
logger_gui.debug(
"Initialised ClickableCell on MainPage with data record:\n%s",
pformat(self.data_record),
)
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.clicked.emit(self.data_record)
@@ -2903,10 +2943,6 @@ CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig(
model=Grunderfassung_Unternehmen,
to_db=backend.initrec_company_to_db,
from_db=backend.initrec_company_from_db,
data_insert=backend.initrec_company_insert_initial_recording,
data_update=backend.initrec_company_update_initial_recording,
data_get=backend.initrec_company_get_initial_recording,
data_delete=backend.initrec_company_delete_initial_recording,
ignored_keys=(
"Metadaten_erstellung",
"Metadaten_aktualisierung",
@@ -2918,10 +2954,6 @@ CONFIG_GRUNDERFASSUNG_PERSONEN: Final[AutoFormConfig] = AutoFormConfig(
model=Grunderfassung_Personen,
to_db=backend.initrec_person_to_db,
from_db=backend.initrec_person_from_db,
data_insert=backend.initrec_person_insert_initial_recording,
data_update=backend.initrec_person_update_initial_recording,
data_get=backend.initrec_person_get_initial_recording,
data_delete=backend.initrec_person_delete_initial_recording,
ignored_keys=(
"Metadaten_erstellung",
"Metadaten_aktualisierung",
@@ -3100,6 +3132,7 @@ class Page_InitRecCompany(QWidget):
form_data=None,
locked=self.STATE.locked,
)
logger_gui.debug(">>>>>>>>>>>>> AutoForm State:\n%s", pformat(auto_form_state))
self.auto_form.load_state(auto_form_state)
if section is None or section == "comp_profile":
@@ -4490,7 +4523,6 @@ class Page_Consulting(QWidget):
"[Consulting Page] Pydantic model:\n%s", consulting_process
)
# !! should be placed in a try block to handle errors
res = backend.page_consulting_to_db(consulting_process)
if res.status != STATUS_HANDLER.SUCCESS:
logger_page_consulting.error(
@@ -5294,11 +5326,13 @@ class EditButtons(QWidget):
def set_modify_state(
self,
enable_modification: bool,
set_delete_btn: bool = True,
) -> None:
if self.modify_button is not None:
self.modify_button.set_modify_state(enable_modification)
self.save_btn.setEnabled(enable_modification)
self.delete_btn.setEnabled(enable_modification)
if set_delete_btn:
self.delete_btn.setEnabled(enable_modification)
class MainWindow(QMainWindow):