generated from dopt-python/py311
refactoring
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
def _get_leafs(data):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for value in data.values():
|
||||||
|
yield from _get_leafs(value)
|
||||||
|
elif isinstance(data, (list, tuple, set)):
|
||||||
|
for item in data:
|
||||||
|
yield from _get_leafs(item)
|
||||||
|
else:
|
||||||
|
yield data
|
||||||
|
|
||||||
|
|
||||||
|
def _get_leaf_dicts(data):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
has_inner_dicts = False
|
||||||
|
|
||||||
|
for value in data.values():
|
||||||
|
for inner_dict in _get_leaf_dicts(value):
|
||||||
|
has_inner_dicts = True
|
||||||
|
yield inner_dict
|
||||||
|
|
||||||
|
if not has_inner_dicts:
|
||||||
|
yield data
|
||||||
|
|
||||||
|
elif isinstance(data, (list, tuple, set)):
|
||||||
|
for item in data:
|
||||||
|
yield from _get_leaf_dicts(item)
|
||||||
@@ -147,7 +147,7 @@ def initrec_company_get_initial_recording(
|
|||||||
row = ret.fetchone()
|
row = ret.fetchone()
|
||||||
assert row, "row was not obtained"
|
assert row, "row was not obtained"
|
||||||
|
|
||||||
return row._asdict()
|
return row._asdict() # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def initrec_company_delete_initial_recording(
|
def initrec_company_delete_initial_recording(
|
||||||
@@ -196,7 +196,7 @@ def initrec_person_update_initial_recording(
|
|||||||
def initrec_person_get_initial_recording(
|
def initrec_person_get_initial_recording(
|
||||||
id_: RecId,
|
id_: RecId,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
logger.debug("[Call backend] get_initial_recording")
|
logger.debug("[Call backend] get_initial_recording person")
|
||||||
stmt = db.grunderfassung_personen.select().where(
|
stmt = db.grunderfassung_personen.select().where(
|
||||||
db.grunderfassung_personen.c.pers_id == id_
|
db.grunderfassung_personen.c.pers_id == id_
|
||||||
)
|
)
|
||||||
@@ -209,7 +209,7 @@ def initrec_person_get_initial_recording(
|
|||||||
row = ret.fetchone()
|
row = ret.fetchone()
|
||||||
assert row, "row was not obtained"
|
assert row, "row was not obtained"
|
||||||
|
|
||||||
return row._asdict()
|
return row._asdict() # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def initrec_person_delete_initial_recording(
|
def initrec_person_delete_initial_recording(
|
||||||
|
|||||||
@@ -13,10 +13,47 @@ from pydantic import (
|
|||||||
field_validator,
|
field_validator,
|
||||||
model_validator,
|
model_validator,
|
||||||
)
|
)
|
||||||
|
from pydantic_core import ErrorDetails
|
||||||
|
|
||||||
ValidAge = Annotated[int, Field(ge=0, le=99)]
|
ValidAge = Annotated[int, Field(ge=0, le=99)]
|
||||||
|
|
||||||
|
|
||||||
|
# // validation translation
|
||||||
|
GERMAN_ERROR_MESSAGES: Final[dict[str, str]] = {
|
||||||
|
"missing": "Dieses Feld ist ein Pflichtfeld.",
|
||||||
|
"int_parsing": "Bitte eine gültige Ganzzahl eingeben.",
|
||||||
|
"float_parsing": "Bitte eine gültige Dezimalzahl eingeben.",
|
||||||
|
"string_too_short": "Der Text ist zu kurz. Er muss mindestens {min_length} Zeichen lang sein.",
|
||||||
|
"string_too_long": "Der Text ist zu lang. Er darf maximal {max_length} Zeichen lang sein.",
|
||||||
|
"greater_than": "Der Wert muss größer als {gt} sein.",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def translate_pydantic_errors(
|
||||||
|
errors: list[ErrorDetails],
|
||||||
|
) -> list[ErrorDetails]:
|
||||||
|
translated_errors: list[ErrorDetails] = []
|
||||||
|
|
||||||
|
for error in errors:
|
||||||
|
err_type = error.get("type", None)
|
||||||
|
|
||||||
|
if err_type is None or err_type not in GERMAN_ERROR_MESSAGES:
|
||||||
|
error["msg"] = "[fehleden Übersetzung, nur Englisch]: " + error["msg"]
|
||||||
|
translated_errors.append(error)
|
||||||
|
continue
|
||||||
|
|
||||||
|
msg_template = GERMAN_ERROR_MESSAGES[err_type]
|
||||||
|
ctx = error.get("ctx", {})
|
||||||
|
try:
|
||||||
|
error["msg"] = msg_template.format(**ctx)
|
||||||
|
except KeyError:
|
||||||
|
error["msg"] = msg_template
|
||||||
|
|
||||||
|
translated_errors.append(error)
|
||||||
|
|
||||||
|
return translated_errors
|
||||||
|
|
||||||
|
|
||||||
def _parse_json(value: Any) -> str:
|
def _parse_json(value: Any) -> str:
|
||||||
if isinstance(value, datetime.date):
|
if isinstance(value, datetime.date):
|
||||||
return value.isoformat()
|
return value.isoformat()
|
||||||
|
|||||||
@@ -911,6 +911,7 @@ INITREC_COMP = [
|
|||||||
required=False,
|
required=False,
|
||||||
key="Metadaten_nutzer",
|
key="Metadaten_nutzer",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
|
ignore_get_data=True,
|
||||||
),
|
),
|
||||||
FormField(
|
FormField(
|
||||||
"Fallnummer",
|
"Fallnummer",
|
||||||
@@ -956,6 +957,7 @@ INITREC_PERSON = [
|
|||||||
required=False,
|
required=False,
|
||||||
key="Metadaten_nutzer",
|
key="Metadaten_nutzer",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
|
ignore_get_data=True,
|
||||||
),
|
),
|
||||||
FormField(
|
FormField(
|
||||||
"Wiedereintrittsdatum",
|
"Wiedereintrittsdatum",
|
||||||
|
|||||||
+224
-154
@@ -4,13 +4,11 @@ import copy
|
|||||||
import dataclasses as dc
|
import dataclasses as dc
|
||||||
import datetime
|
import datetime
|
||||||
import enum
|
import enum
|
||||||
import pickle
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections.abc import Container, Iterable, Sequence
|
from collections.abc import Container, Iterable, Sequence
|
||||||
from pathlib import Path
|
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
@@ -26,7 +24,6 @@ from typing import (
|
|||||||
)
|
)
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
from dopt_basics import datetime as dopt_dt
|
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
@@ -36,7 +33,6 @@ from PySide6.QtCore import (
|
|||||||
QEvent,
|
QEvent,
|
||||||
QObject,
|
QObject,
|
||||||
Qt,
|
Qt,
|
||||||
QTime,
|
|
||||||
QTimer,
|
QTimer,
|
||||||
QtMsgType,
|
QtMsgType,
|
||||||
Signal,
|
Signal,
|
||||||
@@ -84,6 +80,7 @@ from wce_crm.data_models import (
|
|||||||
FlatBaseModel,
|
FlatBaseModel,
|
||||||
Grunderfassung_Personen,
|
Grunderfassung_Personen,
|
||||||
Grunderfassung_Unternehmen,
|
Grunderfassung_Unternehmen,
|
||||||
|
translate_pydantic_errors,
|
||||||
)
|
)
|
||||||
from wce_crm.form_defs import (
|
from wce_crm.form_defs import (
|
||||||
INITREC_COMP,
|
INITREC_COMP,
|
||||||
@@ -166,36 +163,6 @@ def unmerge_dict_to_list(
|
|||||||
return [dict(zip(keys, row)) for row in zip(*value_lists)]
|
return [dict(zip(keys, row)) for row in zip(*value_lists)]
|
||||||
|
|
||||||
|
|
||||||
# TODO check removal or move to basics package
|
|
||||||
def _get_leafs(data):
|
|
||||||
if isinstance(data, dict):
|
|
||||||
for value in data.values():
|
|
||||||
yield from _get_leafs(value)
|
|
||||||
elif isinstance(data, (list, tuple, set)):
|
|
||||||
for item in data:
|
|
||||||
yield from _get_leafs(item)
|
|
||||||
else:
|
|
||||||
yield data
|
|
||||||
|
|
||||||
|
|
||||||
# TODO check removal or move to basics package
|
|
||||||
def _get_leaf_dicts(data):
|
|
||||||
if isinstance(data, dict):
|
|
||||||
has_inner_dicts = False
|
|
||||||
|
|
||||||
for value in data.values():
|
|
||||||
for inner_dict in _get_leaf_dicts(value):
|
|
||||||
has_inner_dicts = True
|
|
||||||
yield inner_dict
|
|
||||||
|
|
||||||
if not has_inner_dicts:
|
|
||||||
yield data
|
|
||||||
|
|
||||||
elif isinstance(data, (list, tuple, set)):
|
|
||||||
for item in data:
|
|
||||||
yield from _get_leaf_dicts(item)
|
|
||||||
|
|
||||||
|
|
||||||
def pprint_registry(widget_registry: WidgetRegistry) -> None:
|
def pprint_registry(widget_registry: WidgetRegistry) -> None:
|
||||||
print("\n\n>>> Widget registry:")
|
print("\n\n>>> Widget registry:")
|
||||||
for key, entry in widget_registry.items():
|
for key, entry in widget_registry.items():
|
||||||
@@ -665,8 +632,6 @@ def _build_ui_recursively(
|
|||||||
else:
|
else:
|
||||||
full_key = field.key
|
full_key = field.key
|
||||||
|
|
||||||
widget: QWidget
|
|
||||||
|
|
||||||
match field.type:
|
match field.type:
|
||||||
case FormFieldType.GROUP:
|
case FormFieldType.GROUP:
|
||||||
group_box = QGroupBox(field.label)
|
group_box = QGroupBox(field.label)
|
||||||
@@ -686,11 +651,6 @@ def _build_ui_recursively(
|
|||||||
widget_registry=widget_registry,
|
widget_registry=widget_registry,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
)
|
)
|
||||||
# TODO remove
|
|
||||||
# widget_registry[full_key] = {
|
|
||||||
# "widget": widget,
|
|
||||||
# "form_field": field,
|
|
||||||
# }
|
|
||||||
keys.append(full_key)
|
keys.append(full_key)
|
||||||
|
|
||||||
return keys
|
return keys
|
||||||
@@ -722,7 +682,7 @@ def reset_form(
|
|||||||
form_field = registry_entry["form_field"]
|
form_field = registry_entry["form_field"]
|
||||||
|
|
||||||
# TODO check if this behaviour is correct in other contexts, deactivated because of
|
# TODO check if this behaviour is correct in other contexts, deactivated because of
|
||||||
# TODO company search widget
|
# company search widget
|
||||||
# if form_field.readonly:
|
# if form_field.readonly:
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
@@ -1410,6 +1370,7 @@ class AutoForm(QWidget):
|
|||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.STATE = state
|
self.STATE = state
|
||||||
|
self.SESSION_USER_KEY: Final[str] = "Metadaten_nutzer"
|
||||||
self.cfg = state.cfg
|
self.cfg = state.cfg
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QGroupBox {
|
QGroupBox {
|
||||||
@@ -1515,40 +1476,21 @@ class AutoForm(QWidget):
|
|||||||
|
|
||||||
self.debug_form_data: dict[str, Any] = {}
|
self.debug_form_data: dict[str, Any] = {}
|
||||||
if self.add_buttons:
|
if self.add_buttons:
|
||||||
self.layout_btn = QHBoxLayout()
|
self.edit_buttons = EditButtons(add_reset_btn=True)
|
||||||
self.main_layout.addLayout(self.layout_btn)
|
reset_btn = self.edit_buttons.reset_btn
|
||||||
# save
|
assert reset_btn
|
||||||
self.save_btn_txt_enabled = "Speichern (Strg + S)"
|
self.save_btn = self.edit_buttons.save_btn
|
||||||
self.save_btn_txt_disabled = "Wird gespeichert..."
|
self.reset_btn = reset_btn
|
||||||
self.save_btn = QPushButton(self.save_btn_txt_enabled)
|
self.delete_btn = self.edit_buttons.delete_btn
|
||||||
self.save_btn.setShortcut("Ctrl+S")
|
|
||||||
self.save_btn.setFixedHeight(50)
|
|
||||||
self.save_btn.setSizePolicy(
|
|
||||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
|
||||||
)
|
|
||||||
self.save_btn.clicked.connect(self.save_data)
|
self.save_btn.clicked.connect(self.save_data)
|
||||||
self.layout_btn.addWidget(self.save_btn)
|
|
||||||
# reset
|
|
||||||
self.reset_btn = QPushButton("Zurücksetzen (Strg + Z)")
|
|
||||||
self.reset_btn.setShortcut("Ctrl+Z")
|
|
||||||
self.reset_btn.setFixedHeight(50)
|
|
||||||
self.reset_btn.setSizePolicy(
|
|
||||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
|
||||||
)
|
|
||||||
self.reset_btn.clicked.connect(self.reset_form)
|
self.reset_btn.clicked.connect(self.reset_form)
|
||||||
self.layout_btn.addWidget(self.reset_btn)
|
|
||||||
# delete
|
|
||||||
self.delete_btn = QPushButton("Eintrag löschen (Strg + L)")
|
|
||||||
self.delete_btn.setShortcut("Ctrl+L")
|
|
||||||
self.delete_btn.setFixedHeight(50)
|
|
||||||
self.delete_btn.setSizePolicy(
|
|
||||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
|
||||||
)
|
|
||||||
self.delete_btn.clicked.connect(self._delete_data)
|
self.delete_btn.clicked.connect(self._delete_data)
|
||||||
self.layout_btn.addWidget(self.delete_btn)
|
|
||||||
|
self.main_layout.addWidget(self.edit_buttons)
|
||||||
|
|
||||||
logger_auto_form.debug(
|
logger_auto_form.debug(
|
||||||
"Initialised Auto Form Widget. Registry:%s",
|
"[Auto-Form] Initialised Auto Form Widget. Registry:%s",
|
||||||
pformat_registry(self.widget_registry),
|
pformat_registry(self.widget_registry),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1561,11 +1503,11 @@ class AutoForm(QWidget):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
index = None
|
index = None
|
||||||
self.STATE.rec_id = index
|
self.STATE.rec_id = index
|
||||||
logger_gui.debug("Set index to %d, new state: %s", index, self.STATE)
|
logger_gui.debug("[Auto-Form] Set index to %s, new state: %s", index, self.STATE)
|
||||||
|
|
||||||
def _disable_save(self) -> None:
|
def _disable_save(self) -> None:
|
||||||
self.save_btn.setEnabled(False)
|
self.save_btn.setEnabled(False)
|
||||||
self.save_btn.setText(self.save_btn_txt_disabled)
|
self.save_btn.setText(self.edit_buttons.save_btn_txt_disabled)
|
||||||
|
|
||||||
def _enable_save(
|
def _enable_save(
|
||||||
self,
|
self,
|
||||||
@@ -1573,7 +1515,7 @@ class AutoForm(QWidget):
|
|||||||
) -> None:
|
) -> None:
|
||||||
QTimer.singleShot(timeout, lambda: self.save_btn.setEnabled(True))
|
QTimer.singleShot(timeout, lambda: self.save_btn.setEnabled(True))
|
||||||
QTimer.singleShot(timeout + 1, lambda: self.save_btn.setShortcut("Ctrl+S"))
|
QTimer.singleShot(timeout + 1, lambda: self.save_btn.setShortcut("Ctrl+S"))
|
||||||
self.save_btn.setText(self.save_btn_txt_enabled)
|
self.save_btn.setText(self.edit_buttons.save_btn_txt_enabled)
|
||||||
|
|
||||||
def _activate_delete(self) -> None:
|
def _activate_delete(self) -> None:
|
||||||
if self.STATE.rec_id is not None:
|
if self.STATE.rec_id is not None:
|
||||||
@@ -1589,35 +1531,32 @@ class AutoForm(QWidget):
|
|||||||
|
|
||||||
def _load_data(
|
def _load_data(
|
||||||
self,
|
self,
|
||||||
lookup_id: int | None = None,
|
lookup_id: int | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if DEBUG_NO_DATABASE:
|
if DEBUG_NO_DATABASE:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger_auto_form.info(">>>> LOAD CLICKED")
|
logger_auto_form.info("[Auto-Form] Load data method...")
|
||||||
if lookup_id is None:
|
|
||||||
# only debugging
|
|
||||||
lookup_id = self.STATE.rec_id
|
|
||||||
self.reset_form()
|
self.reset_form()
|
||||||
|
|
||||||
logger_auto_form.debug("Lookup ID: %d", lookup_id)
|
logger_auto_form.debug("[Auto-Form] Lookup ID: %s", lookup_id)
|
||||||
|
|
||||||
if lookup_id is not None and lookup_id > 0:
|
if lookup_id is not None:
|
||||||
logger_auto_form.debug("Load from DB:")
|
logger_auto_form.debug("Load from DB:")
|
||||||
loaded_data = self.cfg.data_get(lookup_id)
|
loaded_data = self.cfg.data_get(lookup_id)
|
||||||
else:
|
else:
|
||||||
logger_auto_form.error("Loading: Lookup ID 0 or NULL. Do nothing!")
|
logger_auto_form.debug("[Auto-Form] Loading: Lookup ID NULL. Do nothing!")
|
||||||
return
|
return
|
||||||
|
|
||||||
logger_auto_form.debug(
|
logger_auto_form.debug(
|
||||||
"Loaded data dict:\n%s Passing to Pydantic...", pformat(loaded_data)
|
"[Auto-Form] Loaded data dict:\n%s Passing to Pydantic...", pformat(loaded_data)
|
||||||
)
|
)
|
||||||
model = self.cfg.model(**loaded_data)
|
model = self.cfg.model(**loaded_data)
|
||||||
logger_auto_form.debug("Loaded to Pydantic.")
|
logger_auto_form.debug("[Auto-Form] Loaded to Pydantic.")
|
||||||
logger_auto_form.debug("Convert to GUI structure...")
|
logger_auto_form.debug("[Auto-Form] 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("[Auto-Form] Set form data...")
|
||||||
logger_auto_form.debug("Form data:\n%s", pformat(form_data))
|
logger_auto_form.debug("[Auto-Form] Form data:\n%s", pformat(form_data))
|
||||||
self._set_form_data(form_data)
|
self._set_form_data(form_data)
|
||||||
self.STATE.rec_id = lookup_id
|
self.STATE.rec_id = lookup_id
|
||||||
self._activate_delete()
|
self._activate_delete()
|
||||||
@@ -1625,12 +1564,13 @@ class AutoForm(QWidget):
|
|||||||
def reset_form(self) -> None:
|
def reset_form(self) -> None:
|
||||||
reset_form(self.widget_registry)
|
reset_form(self.widget_registry)
|
||||||
self.STATE.rec_id = None
|
self.STATE.rec_id = None
|
||||||
|
self.STATE.form_data = None
|
||||||
self._activate_delete()
|
self._activate_delete()
|
||||||
|
|
||||||
def _get_form_data(self) -> dict[str, Any]:
|
def _get_form_data(self) -> dict[str, Any]:
|
||||||
form_data = get_form_data(self.widget_registry)
|
form_data = get_form_data(self.widget_registry)
|
||||||
logger_auto_form.debug("\n\n>>>>>>> [AutoForm] Call get form data:")
|
logger_auto_form.debug("[Auto-Form] Call get form data:")
|
||||||
logger_auto_form.debug("Form Data:\n%s", pformat(form_data))
|
logger_auto_form.debug("[Auto-Form] Form Data:\n%s", pformat(form_data))
|
||||||
|
|
||||||
if DEBUG_GET_SET:
|
if DEBUG_GET_SET:
|
||||||
self.debug_form_data = form_data
|
self.debug_form_data = form_data
|
||||||
@@ -1641,7 +1581,7 @@ class AutoForm(QWidget):
|
|||||||
self,
|
self,
|
||||||
data: dict[str, Any],
|
data: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
logger_auto_form.debug("\n\n>>>>>>> [AutoForm] Call set form data:")
|
logger_auto_form.debug("[Auto-Form] Call set form data...")
|
||||||
if DEBUG_GET_SET:
|
if DEBUG_GET_SET:
|
||||||
data = self.debug_form_data
|
data = self.debug_form_data
|
||||||
|
|
||||||
@@ -1651,7 +1591,10 @@ class AutoForm(QWidget):
|
|||||||
self._load_data(self.STATE.rec_id)
|
self._load_data(self.STATE.rec_id)
|
||||||
|
|
||||||
def _sync_GUI_to_state(self) -> None:
|
def _sync_GUI_to_state(self) -> None:
|
||||||
self.STATE.form_data = self._get_form_data()
|
form_data = self._get_form_data()
|
||||||
|
# TODO change usage of user ID (needs changes in model and backend)
|
||||||
|
form_data[self.SESSION_USER_KEY] = self.STATE.session.user_name
|
||||||
|
self.STATE.form_data = form_data
|
||||||
|
|
||||||
def validate(self) -> list[str]:
|
def validate(self) -> list[str]:
|
||||||
return validate_form_data(self.widget_registry)
|
return validate_form_data(self.widget_registry)
|
||||||
@@ -1661,8 +1604,6 @@ class AutoForm(QWidget):
|
|||||||
new_state: AutoForm_State,
|
new_state: AutoForm_State,
|
||||||
) -> None:
|
) -> None:
|
||||||
set_page_state(self.STATE, new_state)
|
set_page_state(self.STATE, new_state)
|
||||||
# self.STATE.rec_id = new_state.rec_id
|
|
||||||
# self.STATE.form_data = new_state.form_data
|
|
||||||
self._sync_state_to_GUI()
|
self._sync_state_to_GUI()
|
||||||
|
|
||||||
def get_state(self) -> AutoForm_State:
|
def get_state(self) -> AutoForm_State:
|
||||||
@@ -1675,50 +1616,29 @@ class AutoForm(QWidget):
|
|||||||
errors = self.validate()
|
errors = self.validate()
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
# errors: abort saving and show pop up window
|
logger_auto_form.error(
|
||||||
error_text = (
|
"[Auto-Form] Error during GUI validation phase:\n%s", pformat(errors)
|
||||||
"Bitte füllen Sie die folgenden Pflichtfelder aus:\n\n▸ "
|
|
||||||
+ "\n▸ ".join(errors)
|
|
||||||
)
|
)
|
||||||
QMessageBox.warning(self, "Fehlende Angaben", error_text)
|
GUI_validation_error_handling(errors)
|
||||||
self._enable_save()
|
self._enable_save()
|
||||||
return
|
return
|
||||||
|
|
||||||
# no errors: data can be saved
|
logger_auto_form.info("[Auto-Form] Success! All data is valid")
|
||||||
logger_auto_form.info("Erfolg! Alle Daten sind valide.")
|
logger_auto_form.info("[Auto-Form] Get form data call...")
|
||||||
logger_auto_form.info("Get form data call...")
|
state = self.get_state()
|
||||||
form_data = self._get_form_data()
|
form_data = state.form_data
|
||||||
|
assert form_data, "form data not set"
|
||||||
|
|
||||||
logger_auto_form.debug(
|
logger_auto_form.debug("[Auto-Form] Get form data\n%s", pformat(form_data))
|
||||||
"\n\n------------>>>>>>>>> Get form data\n%s", pformat(form_data)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger_auto_form.debug("------------>>>>>>>>> Call Pydantic")
|
logger_auto_form.debug("[Auto-Form] Call Pydantic...")
|
||||||
validated_data = self.cfg.model(**form_data)
|
validated_data = self.cfg.model(**form_data)
|
||||||
# # TODO add user?
|
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
# catch errors and show them in GUI
|
logger_auto_form.error(
|
||||||
error_texts = []
|
"[Auto-Form] Error during Pydantic validation phase:\n%s", pformat(e.errors())
|
||||||
|
)
|
||||||
# Pydantic detailed error list
|
pydantic_validation_error_handling(e)
|
||||||
for error in e.errors():
|
|
||||||
logger_gui.error("Error during validation phase:\n%s", pformat(error))
|
|
||||||
|
|
||||||
error_field = str(error["loc"][0])
|
|
||||||
reason = error["msg"]
|
|
||||||
|
|
||||||
error_texts.append(f"- {error_field}: {reason}")
|
|
||||||
|
|
||||||
# TODO: Is this needed and feasible?
|
|
||||||
# formatting red
|
|
||||||
# if fehlerhaftes_feld in self.widgets:
|
|
||||||
# self.widgets[fehlerhaftes_feld].setStyleSheet(
|
|
||||||
# "border: 1px solid red; background: #fef2f2;"
|
|
||||||
# )
|
|
||||||
|
|
||||||
# tell user what went wrong
|
|
||||||
QMessageBox.warning(self, "Eingabefehler", "\n".join(error_texts))
|
|
||||||
else:
|
else:
|
||||||
if DEBUG_NO_DATABASE:
|
if DEBUG_NO_DATABASE:
|
||||||
return
|
return
|
||||||
@@ -1726,20 +1646,20 @@ class AutoForm(QWidget):
|
|||||||
# save data to database
|
# save data to database
|
||||||
db_data = validated_data.to_db(exclude=self.cfg.ignored_keys)
|
db_data = validated_data.to_db(exclude=self.cfg.ignored_keys)
|
||||||
logger_auto_form.debug(
|
logger_auto_form.debug(
|
||||||
("Form data with 'exlude' (must be saved in the database):\n%s"),
|
"[Auto-Form] Form data with 'exlude' (must be saved in the database):\n%s",
|
||||||
pformat(db_data),
|
pformat(db_data),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.STATE.rec_id is None:
|
if self.STATE.rec_id is None:
|
||||||
logger_auto_form.debug("Insert triggered")
|
logger_auto_form.debug("[Auto-Form] Insert triggered...")
|
||||||
init_rec_id = self.cfg.data_insert(db_data)
|
init_rec_id = self.cfg.data_insert(db_data)
|
||||||
assert isinstance(init_rec_id, int)
|
assert isinstance(init_rec_id, int)
|
||||||
self.STATE.rec_id = init_rec_id
|
self.STATE.rec_id = init_rec_id
|
||||||
else:
|
else:
|
||||||
logger_auto_form.debug("Update triggered")
|
logger_auto_form.debug("[Auto-Form] Update triggered...")
|
||||||
self.cfg.data_update(self.STATE.rec_id, db_data)
|
self.cfg.data_update(self.STATE.rec_id, db_data)
|
||||||
|
|
||||||
logger_auto_form.info("Data saved successfully")
|
logger_auto_form.info("[Auto-Form] Data saved successfully")
|
||||||
self.update_triggered.emit()
|
self.update_triggered.emit()
|
||||||
self._activate_delete()
|
self._activate_delete()
|
||||||
finally:
|
finally:
|
||||||
@@ -3688,19 +3608,6 @@ class Page_Consulting(QWidget):
|
|||||||
# state management
|
# state management
|
||||||
self.STATE = Page_Consulting_State(session=session)
|
self.STATE = Page_Consulting_State(session=session)
|
||||||
|
|
||||||
# self.current_id: InitRecId = -1
|
|
||||||
# self.COMPANY_FETAURES_MASTER_DATA: dict[str, str] = {
|
|
||||||
# "ma_unternehmensname": "Name:",
|
|
||||||
# "ma_branche": "Branche:",
|
|
||||||
# "ma_strasse": "Straße:",
|
|
||||||
# "ma_hausnummer": "Hausnummer:",
|
|
||||||
# "ma_plz": "PLZ:",
|
|
||||||
# "ma_ort": "Ort:",
|
|
||||||
# "ma_website": "Webseite:",
|
|
||||||
# "ma_mail": "E-Mail:",
|
|
||||||
# "ma_telefonnummer": "Telefon:",
|
|
||||||
# }
|
|
||||||
|
|
||||||
# Hauptlayout der Seite
|
# Hauptlayout der Seite
|
||||||
outer_layout = QHBoxLayout(self)
|
outer_layout = QHBoxLayout(self)
|
||||||
vert_layout = QVBoxLayout()
|
vert_layout = QVBoxLayout()
|
||||||
@@ -3907,8 +3814,6 @@ class Page_Consulting(QWidget):
|
|||||||
self,
|
self,
|
||||||
new_state: Page_Consulting_State,
|
new_state: Page_Consulting_State,
|
||||||
) -> None:
|
) -> None:
|
||||||
# TODO add correct loading behaviour
|
|
||||||
# TODO add validation (correct type with necessary information)
|
|
||||||
logger_page_consulting.debug("[Consulting] Loading with request:\n%s", new_state)
|
logger_page_consulting.debug("[Consulting] Loading with request:\n%s", new_state)
|
||||||
set_page_state(self.STATE, new_state)
|
set_page_state(self.STATE, new_state)
|
||||||
# child modules
|
# child modules
|
||||||
@@ -4696,19 +4601,184 @@ def global_exception_handler(exc_type, exc_value, exc_traceback):
|
|||||||
# message to user
|
# message to user
|
||||||
# check if QApplication exists otherwise crashing of crash handler possible)
|
# check if QApplication exists otherwise crashing of crash handler possible)
|
||||||
if QApplication.instance():
|
if QApplication.instance():
|
||||||
msg_box = QMessageBox()
|
msg_box = get_error_message_box(
|
||||||
msg_box.setIcon(QMessageBox.Icon.Critical)
|
QMessageBox.Icon.Critical,
|
||||||
msg_box.setWindowTitle("Kritischer Fehler")
|
"Kritischer Fehler",
|
||||||
msg_box.setText(
|
"Ein unerwarteter Fehler ist aufgetreten. Die Details wurden protokolliert.",
|
||||||
"Ein unerwarteter Fehler ist aufgetreten. Die Details wurden protokolliert."
|
error_msg,
|
||||||
)
|
)
|
||||||
# details for user or screenshots
|
# msg_box.setIcon(QMessageBox.Icon.Critical)
|
||||||
msg_box.setDetailedText(error_msg)
|
# msg_box.setWindowTitle("Kritischer Fehler")
|
||||||
|
# msg_box.setText(
|
||||||
|
# "Ein unerwarteter Fehler ist aufgetreten. Die Details wurden protokolliert."
|
||||||
|
# )
|
||||||
|
# # details for user or screenshots
|
||||||
|
# msg_box.setDetailedText(error_msg)
|
||||||
msg_box.exec()
|
msg_box.exec()
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_error_message_box(
|
||||||
|
msg_type: QMessageBox.Icon,
|
||||||
|
title: str,
|
||||||
|
message: str,
|
||||||
|
detailed_text: str | None = None,
|
||||||
|
) -> QMessageBox:
|
||||||
|
msg_box = QMessageBox()
|
||||||
|
msg_box.setIcon(msg_type)
|
||||||
|
msg_box.setWindowTitle(title)
|
||||||
|
msg_box.setText(message)
|
||||||
|
if detailed_text is not None:
|
||||||
|
msg_box.setDetailedText(detailed_text)
|
||||||
|
|
||||||
|
return msg_box
|
||||||
|
|
||||||
|
|
||||||
|
def pydantic_validation_error_handling(
|
||||||
|
val_error: ValidationError,
|
||||||
|
) -> None:
|
||||||
|
error_texts: list[str] = []
|
||||||
|
translated_errors = translate_pydantic_errors(val_error.errors())
|
||||||
|
|
||||||
|
for error in translated_errors:
|
||||||
|
error_field = str(error["loc"][0])
|
||||||
|
reason = error["msg"]
|
||||||
|
path = " → ".join(error["loc"][:-1]) # type: ignore
|
||||||
|
|
||||||
|
error_texts.append(f"- {error_field}: {reason}, (Pfad: {path})")
|
||||||
|
|
||||||
|
msg_box = get_error_message_box(
|
||||||
|
QMessageBox.Icon.Warning,
|
||||||
|
"Fehler bei der Validierung der Eingabedaten",
|
||||||
|
(
|
||||||
|
"Bei der Validierung der Eingaben ist ein Fehler aufgetreten. Details "
|
||||||
|
"sind unten angefügt."
|
||||||
|
),
|
||||||
|
"\n".join(error_texts),
|
||||||
|
)
|
||||||
|
msg_box.exec()
|
||||||
|
|
||||||
|
|
||||||
|
def GUI_validation_error_handling(
|
||||||
|
errors: list[str],
|
||||||
|
) -> None:
|
||||||
|
error_text = "Bitte füllen Sie die folgenden Pflichtfelder aus:\n\n▸ " + "\n▸ ".join(
|
||||||
|
errors
|
||||||
|
)
|
||||||
|
msg_box = get_error_message_box(
|
||||||
|
QMessageBox.Icon.Warning,
|
||||||
|
"Fehlende oder fehlerhafte Angaben",
|
||||||
|
error_text,
|
||||||
|
)
|
||||||
|
msg_box.exec()
|
||||||
|
|
||||||
|
|
||||||
|
# @dc.dataclass(slots=True)
|
||||||
|
# class EditButton:
|
||||||
|
# btn: QWidget
|
||||||
|
# txt_enabled: str
|
||||||
|
# txt_disabled: str
|
||||||
|
|
||||||
|
|
||||||
|
# @dc.dataclass(slots=True, kw_only=True)
|
||||||
|
# class EditButtonLayout:
|
||||||
|
# layout: QLayout
|
||||||
|
# save: EditButton
|
||||||
|
# delete: EditButton
|
||||||
|
# reset: EditButton | None
|
||||||
|
|
||||||
|
|
||||||
|
class EditButtons(QWidget):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
add_reset_btn: bool,
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
layout_btn = QHBoxLayout(self)
|
||||||
|
layout_btn.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# main_layout.addLayout(layout_btn) # TODO add in component
|
||||||
|
# save
|
||||||
|
self.save_btn_txt_enabled = "Speichern (Strg + S)"
|
||||||
|
self.save_btn_txt_disabled = "Wird gespeichert..."
|
||||||
|
self.save_btn = QPushButton(self.save_btn_txt_enabled)
|
||||||
|
self.save_btn.setShortcut("Ctrl+S")
|
||||||
|
self.save_btn.setFixedHeight(50)
|
||||||
|
self.save_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
# save_btn.clicked.connect(save_data) # TODO add in component
|
||||||
|
layout_btn.addWidget(self.save_btn)
|
||||||
|
|
||||||
|
# reset
|
||||||
|
self.reset_button: QPushButton | None = None
|
||||||
|
if add_reset_btn:
|
||||||
|
self.reset_btn = QPushButton("Zurücksetzen (Strg + Z)")
|
||||||
|
self.reset_btn.setShortcut("Ctrl+Z")
|
||||||
|
self.reset_btn.setFixedHeight(50)
|
||||||
|
self.reset_btn.setSizePolicy(
|
||||||
|
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
||||||
|
)
|
||||||
|
# reset_btn.clicked.connect(reset_form) # TODO add in component
|
||||||
|
layout_btn.addWidget(self.reset_btn)
|
||||||
|
|
||||||
|
# delete
|
||||||
|
self.delete_btn = QPushButton("Eintrag löschen (Strg + L)")
|
||||||
|
self.delete_btn.setShortcut("Ctrl+L")
|
||||||
|
self.delete_btn.setFixedHeight(50)
|
||||||
|
self.delete_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
# delete_btn.clicked.connect(self._delete_data) # TODO add in component
|
||||||
|
layout_btn.addWidget(self.delete_btn)
|
||||||
|
|
||||||
|
|
||||||
|
# def get_edit_buttons(
|
||||||
|
# add_reset_btn: bool,
|
||||||
|
# ) -> EditButtonLayout:
|
||||||
|
# layout_btn = QHBoxLayout()
|
||||||
|
# # main_layout.addLayout(layout_btn) # TODO add in component
|
||||||
|
# # save
|
||||||
|
# save_btn_txt_enabled = "Speichern (Strg + S)"
|
||||||
|
# save_btn_txt_disabled = "Wird gespeichert..."
|
||||||
|
# save_btn = QPushButton(save_btn_txt_enabled)
|
||||||
|
# save_btn.setShortcut("Ctrl+S")
|
||||||
|
# save_btn.setFixedHeight(50)
|
||||||
|
# save_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
# # save_btn.clicked.connect(save_data) # TODO add in component
|
||||||
|
# edit_button_save = EditButton(save_btn, "Speichern (Strg + S)", "Wird gespeichert...")
|
||||||
|
# layout_btn.addWidget(save_btn)
|
||||||
|
|
||||||
|
# # reset
|
||||||
|
# edit_button_reset: EditButton | None = None
|
||||||
|
# if add_reset_btn:
|
||||||
|
# reset_btn = QPushButton("Zurücksetzen (Strg + Z)")
|
||||||
|
# reset_btn.setShortcut("Ctrl+Z")
|
||||||
|
# reset_btn.setFixedHeight(50)
|
||||||
|
# reset_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
# # reset_btn.clicked.connect(reset_form) # TODO add in component
|
||||||
|
# edit_button_reset = EditButton(
|
||||||
|
# save_btn, "Zurücksetzen (Strg + Z)", "Zurücksetzen (Strg + Z)"
|
||||||
|
# )
|
||||||
|
# layout_btn.addWidget(reset_btn)
|
||||||
|
|
||||||
|
# # delete
|
||||||
|
# delete_btn = QPushButton("Eintrag löschen (Strg + L)")
|
||||||
|
# delete_btn.setShortcut("Ctrl+L")
|
||||||
|
# delete_btn.setFixedHeight(50)
|
||||||
|
# delete_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
# edit_button_delete = EditButton(
|
||||||
|
# save_btn, "Eintrag löschen (Strg + L)", "Eintrag löschen (Strg + L)"
|
||||||
|
# )
|
||||||
|
# # delete_btn.clicked.connect(self._delete_data) # TODO add in component
|
||||||
|
# layout_btn.addWidget(delete_btn)
|
||||||
|
|
||||||
|
# return EditButtonLayout(
|
||||||
|
# layout=layout_btn,
|
||||||
|
# save=edit_button_save,
|
||||||
|
# delete=edit_button_delete,
|
||||||
|
# reset=edit_button_reset,
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
def qt_message_handler(mode, context, message):
|
def qt_message_handler(mode, context, message):
|
||||||
"""forwards internal Qt C++ warnings to Python loggers"""
|
"""forwards internal Qt C++ warnings to Python loggers"""
|
||||||
if mode == QtMsgType.QtInfoMsg:
|
if mode == QtMsgType.QtInfoMsg:
|
||||||
|
|||||||
Reference in New Issue
Block a user