generated from dopt-python/py311
nested validation and error messages
This commit is contained in:
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import dataclasses as dc
|
||||
import enum
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
@@ -319,8 +320,10 @@ class FormField:
|
||||
elif self.type is FormFieldType.DROPDOWN:
|
||||
self.dropdown_options = tuple(DropdownOption(op[0], op[1]) for op in options)
|
||||
|
||||
for child in self.children:
|
||||
child.parent = self
|
||||
if self.children:
|
||||
self.required = any((child.required for child in self.children))
|
||||
for child in self.children:
|
||||
child.parent = self
|
||||
|
||||
|
||||
class WidgetRegistryEntry(TypedDict):
|
||||
@@ -700,7 +703,7 @@ FORM_FIELDS_SCHOOL = [
|
||||
FormField(
|
||||
"Abschluss",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
required=True,
|
||||
),
|
||||
FormField(
|
||||
"Abschlussgrad laut Dokument",
|
||||
@@ -1157,6 +1160,104 @@ def _insert_nested(
|
||||
target_dict[key_path[-1]] = value
|
||||
|
||||
|
||||
def get_form_data(
|
||||
widget_registry: WidgetRegistry,
|
||||
) -> dict[str, Any]:
|
||||
raw_data = {}
|
||||
for key, registry_entry in widget_registry.items():
|
||||
value: Any | None = None
|
||||
|
||||
widget = registry_entry["widget"]
|
||||
if isinstance(widget, QLineEdit):
|
||||
value = widget.text()
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
value = widget.toPlainText()
|
||||
elif isinstance(widget, QDateEdit):
|
||||
qt_date = widget.date()
|
||||
value = qt_date.toPython()
|
||||
elif isinstance(widget, QComboBox):
|
||||
value = widget.currentData()
|
||||
elif isinstance(widget, DynamicListWidget):
|
||||
# this should be a list: each dynamic list contains a list
|
||||
# of such dictionaries
|
||||
form_data = widget.get_form_data()
|
||||
value = [val for val in form_data.values()]
|
||||
|
||||
_insert_nested(raw_data, key.split("."), value)
|
||||
|
||||
return raw_data
|
||||
|
||||
|
||||
DYNAMIC_LIST_KEY_PATTERN = re.compile(r"-\[(\d+)\]")
|
||||
|
||||
|
||||
def validate_form_data(
|
||||
widget_registry: WidgetRegistry,
|
||||
) -> list[str]:
|
||||
errors: list[str] = []
|
||||
|
||||
for key, registry_entry in widget_registry.items():
|
||||
error_post: bool = False
|
||||
widget = registry_entry["widget"]
|
||||
form_field = registry_entry["form_field"]
|
||||
|
||||
if not form_field.readonly:
|
||||
widget.setStyleSheet("")
|
||||
|
||||
if not form_field.required:
|
||||
continue
|
||||
|
||||
dynamic_list_num: str = ""
|
||||
if (
|
||||
form_field.parent is not None
|
||||
and form_field.parent.type is FormFieldType.DYNAMIC_LIST
|
||||
):
|
||||
# get also number of group for enhanced error messages
|
||||
matches = DYNAMIC_LIST_KEY_PATTERN.search(key)
|
||||
if matches:
|
||||
dynamic_list_num = matches.group(1)
|
||||
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
if widget.text().strip():
|
||||
continue
|
||||
error_post = True
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
if widget.toPlainText().strip():
|
||||
continue
|
||||
error_post = True
|
||||
elif isinstance(widget, QComboBox):
|
||||
if widget.currentData() is not None:
|
||||
continue
|
||||
error_post = True
|
||||
elif isinstance(widget, DynamicListWidget):
|
||||
errors_widget = widget.validate_form_data()
|
||||
if not errors_widget:
|
||||
continue
|
||||
errors.extend(errors_widget)
|
||||
|
||||
if not error_post:
|
||||
continue
|
||||
|
||||
error = form_field.label.replace("*", "").replace(":", "")
|
||||
if form_field.parent is not None:
|
||||
parent_label = form_field.parent.label.replace("*", "").replace(":", "")
|
||||
if dynamic_list_num:
|
||||
error = f"{parent_label} → {f'{parent_label} {dynamic_list_num}'} → {error}"
|
||||
else:
|
||||
error = f"{parent_label} → {error}"
|
||||
error = error.replace("&&", "&")
|
||||
errors.append(error)
|
||||
# optical feedback to highlight erroneous cells
|
||||
widget.setStyleSheet("""
|
||||
border: 1px solid #ef4444;
|
||||
background-color: #ffe9e9;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
""")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
class AutoForm(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
@@ -1249,45 +1350,13 @@ class AutoForm(QWidget):
|
||||
|
||||
def on_save_clicked(self) -> None:
|
||||
self._disable_save()
|
||||
errors: list[str] = []
|
||||
|
||||
for registry_entry in self.widget_registry.values():
|
||||
widget = registry_entry["widget"]
|
||||
form_field = registry_entry["form_field"]
|
||||
|
||||
if not form_field.readonly:
|
||||
widget.setStyleSheet("")
|
||||
|
||||
if not form_field.required:
|
||||
continue
|
||||
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
if widget.text().strip():
|
||||
continue
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
if widget.toPlainText().strip():
|
||||
continue
|
||||
elif isinstance(widget, QComboBox):
|
||||
if widget.currentData() is not None:
|
||||
continue
|
||||
|
||||
error = form_field.label.replace("*", "").replace(":", "")
|
||||
if form_field.parent is not None:
|
||||
error = f"{form_field.parent.label}: {error}"
|
||||
errors.append(error)
|
||||
# optical feedback to highlight erroneous cells
|
||||
widget.setStyleSheet("""
|
||||
border: 1px solid #ef4444;
|
||||
background-color: #ffe9e9;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
""")
|
||||
errors = validate_form_data(self.widget_registry)
|
||||
|
||||
if errors:
|
||||
# errors: abort saving and show pop up window
|
||||
error_text = (
|
||||
"Bitte füllen Sie die folgenden Pflichtfelder aus:\n\n- "
|
||||
+ "\n- ".join(errors)
|
||||
"Bitte füllen Sie die folgenden Pflichtfelder aus:\n\n▸ "
|
||||
+ "\n▸ ".join(errors)
|
||||
)
|
||||
QMessageBox.warning(self, "Fehlende Angaben", error_text)
|
||||
self._enable_save()
|
||||
@@ -1310,30 +1379,7 @@ class AutoForm(QWidget):
|
||||
reset_form(self.widget_registry)
|
||||
|
||||
def get_form_data(self) -> dict[str, Any]:
|
||||
# raise NotImplementedError()
|
||||
|
||||
raw_data = {}
|
||||
for key, registry_entry in self.widget_registry.items():
|
||||
value: Any | None = None
|
||||
|
||||
widget = registry_entry["widget"]
|
||||
if isinstance(widget, QLineEdit):
|
||||
value = widget.text()
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
value = widget.toPlainText()
|
||||
elif isinstance(widget, QDateEdit):
|
||||
qt_date = widget.date()
|
||||
value = qt_date.toPython()
|
||||
elif isinstance(widget, QComboBox):
|
||||
value = widget.currentData()
|
||||
elif isinstance(widget, DynamicListWidget):
|
||||
# TODO add method
|
||||
# value = widget.get_data()
|
||||
# this should be a list: each dynamic list contains a list
|
||||
# of such dictionaries
|
||||
value = "test"
|
||||
|
||||
_insert_nested(raw_data, key.split("."), value)
|
||||
raw_data = get_form_data(self.widget_registry)
|
||||
|
||||
return raw_data
|
||||
|
||||
@@ -1442,10 +1488,6 @@ class DynamicListWidget(QWidget):
|
||||
self.inner_layout.insertWidget(self.inner_layout.count() - 1, entry_box)
|
||||
self.update_sub_forms()
|
||||
|
||||
# print("------------>>>>>>>>> Added entry, length widget registry:")
|
||||
# pprint(len(self.widget_registry))
|
||||
# pprint(list(self.widget_registry.keys()))
|
||||
|
||||
def remove_entry(
|
||||
self,
|
||||
subform_to_remove: SubForm,
|
||||
@@ -1454,9 +1496,6 @@ class DynamicListWidget(QWidget):
|
||||
subform_to_remove.entry_box.deleteLater()
|
||||
self.sub_forms.remove(subform_to_remove)
|
||||
self.update_sub_forms()
|
||||
# print("------------>>>>>>>>> Removed entry, length widget registry:")
|
||||
# pprint(len(self.widget_registry))
|
||||
# pprint(list(self.widget_registry.keys()))
|
||||
|
||||
def update_sub_forms(self):
|
||||
for index, sub_form in enumerate(self.sub_forms, start=1):
|
||||
@@ -1470,22 +1509,17 @@ class DynamicListWidget(QWidget):
|
||||
def reset_form(self) -> None:
|
||||
reset_form(self.widget_registry)
|
||||
|
||||
def validate_form_data(self) -> list[str]:
|
||||
return validate_form_data(self.widget_registry)
|
||||
|
||||
def load_form_data(self) -> None:
|
||||
# TODO add way to load data when initialised (probably with click)
|
||||
...
|
||||
|
||||
def get_form_data(self):
|
||||
raise NotImplementedError()
|
||||
raw_data = get_form_data(self.widget_registry)
|
||||
|
||||
results = []
|
||||
for form in self.sub_forms:
|
||||
# Hier bauen wir ein Dictionary pro Sub-Formular
|
||||
entry_data = {
|
||||
"strasse": form["widgets"]["strasse"].text(),
|
||||
"ort": form["widgets"]["ort"].text(),
|
||||
}
|
||||
results.append(entry_data)
|
||||
return results
|
||||
return raw_data
|
||||
|
||||
|
||||
class ClickableCell(QFrame):
|
||||
@@ -1604,7 +1638,6 @@ class NewEntrySelect_view(QWidget):
|
||||
btn_company.setFixedWidth(300)
|
||||
btn_company.setFixedHeight(40)
|
||||
btn_company.clicked.connect(lambda: self.company_requested.emit())
|
||||
# btn_company.clicked.connect(lambda: print("Unternehmen gewählt"))
|
||||
|
||||
btn_person = QPushButton("Individualperson →")
|
||||
btn_person.setFixedWidth(300)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# %%
|
||||
import dataclasses as dc
|
||||
import enum
|
||||
import re
|
||||
|
||||
from PySide6.QtCore import QDate, Qt
|
||||
|
||||
@@ -27,6 +28,18 @@ class FormField:
|
||||
self.label += "*"
|
||||
|
||||
|
||||
# %%
|
||||
DYNAMIC_LIST_KEY_PATTERN = r"-\[(\d+)\]"
|
||||
key = "Schulbildung-[12].7b8da0f7-7a0e-4f71-878a-85616099e849"
|
||||
|
||||
matches = re.search(DYNAMIC_LIST_KEY_PATTERN, key)
|
||||
|
||||
# %%
|
||||
matches
|
||||
|
||||
# %%
|
||||
matches.group(1)
|
||||
|
||||
# %%
|
||||
t_str = "asd.yxcxc.dfgjj.aasdsdsdsd.sdsdsdsd"
|
||||
splitted = t_str.split(".")
|
||||
|
||||
Reference in New Issue
Block a user