generated from dopt-python/py311
Dev #7
+325
-227
@@ -93,6 +93,7 @@ from wce_crm.form_defs import (
|
||||
)
|
||||
from wce_crm.logging import (
|
||||
logger_auto_form,
|
||||
logger_custom_fields,
|
||||
logger_get_data,
|
||||
logger_gui,
|
||||
logger_page_consulting,
|
||||
@@ -107,6 +108,7 @@ V = TypeVar("V")
|
||||
T = TypeVar("T")
|
||||
W = TypeVar("W", bound="QWidget")
|
||||
M = TypeVar("M", bound="Module")
|
||||
S = TypeVar("S", bound="PageState")
|
||||
|
||||
DEBUG: bool = True
|
||||
DEBUG_SEARCH_WIDGET: bool = False
|
||||
@@ -225,16 +227,45 @@ class CustomForm(Protocol):
|
||||
def validate_form_data(self) -> list[str]: ...
|
||||
|
||||
|
||||
class Module(Protocol):
|
||||
def __sync_state_to_GUI(self) -> None: ... # type: ignore
|
||||
class WrapperModule(Protocol):
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
"""build states for all child modules and use their `load_state` method
|
||||
to initialise them with the new data
|
||||
"""
|
||||
...
|
||||
|
||||
def __sync_GUI_to_state(self) -> None: ... # type: ignore
|
||||
|
||||
def validate(self) -> None: ...
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
"""call `get_state` method on all child modules and assign respective
|
||||
properties to state of parent module
|
||||
"""
|
||||
...
|
||||
|
||||
def load_state(self, new_state: Any) -> None: ...
|
||||
|
||||
def get_state(self) -> Any: ...
|
||||
def get_state(self) -> Any:
|
||||
"""should always be a simple call to `_sync_GUI_to_state` and then returning
|
||||
the module's state
|
||||
|
||||
Returns
|
||||
-------
|
||||
Any
|
||||
state of the respective module
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class Module(WrapperModule, Protocol):
|
||||
def validate(self) -> list[str]:
|
||||
"""should be a list of field names which could not be validated successfully,
|
||||
propagates through: parent components must call this on all child modules and extend
|
||||
their own error list with the return value
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[str]
|
||||
list of error fields (form field label)
|
||||
"""
|
||||
...
|
||||
|
||||
def save_data(self) -> None: ...
|
||||
|
||||
@@ -268,26 +299,6 @@ class AutoFormDelete(Protocol):
|
||||
) -> None: ...
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class AutoFormConfig:
|
||||
model: type[FlatBaseModel]
|
||||
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
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class AutoForm_State:
|
||||
session: Session
|
||||
cfg: AutoFormConfig
|
||||
rec_id: RecId | None = None
|
||||
form_data: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class CustomWidget(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
@@ -337,6 +348,26 @@ class Page_InitRecCompany_State(PageState[Module]):
|
||||
un_id: RecId | None = None
|
||||
|
||||
|
||||
@dc.dataclass(slots=True, kw_only=True)
|
||||
class AutoFormConfig:
|
||||
model: type[FlatBaseModel]
|
||||
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
|
||||
|
||||
|
||||
@dc.dataclass(slots=True, kw_only=True)
|
||||
class AutoForm_State(PageState[Module]):
|
||||
session: Session
|
||||
cfg: AutoFormConfig
|
||||
rec_id: RecId | None = None
|
||||
form_data: dict[str, Any] | None = None
|
||||
|
||||
|
||||
@dc.dataclass(slots=True, kw_only=True)
|
||||
class Page_InitRecPerson_State(PageState[Module]):
|
||||
session: Session
|
||||
@@ -415,6 +446,7 @@ def _add_widget_to_layout(
|
||||
def build_and_add_widget(
|
||||
form_field: FormField[W],
|
||||
parent_layout: QLayout,
|
||||
widget_registry: WidgetRegistry | None = None,
|
||||
prefix: str = "",
|
||||
) -> tuple[str, W]:
|
||||
no_scroll_filter = NoScrollFilter(parent_layout)
|
||||
@@ -609,6 +641,12 @@ def build_and_add_widget(
|
||||
|
||||
_add_widget_to_layout(form_field, widget, parent_layout, widget_only=layout_widget_only)
|
||||
|
||||
if widget_registry is not None:
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": form_field,
|
||||
}
|
||||
|
||||
return full_key, cast(W, widget)
|
||||
|
||||
|
||||
@@ -642,11 +680,17 @@ def _build_ui_recursively(
|
||||
parent_layout.addRow(group_box)
|
||||
|
||||
case _:
|
||||
full_key, widget = build_and_add_widget(field, parent_layout, prefix=prefix)
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
full_key, _ = build_and_add_widget(
|
||||
field,
|
||||
parent_layout,
|
||||
widget_registry=widget_registry,
|
||||
prefix=prefix,
|
||||
)
|
||||
# TODO remove
|
||||
# widget_registry[full_key] = {
|
||||
# "widget": widget,
|
||||
# "form_field": field,
|
||||
# }
|
||||
keys.append(full_key)
|
||||
|
||||
return keys
|
||||
@@ -1010,6 +1054,7 @@ def validate_form_data(
|
||||
|
||||
for key, registry_entry in widget_registry.items():
|
||||
error_post: bool = False
|
||||
custom_formatting: bool = False
|
||||
widget = registry_entry["widget"]
|
||||
form_field = registry_entry["form_field"]
|
||||
|
||||
@@ -1041,16 +1086,10 @@ def validate_form_data(
|
||||
if widget.currentData() is not None:
|
||||
continue
|
||||
error_post = True
|
||||
elif isinstance(widget, FlexibleDateInput):
|
||||
date = widget.get_date()
|
||||
if date and date.isValid():
|
||||
continue
|
||||
error_post = True
|
||||
elif isinstance(widget, FlexibleDateTimeInput):
|
||||
datetime = widget.get_datetime()
|
||||
if datetime and datetime.isValid():
|
||||
continue
|
||||
error_post = True
|
||||
elif isinstance(widget, (FlexibleDateInput, FlexibleDateTimeInput)):
|
||||
valid = widget.validate()
|
||||
error_post = not valid
|
||||
custom_formatting = True
|
||||
elif isinstance(
|
||||
widget,
|
||||
(
|
||||
@@ -1078,6 +1117,7 @@ def validate_form_data(
|
||||
error = error.replace("&&", "&")
|
||||
errors.append(error)
|
||||
# optical feedback to highlight erroneous cells
|
||||
if not custom_formatting:
|
||||
widget.setStyleSheet("""
|
||||
border: 1px solid #ef4444;
|
||||
background-color: #ffe9e9;
|
||||
@@ -1088,6 +1128,14 @@ def validate_form_data(
|
||||
return errors
|
||||
|
||||
|
||||
def validate_child_modules(modules: Iterable[Module]) -> list[str]:
|
||||
errors: list[str] = []
|
||||
for mod in modules:
|
||||
errors.extend(mod.validate())
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
class Grunderfassung_SuchWidget(CustomWidget):
|
||||
def __init__(
|
||||
self,
|
||||
@@ -1487,7 +1535,7 @@ class AutoForm(QWidget):
|
||||
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)")
|
||||
@@ -1527,7 +1575,7 @@ class AutoForm(QWidget):
|
||||
QTimer.singleShot(timeout + 1, lambda: self.save_btn.setShortcut("Ctrl+S"))
|
||||
self.save_btn.setText(self.save_btn_txt_enabled)
|
||||
|
||||
def _activation_delete(self) -> None:
|
||||
def _activate_delete(self) -> None:
|
||||
if self.STATE.rec_id is not None:
|
||||
self.delete_btn.setEnabled(True)
|
||||
else:
|
||||
@@ -1537,7 +1585,7 @@ class AutoForm(QWidget):
|
||||
assert self.STATE.rec_id is not None, "deletion initialised despite no index set"
|
||||
self.cfg.data_delete(self.STATE.rec_id)
|
||||
self.update_triggered.emit()
|
||||
self._reset_form()
|
||||
self.reset_form()
|
||||
|
||||
def _load_data(
|
||||
self,
|
||||
@@ -1550,7 +1598,7 @@ class AutoForm(QWidget):
|
||||
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)
|
||||
|
||||
@@ -1572,15 +1620,12 @@ class AutoForm(QWidget):
|
||||
logger_auto_form.debug("Form data:\n%s", pformat(form_data))
|
||||
self._set_form_data(form_data)
|
||||
self.STATE.rec_id = lookup_id
|
||||
self._activation_delete()
|
||||
self._activate_delete()
|
||||
|
||||
def _validate_form_data(self) -> list[str]:
|
||||
return validate_form_data(self.widget_registry)
|
||||
|
||||
def _reset_form(self) -> None:
|
||||
def reset_form(self) -> None:
|
||||
reset_form(self.widget_registry)
|
||||
self.STATE.rec_id = None
|
||||
self._activation_delete()
|
||||
self._activate_delete()
|
||||
|
||||
def _get_form_data(self) -> dict[str, Any]:
|
||||
form_data = get_form_data(self.widget_registry)
|
||||
@@ -1602,32 +1647,32 @@ class AutoForm(QWidget):
|
||||
|
||||
set_form_data(self.widget_registry, data)
|
||||
|
||||
def sync_state_to_GUI(self) -> None:
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
self._load_data(self.STATE.rec_id)
|
||||
|
||||
def __sync_GUI_to_state(self) -> None:
|
||||
form_data = self._get_form_data()
|
||||
self.STATE.form_data = form_data
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
self.STATE.form_data = self._get_form_data()
|
||||
|
||||
def validate(self) -> None: ...
|
||||
def validate(self) -> list[str]:
|
||||
return validate_form_data(self.widget_registry)
|
||||
|
||||
def load_state(
|
||||
self,
|
||||
new_state: AutoForm_State,
|
||||
) -> None:
|
||||
load_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:
|
||||
self.__sync_GUI_to_state()
|
||||
self._sync_GUI_to_state()
|
||||
|
||||
return self.STATE
|
||||
|
||||
def save_data(self) -> None:
|
||||
self._disable_save()
|
||||
errors = self._validate_form_data()
|
||||
errors = self.validate()
|
||||
|
||||
if errors:
|
||||
# errors: abort saving and show pop up window
|
||||
@@ -1696,7 +1741,7 @@ class AutoForm(QWidget):
|
||||
|
||||
logger_auto_form.info("Data saved successfully")
|
||||
self.update_triggered.emit()
|
||||
self._activation_delete()
|
||||
self._activate_delete()
|
||||
finally:
|
||||
# always re-enable save, even if error occurred
|
||||
self._enable_save()
|
||||
@@ -1740,7 +1785,7 @@ class FlexibleDateInput(QWidget):
|
||||
self.button.clicked.connect(self.show_calendar)
|
||||
self.calendar.activated.connect(self.set_date)
|
||||
# signal to validate
|
||||
self.line_edit.editingFinished.connect(self.validate_input)
|
||||
self.line_edit.editingFinished.connect(self._validate_input)
|
||||
self.line_edit.installEventFilter(self)
|
||||
|
||||
self.setReadOnly(False)
|
||||
@@ -1754,10 +1799,10 @@ class FlexibleDateInput(QWidget):
|
||||
if watched == self.line_edit:
|
||||
if event.type() == QEvent.Type.KeyPress:
|
||||
if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): # type: ignore
|
||||
self.validate_input()
|
||||
self._validate_input()
|
||||
return True # mark event as processed
|
||||
elif event.type() == QEvent.Type.FocusOut:
|
||||
self.validate_input()
|
||||
self._validate_input()
|
||||
# no return to not block focus change
|
||||
return super().eventFilter(watched, event)
|
||||
|
||||
@@ -1798,7 +1843,7 @@ class FlexibleDateInput(QWidget):
|
||||
self.line_edit.setInputMask("99.99.9999;_")
|
||||
self.line_edit.setPlaceholderText("TT.MM.JJJJ")
|
||||
|
||||
def validate_input(self) -> None:
|
||||
def _validate_input(self) -> None:
|
||||
logger_gui.debug("[Widget -- FlexibleDateInput] Trigger input validation")
|
||||
if self._read_only:
|
||||
return
|
||||
@@ -1824,6 +1869,16 @@ class FlexibleDateInput(QWidget):
|
||||
else:
|
||||
self.line_edit.setStyleSheet("")
|
||||
|
||||
def validate(self) -> bool:
|
||||
logger_custom_fields.debug("[CustomField - FlexDate]: Call validate...")
|
||||
self._validate_input()
|
||||
date = self.get_date()
|
||||
if date and date.isValid():
|
||||
logger_custom_fields.debug("[CustomField - FlexDate]: returning valid")
|
||||
return True
|
||||
logger_custom_fields.debug("[CustomField - FlexDate]: returning invalid")
|
||||
return False
|
||||
|
||||
def set_date(
|
||||
self,
|
||||
date: QDate,
|
||||
@@ -1905,7 +1960,7 @@ class FlexibleDateTimeInput(QWidget):
|
||||
self.calendar.activated.connect(self.set_date_from_calendar)
|
||||
|
||||
# signal to validate
|
||||
self.line_edit.editingFinished.connect(self.validate_input)
|
||||
self.line_edit.editingFinished.connect(self._validate_input)
|
||||
self.line_edit.installEventFilter(self)
|
||||
|
||||
self.setReadOnly(False)
|
||||
@@ -1919,10 +1974,10 @@ class FlexibleDateTimeInput(QWidget):
|
||||
if watched == self.line_edit:
|
||||
if event.type() == QEvent.Type.KeyPress:
|
||||
if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): # type: ignore
|
||||
self.validate_input()
|
||||
self._validate_input()
|
||||
return True # mark event as processed
|
||||
elif event.type() == QEvent.Type.FocusOut:
|
||||
self.validate_input()
|
||||
self._validate_input()
|
||||
# no return to not block focus change
|
||||
return super().eventFilter(watched, event)
|
||||
|
||||
@@ -1963,7 +2018,7 @@ class FlexibleDateTimeInput(QWidget):
|
||||
self.line_edit.setInputMask("99.99.9999 99:99;_")
|
||||
self.line_edit.setPlaceholderText("TT.MM.JJJJ hh:mm")
|
||||
|
||||
def validate_input(self) -> None:
|
||||
def _validate_input(self) -> None:
|
||||
logger_gui.debug("[Widget -- FlexibleDateTimeInput] Trigger input validation")
|
||||
if self._read_only:
|
||||
return
|
||||
@@ -1990,6 +2045,16 @@ class FlexibleDateTimeInput(QWidget):
|
||||
else:
|
||||
self.line_edit.setStyleSheet("")
|
||||
|
||||
def validate(self) -> bool:
|
||||
logger_custom_fields.debug("[CustomField - FlexDatetime]: Call validate...")
|
||||
self._validate_input()
|
||||
datetime = self.get_datetime()
|
||||
if datetime and datetime.isValid():
|
||||
logger_custom_fields.debug("[CustomField - FlexDatetime]: returning valid")
|
||||
return True
|
||||
logger_custom_fields.debug("[CustomField - FlexDatetime]: returning invalid")
|
||||
return False
|
||||
|
||||
def set_date_from_calendar(
|
||||
self,
|
||||
date: QDate,
|
||||
@@ -2692,10 +2757,10 @@ class Page_NewInitRec(QWidget):
|
||||
logger_gui.debug("[Page -- InitRec Company] State to call: %s", req_state)
|
||||
self.person_requested.emit(req_state)
|
||||
|
||||
def __sync_state_to_GUI(self) -> None:
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
pass
|
||||
|
||||
def __sync_GUI_to_state(self) -> None:
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
pass
|
||||
|
||||
def validate(self) -> None:
|
||||
@@ -2705,10 +2770,10 @@ class Page_NewInitRec(QWidget):
|
||||
self,
|
||||
new_state: Page_NewInitRec_State,
|
||||
) -> None:
|
||||
self.__sync_state_to_GUI()
|
||||
self._sync_state_to_GUI()
|
||||
|
||||
def get_state(self) -> Page_NewInitRec_State:
|
||||
self.__sync_GUI_to_state()
|
||||
self._sync_GUI_to_state()
|
||||
return self.STATE
|
||||
|
||||
def save_data(self) -> None:
|
||||
@@ -2900,10 +2965,10 @@ class Page_InitRecCompany(QWidget):
|
||||
logger_gui.debug("[Page -- InitRec Company] State to call: %s", req_state)
|
||||
self.company_profile_requested.emit(req_state)
|
||||
|
||||
def reset_form(self) -> None:
|
||||
self.auto_form._reset_form()
|
||||
def reset(self) -> None:
|
||||
self.auto_form.reset_form()
|
||||
|
||||
def __sync_state_to_GUI(self) -> None:
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
auto_form_state = AutoForm_State(
|
||||
session=self.STATE.session,
|
||||
cfg=self.AUTO_FORM_CFG,
|
||||
@@ -2912,26 +2977,22 @@ class Page_InitRecCompany(QWidget):
|
||||
)
|
||||
self.auto_form.load_state(auto_form_state)
|
||||
|
||||
def __sync_GUI_to_state(self) -> None:
|
||||
# TODO behaviour not consistent
|
||||
self.auto_form.__sync_GUI_to_state()
|
||||
|
||||
def validate(self) -> None: ...
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
auto_form_state = self.auto_form.get_state()
|
||||
self.STATE.un_id = auto_form_state.rec_id
|
||||
|
||||
def load_state(
|
||||
self,
|
||||
new_state: Page_InitRecCompany_State,
|
||||
) -> None:
|
||||
self.STATE.un_id = new_state.un_id
|
||||
set_page_state(self.STATE, new_state)
|
||||
|
||||
self.__sync_state_to_GUI()
|
||||
self._sync_state_to_GUI()
|
||||
|
||||
def get_state(self) -> Page_InitRecCompany_State:
|
||||
self.__sync_GUI_to_state()
|
||||
return self.STATE
|
||||
self._sync_GUI_to_state()
|
||||
|
||||
def save_data(self) -> None:
|
||||
self.auto_form.save_data()
|
||||
return self.STATE
|
||||
|
||||
|
||||
class Page_InitRecPerson(QWidget):
|
||||
@@ -3067,9 +3128,9 @@ class Page_InitRecPerson(QWidget):
|
||||
self.selection_county.update()
|
||||
|
||||
def reset_form(self) -> None:
|
||||
self.auto_form._reset_form()
|
||||
self.auto_form.reset_form()
|
||||
|
||||
def __sync_state_to_GUI(self) -> None:
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
auto_form_state = AutoForm_State(
|
||||
session=self.STATE.session,
|
||||
cfg=self.AUTO_FORM_CFG,
|
||||
@@ -3078,25 +3139,21 @@ class Page_InitRecPerson(QWidget):
|
||||
)
|
||||
self.auto_form.load_state(auto_form_state)
|
||||
|
||||
def __sync_GUI_to_state(self) -> None:
|
||||
self.auto_form.__sync_GUI_to_state()
|
||||
|
||||
def validate(self) -> None: ...
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
auto_form_state = self.auto_form.get_state()
|
||||
self.STATE.pers_id = auto_form_state.rec_id
|
||||
|
||||
def load_state(
|
||||
self,
|
||||
new_state: Page_InitRecPerson_State,
|
||||
) -> None:
|
||||
self.STATE.pers_id = new_state.pers_id
|
||||
|
||||
self.__sync_state_to_GUI()
|
||||
set_page_state(self.STATE, new_state)
|
||||
self._sync_state_to_GUI()
|
||||
|
||||
def get_state(self) -> Page_InitRecPerson_State:
|
||||
self.__sync_GUI_to_state()
|
||||
return self.STATE
|
||||
self._sync_GUI_to_state()
|
||||
|
||||
def save_data(self) -> None:
|
||||
self.auto_form.save_data()
|
||||
return self.STATE
|
||||
|
||||
|
||||
class Page_CompanyProfile(QWidget):
|
||||
@@ -3352,7 +3409,7 @@ class Page_CompanyProfile(QWidget):
|
||||
self.add_btn_pauschal.setEnabled(True)
|
||||
self.add_btn_individual.setEnabled(True)
|
||||
|
||||
def __sync_state_to_GUI(self) -> None:
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
if self.STATE.rec_id is None:
|
||||
# just clear and do nothing else
|
||||
logger_gui.debug("just reset, ID is None")
|
||||
@@ -3376,7 +3433,7 @@ class Page_CompanyProfile(QWidget):
|
||||
self.table_master_data.load_data(data)
|
||||
self.set_company_name(data["Name:"])
|
||||
|
||||
def __sync_GUI_to_state(self) -> None: ...
|
||||
def _sync_GUI_to_state(self) -> None: ...
|
||||
|
||||
def validate(self) -> None: ...
|
||||
|
||||
@@ -3387,7 +3444,7 @@ class Page_CompanyProfile(QWidget):
|
||||
logger_gui.debug("Called function to load with ID: %s", new_state.rec_id)
|
||||
self.STATE.rec_id = new_state.rec_id
|
||||
|
||||
self.__sync_state_to_GUI()
|
||||
self._sync_state_to_GUI()
|
||||
|
||||
def get_state(self) -> Any: ...
|
||||
|
||||
@@ -3715,9 +3772,14 @@ class Page_Consulting(QWidget):
|
||||
# layout_change_title.addWidget(change_title_btn)
|
||||
# change_title_btn.clicked.connect(self.set_company_name)
|
||||
# container_layout.addLayout(layout_change_title)
|
||||
btn_get_data_table = QPushButton("Call 'get_data' from table")
|
||||
btn_get_data_table.clicked.connect(self._debug_get_data_table)
|
||||
btn_get_data_table = QPushButton("Debug call 'get_state' from table and page")
|
||||
btn_get_data_table.clicked.connect(self._debug_get_state)
|
||||
container_layout.addWidget(btn_get_data_table)
|
||||
btn_validate_data_table = QPushButton(
|
||||
"Debuig call 'validate' from table and page"
|
||||
)
|
||||
btn_validate_data_table.clicked.connect(self._debug_validate)
|
||||
container_layout.addWidget(btn_validate_data_table)
|
||||
|
||||
separator2 = QFrame()
|
||||
separator2.setFrameShape(QFrame.Shape.HLine)
|
||||
@@ -3747,24 +3809,6 @@ class Page_Consulting(QWidget):
|
||||
self.table_sessions = Page_Consulting_Table(site_state=self.STATE, state=table_state)
|
||||
self.STATE.child_modules.append(self.table_sessions)
|
||||
container_layout.addWidget(self.table_sessions)
|
||||
# container_layout.addSpacing(50)
|
||||
|
||||
# self.layout_sessions = QVBoxLayout()
|
||||
# self.layout_sessions.setContentsMargins(0, 0, 0, 0)
|
||||
# container_layout.addLayout(self.layout_sessions)
|
||||
# self.btn_add_cons_session = QPushButton("+ Hinzufügen")
|
||||
# self.btn_add_cons_session.setStyleSheet(
|
||||
# "color: #0369a1; font-weight: bold; border: 1px dashed #0369a1; padding: 5px;"
|
||||
# )
|
||||
# self.btn_add_cons_session.clicked.connect(self.add_cons_entry)
|
||||
# self.layout_sessions.addWidget(self.btn_add_cons_session)
|
||||
# self.layout_sessions.addStretch(1)
|
||||
|
||||
# self.cons_entries: list[QWidget] = []
|
||||
|
||||
# self.layout_sessions.addStretch()
|
||||
|
||||
# container_layout.addStretch()
|
||||
|
||||
# --- STYLE ---
|
||||
self.setStyleSheet("""
|
||||
@@ -3798,70 +3842,6 @@ class Page_Consulting(QWidget):
|
||||
# }
|
||||
# """)
|
||||
|
||||
# def add_cons_entry(self) -> None:
|
||||
# logger_gui.debug("[Consulting Page] Adding entry...")
|
||||
|
||||
# row_widget = QWidget()
|
||||
# entry_row = QHBoxLayout(row_widget)
|
||||
# entry_fields = QHBoxLayout()
|
||||
# entry_row.addLayout(entry_fields)
|
||||
|
||||
# txt1 = QLineEdit()
|
||||
# txt2 = QPlainTextEdit()
|
||||
# txt2.setMaximumHeight(100)
|
||||
# entry_fields.addWidget(txt1)
|
||||
# entry_fields.addWidget(txt2)
|
||||
|
||||
# del_btn = QPushButton("🗑️")
|
||||
# del_btn.setFixedSize(30, 30)
|
||||
# # Lambda with default parameter to delete exactly this(!) box
|
||||
# del_btn.clicked.connect(
|
||||
# lambda checked=False, widget=row_widget: self.remove_cons_entry(widget)
|
||||
# )
|
||||
# entry_row.addWidget(del_btn)
|
||||
|
||||
# row_widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||
# # row_widget.setMaximumHeight(200)
|
||||
|
||||
# self.cons_entries.append(row_widget)
|
||||
# self.layout_sessions.insertWidget(self.layout_sessions.count() - 2, row_widget)
|
||||
|
||||
# def remove_cons_entry(
|
||||
# self,
|
||||
# row_widget: QWidget,
|
||||
# ) -> None:
|
||||
# logger_gui.debug("[Consulting Page]: Delete clicked...")
|
||||
# self.layout_sessions.removeWidget(row_widget)
|
||||
# row_widget.deleteLater()
|
||||
# self.cons_entries.remove(row_widget)
|
||||
|
||||
def _debug_get_data_table(self) -> None:
|
||||
self.table_sessions.get_state()
|
||||
|
||||
# TODO placeholder, check if needed
|
||||
def __sync_state_to_GUI(self) -> None:
|
||||
match self.STATE.cons_type:
|
||||
case ConsultingType.PAUSCHAL:
|
||||
self.type_pauschal_btn.setChecked(True)
|
||||
case ConsultingType.INDIVIUAL:
|
||||
self.type_individual_btn.setChecked(True)
|
||||
|
||||
# deactivate if at least one ID is set (type is pre-defined by how
|
||||
# the site is called)
|
||||
if self.STATE.un_id or self.STATE.pers_id:
|
||||
self.type_pauschal_btn.setEnabled(False)
|
||||
self.type_individual_btn.setEnabled(False)
|
||||
|
||||
# call to child modules not needed because these are initialised by their own
|
||||
# "load_state" method which again calls the modules sync method
|
||||
|
||||
def __sync_GUI_to_state(self) -> None:
|
||||
# call to child modules not needed because their syncing method is called when
|
||||
# the "get_state" method is called --> this has to be managed within this method
|
||||
|
||||
table_state = self.table_sessions.get_state()
|
||||
self.STATE.cons_sessions = table_state.row_states
|
||||
|
||||
def type_consulting_changed(
|
||||
self,
|
||||
button_id: int,
|
||||
@@ -3879,21 +3859,68 @@ class Page_Consulting(QWidget):
|
||||
logger_page_consulting.debug("[Consulting Page] Selected cons_type: %s", cons_type)
|
||||
self.STATE.cons_type = cons_type
|
||||
|
||||
def load_from_state(
|
||||
def _debug_get_state(self) -> None:
|
||||
logger_page_consulting.debug("[Consulting Page] Call get state on table...")
|
||||
_ = self.table_sessions.get_state()
|
||||
logger_page_consulting.debug("[Consulting Page] Call get state on page...")
|
||||
state = self.get_state()
|
||||
logger_page_consulting.debug("[Consulting Page] Page state:\n%s", state)
|
||||
|
||||
def _debug_validate(self) -> None:
|
||||
logger_page_consulting.debug("[Consulting Page] Call validate data on table...")
|
||||
errors = self.table_sessions.validate()
|
||||
logger_page_consulting.debug("[Consulting Page] Table errors:\n%s", pformat(errors))
|
||||
logger_page_consulting.debug("[Consulting Page] Call get state on page...")
|
||||
errors = self.validate()
|
||||
logger_page_consulting.debug("[Consulting Page] Page errors:\n%s", pformat(errors))
|
||||
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
match self.STATE.cons_type:
|
||||
case ConsultingType.PAUSCHAL:
|
||||
self.type_pauschal_btn.setChecked(True)
|
||||
case ConsultingType.INDIVIUAL:
|
||||
self.type_individual_btn.setChecked(True)
|
||||
|
||||
# deactivate if at least one ID is set (type is pre-defined by how
|
||||
# the site is called)
|
||||
if self.STATE.un_id or self.STATE.pers_id:
|
||||
self.type_pauschal_btn.setEnabled(False)
|
||||
self.type_individual_btn.setEnabled(False)
|
||||
|
||||
# call to child modules' syncing method not needed because these are initialised by
|
||||
# their own "load_state" method which again calls the modules sync method
|
||||
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
# call to child modules' syncing method not needed because their syncing method is
|
||||
# called when the "get_state" method is called --> this has to be managed within
|
||||
# this method
|
||||
table_state = self.table_sessions.get_state()
|
||||
self.STATE.cons_sessions = table_state.row_states
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
errors: list[str] = []
|
||||
errors.extend(self.table_sessions.validate())
|
||||
|
||||
return errors
|
||||
|
||||
def load_state(
|
||||
self,
|
||||
new_state: Page_Consulting_State,
|
||||
) -> 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)
|
||||
load_state(self.STATE, new_state)
|
||||
# self.STATE.un_id = new_state.un_id
|
||||
# self.STATE.pers_id = new_state.pers_id
|
||||
# self.STATE.cons_type = new_state.cons_type
|
||||
set_page_state(self.STATE, new_state)
|
||||
# child modules
|
||||
table_state = Page_Consulting_Table_State(row_states=self.STATE.cons_sessions)
|
||||
self.table_sessions.load_state(table_state)
|
||||
|
||||
self.__sync_state_to_GUI()
|
||||
self._sync_state_to_GUI()
|
||||
|
||||
def get_state(self) -> Page_Consulting_State:
|
||||
self._sync_GUI_to_state()
|
||||
|
||||
return self.STATE
|
||||
|
||||
|
||||
class Page_Consulting_ConsultingSession(QWidget):
|
||||
@@ -3918,40 +3945,91 @@ class Page_Consulting_ConsultingSession(QWidget):
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignVCenter)
|
||||
ROW_HEIGHT = 70
|
||||
|
||||
# ?? widget registry?
|
||||
self.widget_registry: WidgetRegistry = {}
|
||||
|
||||
user_field_def = FormField[QLineEdit](
|
||||
"",
|
||||
"Nutzername",
|
||||
FormFieldType.TEXT,
|
||||
readonly=True,
|
||||
placeholder="wird über aktiven Nutzer bei Anlage gesetzt",
|
||||
enable_uuid_key=True,
|
||||
)
|
||||
_, self.user_field = build_and_add_widget(user_field_def, layout)
|
||||
|
||||
# ?? date or datetime?
|
||||
timestamp_def = FormField[FlexibleDateTimeInput]("", FormFieldType.DATETIME_FLEXIBLE)
|
||||
_, self.timestamp = build_and_add_widget(timestamp_def, layout)
|
||||
timestamp_def = FormField[FlexibleDateTimeInput](
|
||||
"Zeitstempel",
|
||||
FormFieldType.DATETIME_FLEXIBLE,
|
||||
required=True,
|
||||
enable_uuid_key=True,
|
||||
)
|
||||
_, self.timestamp = build_and_add_widget(
|
||||
timestamp_def,
|
||||
layout,
|
||||
widget_registry=self.widget_registry,
|
||||
)
|
||||
self.timestamp.set_pydatetime(datetime.datetime.now())
|
||||
|
||||
contact_def = FormField[QLineEdit]("", FormFieldType.TEXT, placeholder="Freitext")
|
||||
_, self.contact = build_and_add_widget(contact_def, layout)
|
||||
contact_def = FormField[QLineEdit](
|
||||
"Kontaktperson",
|
||||
FormFieldType.TEXT,
|
||||
placeholder="Freitext",
|
||||
required=True,
|
||||
enable_uuid_key=True,
|
||||
)
|
||||
_, self.contact = build_and_add_widget(
|
||||
contact_def,
|
||||
layout,
|
||||
widget_registry=self.widget_registry,
|
||||
)
|
||||
|
||||
contact_type_def = FormField[QComboBox](
|
||||
"",
|
||||
"Kontakttyp",
|
||||
FormFieldType.DROPDOWN,
|
||||
placeholder="sollte Dropdown sein",
|
||||
options=[("Persönlich", None), ("Telefon", None), ("Mail", None)],
|
||||
required=True,
|
||||
enable_uuid_key=True,
|
||||
)
|
||||
_, self.contact_type = build_and_add_widget(
|
||||
contact_type_def,
|
||||
layout,
|
||||
widget_registry=self.widget_registry,
|
||||
)
|
||||
_, self.contact_type = build_and_add_widget(contact_type_def, layout)
|
||||
|
||||
crm_link_def = FormField[QLineEdit]("", FormFieldType.TEXT, placeholder="CRM Links")
|
||||
_, self.crm_link = build_and_add_widget(crm_link_def, layout)
|
||||
crm_link_def = FormField[QLineEdit](
|
||||
"CRM-Verknüpfung",
|
||||
FormFieldType.TEXT,
|
||||
placeholder="CRM Links",
|
||||
required=True,
|
||||
enable_uuid_key=True,
|
||||
)
|
||||
_, self.crm_link = build_and_add_widget(
|
||||
crm_link_def,
|
||||
layout,
|
||||
widget_registry=self.widget_registry,
|
||||
)
|
||||
|
||||
plain_text_def = FormField[QPlainTextEdit]("", FormFieldType.LONGTEXT)
|
||||
_, self.plain_text = build_and_add_widget(plain_text_def, layout)
|
||||
plain_text_def = FormField[QPlainTextEdit](
|
||||
"Anmerkungen",
|
||||
FormFieldType.LONGTEXT,
|
||||
enable_uuid_key=True,
|
||||
)
|
||||
_, self.plain_text = build_and_add_widget(
|
||||
plain_text_def,
|
||||
layout,
|
||||
widget_registry=self.widget_registry,
|
||||
)
|
||||
|
||||
response_def = FormField[QPlainTextEdit]("", FormFieldType.LONGTEXT)
|
||||
_, self.response = build_and_add_widget(response_def, layout)
|
||||
response_def = FormField[QPlainTextEdit](
|
||||
"Rückmeldung",
|
||||
FormFieldType.LONGTEXT,
|
||||
enable_uuid_key=True,
|
||||
)
|
||||
_, self.response = build_and_add_widget(
|
||||
response_def,
|
||||
layout,
|
||||
widget_registry=self.widget_registry,
|
||||
)
|
||||
|
||||
self.btn_delete = QPushButton("✕")
|
||||
self.btn_delete.setFlat(True) # Entfernt den blockigen Rahmen
|
||||
@@ -3991,9 +4069,9 @@ class Page_Consulting_ConsultingSession(QWidget):
|
||||
layout.setStretch(i, stretch)
|
||||
|
||||
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
||||
self.__sync_state_to_GUI() # ?? only needed if entry is from database?
|
||||
self._sync_state_to_GUI() # ?? only needed if entry is from database?
|
||||
|
||||
def __sync_state_to_GUI(self) -> None:
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
logger_page_consulting.debug(
|
||||
"[Page Consulting -- Consulting Session]: State = %s", self.STATE
|
||||
)
|
||||
@@ -4005,7 +4083,7 @@ class Page_Consulting_ConsultingSession(QWidget):
|
||||
set_widget_value(self.plain_text, self.STATE.anmerkungen)
|
||||
set_widget_value(self.response, self.STATE.rueckmeldung)
|
||||
|
||||
def __sync_GUI_to_state(self) -> None:
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
self.STATE.zeitstempel = self.timestamp.get_pydatetime()
|
||||
self.STATE.ansprechpartner = get_widget_value(self.contact)
|
||||
self.STATE.interaction_scheme = get_widget_value(self.contact_type)
|
||||
@@ -4014,12 +4092,16 @@ class Page_Consulting_ConsultingSession(QWidget):
|
||||
self.STATE.rueckmeldung = get_widget_value(self.response)
|
||||
|
||||
def get_state(self) -> Page_Consulting_ConsultingSession_State:
|
||||
self.__sync_GUI_to_state()
|
||||
self._sync_GUI_to_state()
|
||||
|
||||
return self.STATE
|
||||
|
||||
def validate(self) -> None:
|
||||
pass
|
||||
def validate(self) -> list[str]:
|
||||
logger_page_consulting.debug(
|
||||
">>>>> Call validate on Session. Widget registry:\n%s",
|
||||
pprint_registry(self.widget_registry),
|
||||
)
|
||||
return validate_form_data(self.widget_registry)
|
||||
|
||||
def save_data(self) -> None:
|
||||
pass
|
||||
@@ -4028,13 +4110,13 @@ class Page_Consulting_ConsultingSession(QWidget):
|
||||
self,
|
||||
new_state: Page_Consulting_ConsultingSession_State,
|
||||
) -> None:
|
||||
load_state(self.STATE, new_state)
|
||||
self.__sync_state_to_GUI()
|
||||
set_page_state(self.STATE, new_state)
|
||||
self._sync_state_to_GUI()
|
||||
|
||||
|
||||
def load_state(
|
||||
target: T,
|
||||
source: T,
|
||||
def set_page_state(
|
||||
target: S,
|
||||
source: S,
|
||||
deep: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -4047,6 +4129,9 @@ def load_state(
|
||||
raise TypeError("Both source and target must have the same type")
|
||||
|
||||
for field in dc.fields(source):
|
||||
if field.name == "child_modules":
|
||||
continue
|
||||
|
||||
new_value = getattr(source, field.name)
|
||||
|
||||
if deep:
|
||||
@@ -4248,12 +4333,12 @@ class Page_Consulting_Table(QWidget):
|
||||
while self.STATE.child_modules:
|
||||
self.remove_row(self.STATE.child_modules[0])
|
||||
|
||||
def __sync_state_to_GUI(self) -> None:
|
||||
def _sync_state_to_GUI(self) -> None:
|
||||
self.reset()
|
||||
for row_state in self.STATE.row_states:
|
||||
self.add_row(row_state)
|
||||
|
||||
def __sync_GUI_to_state(self) -> None:
|
||||
def _sync_GUI_to_state(self) -> None:
|
||||
logger_page_consulting.debug("[Page -- Consulting] Table: Call syncing GUI to state")
|
||||
row_data: list[Page_Consulting_ConsultingSession_State] = []
|
||||
|
||||
@@ -4281,7 +4366,7 @@ class Page_Consulting_Table(QWidget):
|
||||
|
||||
def get_state(self) -> Page_Consulting_Table_State:
|
||||
logger_page_consulting.debug("[Page Consulting -- Table] Syncing GUI to state...")
|
||||
self.__sync_GUI_to_state()
|
||||
self._sync_GUI_to_state()
|
||||
|
||||
logger_page_consulting.debug("[Page Consulting -- Table]: State = %s", self.STATE)
|
||||
|
||||
@@ -4294,12 +4379,16 @@ class Page_Consulting_Table(QWidget):
|
||||
logger_page_consulting.debug(
|
||||
"[Page -- Consulting] Table: Call loading from state, %s", new_state
|
||||
)
|
||||
self.STATE.row_states = new_state.row_states
|
||||
set_page_state(self.STATE, new_state)
|
||||
|
||||
self.__sync_state_to_GUI()
|
||||
self._sync_state_to_GUI()
|
||||
|
||||
def validate(self) -> None:
|
||||
pass
|
||||
def validate(self) -> list[str]:
|
||||
errors: list[str] = []
|
||||
for m in self.STATE.child_modules:
|
||||
errors.extend(m.validate())
|
||||
|
||||
return errors
|
||||
|
||||
def save_data(self) -> None:
|
||||
pass
|
||||
@@ -4578,7 +4667,7 @@ class MainWindow(QMainWindow):
|
||||
self,
|
||||
new_state: Page_Consulting_State,
|
||||
) -> None:
|
||||
self.consulting_page.load_from_state(new_state)
|
||||
self.consulting_page.load_state(new_state)
|
||||
self._navigate_to_target(self.consulting_page)
|
||||
|
||||
# --- MENÜ LOGIK ---
|
||||
@@ -4632,6 +4721,15 @@ def qt_message_handler(mode, context, message):
|
||||
logger_gui.critical(message)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# TODO add all relevant modules
|
||||
# enable static type checking for the protocol
|
||||
_m1: type[Module] = Page_Consulting
|
||||
_m2: type[Module] = Page_Consulting_Table
|
||||
_m3: type[Module] = Page_Consulting_ConsultingSession
|
||||
_w1: type[WrapperModule] = Page_InitRecCompany
|
||||
_w2: type[WrapperModule] = Page_InitRecPerson
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.excepthook = global_exception_handler
|
||||
qInstallMessageHandler(qt_message_handler)
|
||||
|
||||
@@ -37,6 +37,9 @@ logger_get_data.setLevel(logging.DEBUG)
|
||||
logger_auto_form = logger_gui.getChild("get_data_auto_form")
|
||||
logger_auto_form.setLevel(logging.DEBUG)
|
||||
|
||||
logger_custom_fields = logger_gui.getChild("custom_fields")
|
||||
logger_custom_fields.setLevel(logging.DEBUG)
|
||||
|
||||
logger_page_consulting = logger_gui.getChild("page_consulting")
|
||||
logger_page_consulting.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user