prepare backend interaction scheme

This commit is contained in:
2026-07-09 16:20:01 +02:00
parent 39d4327e19
commit 14357df8cd
4 changed files with 242 additions and 194 deletions
+49 -1
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import datetime
from pprint import pformat
from typing import TYPE_CHECKING, Any, cast
import polars as pl
@@ -8,11 +9,12 @@ import sqlalchemy as sql
from wce_crm import db
from wce_crm.constants import TIMEZONE_CEST
from wce_crm.data_models import Beratungsgespraech_Vorgang
from wce_crm.logging import logger_back as logger
from wce_crm.types import CompanyInfo, ContactPersonInfo, InitRecType, MainPageEntry
if TYPE_CHECKING:
from wce_crm.types import ExtAnId, ExtMaId, RecId
from wce_crm.types import ConsId, ExtAnId, ExtMaId, RecId
def _transform_for_gui_output(
@@ -226,6 +228,52 @@ def initrec_person_delete_initial_recording(
raise KeyError(f"Database ID {id_} not found for deletion")
def page_consulting_to_db(
data: Beratungsgespraech_Vorgang,
) -> None:
logger.debug("[Consulting Page] Call Database Routine...")
fk_vorgang_id: int
if data.vorgang_id is None:
# insert new "Vorgang"
insert_data = data.model_dump(exclude={"vorgang_id", "beratungen"})
logger.debug("[Consulting Page] Call insert with data:\n%s", pformat(insert_data))
fk_vorgang_id = 42 # TODO replace for real inserted PK
else:
fk_vorgang_id = data.vorgang_id
logger.debug("[Consulting Page] VorgangID already set. ID: %d", fk_vorgang_id)
rows_for_db_insert: list[dict[str, Any]] = []
rows_for_db_update: list[dict[str, Any]] = []
for cons_session in data.beratungen:
cons_session.vorgang_id = fk_vorgang_id
if cons_session.beratung_id is None:
row_data = cons_session.model_dump(exclude={"beratung_id"})
rows_for_db_insert.append(row_data)
else:
row_data = cons_session.model_dump()
rows_for_db_update.append(row_data)
if rows_for_db_update:
# ... update
logger.debug(
"[Consulting Page] Call update for sessions:\n%s", pformat(rows_for_db_update)
)
if rows_for_db_insert:
# ... insert
logger.debug(
"[Consulting Page] Call insert for sessions:\n%s", pformat(rows_for_db_insert)
)
def page_consulting_from_db(
cons_id: ConsId,
) -> Beratungsgespraech_Vorgang:
# TODO add Routine
# get "Vorgang" and all associated sessions to instantiate Pydantic data model
...
# // main page interaction
def _main_page_get_company_list() -> list[MainPageEntry]:
logger.debug("[Call backend] get_company_list")
+9 -5
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import datetime
import json
from typing import Annotated, Any, Final
from typing import Annotated, Any, Final, Literal
from pydantic import (
AwareDatetime,
@@ -346,23 +346,27 @@ class Grunderfassung_Sprachen(BaseModel):
# ** Consulting
class Beratungsgespraech(BaseModel):
class Beratungsgespraech_Vorgang(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
vorgang_id: int | None
un_id: int | None
pers_id: int | None
titel: str
beratungs_typ: str
beratungs_typ: Literal["pauschal", "individuell"]
# default in SQLAlchemy with lambda and timezone-aware datetime
erstellt: AwareDatetime | None = None
aktualisiert: AwareDatetime | None = None # see above
beratungen: list[Beratungsgespraech_Einzelgespraech]
class Beratungsgespraech_Vorgang(BaseModel):
class Beratungsgespraech_Einzelgespraech(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
vorgang_id: int
vorgang_id: int | None
beratung_id: int | None
nutzer_id: int
nutzer_name: str
zeitstempel: AwareDatetime
+183 -188
View File
@@ -6,6 +6,7 @@ import datetime
import enum
import re
import sys
import time
import traceback
from collections import defaultdict
from collections.abc import Container, Iterable, Sequence
@@ -77,6 +78,8 @@ from wce_crm.backend import backend
from wce_crm.constants import TIMEZONE_CEST
from wce_crm.data_models import (
COLUMN_SEP,
Beratungsgespraech_Einzelgespraech,
Beratungsgespraech_Vorgang,
FlatBaseModel,
Grunderfassung_Personen,
Grunderfassung_Unternehmen,
@@ -95,10 +98,9 @@ from wce_crm.logging import (
logger_gui,
logger_page_consulting,
)
from wce_crm.types import ExtMaId, RecId, UserId
if TYPE_CHECKING:
...
from wce_crm.types import ConsId, ExtMaId, RecId, UserId
K = TypeVar("K")
V = TypeVar("V")
@@ -343,15 +345,17 @@ class Page_InitRecPerson_State(PageState[Module]):
class ConsultingType(enum.StrEnum):
PAUSCHAL = enum.auto()
INDIVIUAL = enum.auto()
INDIVIDUAL = enum.auto()
@dc.dataclass(slots=True, kw_only=True)
class Page_Consulting_State(PageState[Module]):
session: Session
vorgang_id: ConsId | None
un_id: RecId | None = None
pers_id: RecId | None = None
cons_type: ConsultingType | None = None
titel: str = "TEST-TITEL" # add title later to GUI or build it in the background
beratungs_typ: ConsultingType
cons_sessions: list[Page_Consulting_ConsultingSession_State] = dc.field(
default_factory=list
)
@@ -359,15 +363,16 @@ class Page_Consulting_State(PageState[Module]):
@dc.dataclass(slots=True, kw_only=True)
class Page_Consulting_ConsultingSession_State(PageState[Module]):
session: Session
nutzer_id: UserId # must be set with the session (always known)
nutzer_name: str # must be set with the session (always known)
beratung_id: int | None = None # known and not known entries
beratung_id: int | None # known and not known entries
zeitstempel: datetime.datetime = dc.field(
default_factory=lambda: datetime.datetime.now(tz=datetime.UTC)
)
ansprechpartner: str | None = None
interaction_scheme: str | None = None
topic: str | None = (
kommunikationsweg: str | None = None
thema_crm_matrix: str | None = (
None # TODO should be linked to something, target unclear (need specification by customer)
)
anmerkungen: str | None = None
@@ -376,6 +381,7 @@ class Page_Consulting_ConsultingSession_State(PageState[Module]):
@dc.dataclass(slots=True, kw_only=True)
class Page_Consulting_Table_State(PageState["Page_Consulting_ConsultingSession"]):
session: Session
row_states: list[Page_Consulting_ConsultingSession_State] = dc.field(default_factory=list)
@@ -2829,53 +2835,6 @@ class Page_InitRecCompany(QWidget):
self.search_widget_trigger = cast(
QLineEdit, search_widget.company_widgets["ma_unternehmensname"]
)
# TODO check how this should be done with new architecture
# text_widget_set = search_widgets_by_key(
# self.auto_form.widget_registry, f"Kontaktperson{COLUMN_SEP}KP_name_partner"
# )
# assert len(text_widget_set) == 1
# self.text_widget_set = cast(QLineEdit, text_widget_set[0]["widget"])
# self.search_widget_trigger.textChanged.connect(
# self._custom_set_company_name_contact_person
# )
# ** 'Bundesland' only if 'Inland' selected in 'Stammdaten'
# TODO check removal
# person_location = search_widgets_by_key(
# self.auto_form.widget_registry, f"Stammdaten{COLUMN_SEP}aufenthaltsort"
# )
# assert len(person_location) == 1
# self.person_location = cast(QComboBox, person_location[0]["widget"])
# assert isinstance(self.person_location, QComboBox)
# self.person_location.currentIndexChanged.connect(self._custom_county_selection)
# selection_county = search_widgets_by_key(
# self.auto_form.widget_registry, f"Stammdaten{COLUMN_SEP}bundesland"
# )
# assert len(selection_county) == 1
# self.selection_county = cast(QComboBox, selection_county[0]["widget"])
# assert isinstance(self.selection_county, QComboBox)
# self.selection_county.setProperty("styleClass", "stempel")
# self.selection_county.setEnabled(False)
# def _custom_set_company_name_contact_person(self, value: str) -> None:
# self.text_widget_set.setText(str(value))
# def _custom_county_selection(self, idx: int) -> None:
# value = self.person_location.itemData(idx)
# if value == "Inland":
# self.selection_county.setEnabled(True)
# self.selection_county.setProperty("styleClass", "")
# self.selection_county.style().unpolish(self.selection_county)
# self.selection_county.style().polish(self.selection_county)
# self.selection_county.update()
# else:
# self.selection_county.setCurrentIndex(0)
# self.selection_county.setEnabled(False)
# self.selection_county.setProperty("styleClass", "stempel")
# self.selection_county.style().unpolish(self.selection_county)
# self.selection_county.style().polish(self.selection_county)
# self.selection_county.update()
def _request_company_profile(self) -> None:
req_state = Page_CompanyProfile_State(
@@ -2993,23 +2952,6 @@ class Page_InitRecPerson(QWidget):
container_layout.addSpacing(15)
# --- CUSTOM LOGIC ---
# ** fill 'Kontaktperson -> Namen Unternehmen'
# TODO check how partners are selected with new architecture
# search_res = search_widgets_by_key(self.auto_form.widget_registry, "Partnersuche")
# assert len(search_res) == 1
# search_widget = cast(Grunderfassung_SuchWidget, search_res[0]["widget"])
# self.search_widget_trigger = cast(
# QLineEdit, search_widget.company_widgets["ma_unternehmensname"]
# )
# text_widget_set = search_widgets_by_key(
# self.auto_form.widget_registry, f"Kontaktperson{COLUMN_SEP}KP_name_partner"
# )
# assert len(text_widget_set) == 1
# self.text_widget_set = cast(QLineEdit, text_widget_set[0]["widget"])
# self.search_widget_trigger.textChanged.connect(
# self._custom_set_company_name_contact_person
# )
# ** 'Bundesland' only if 'Inland' selected in 'Stammdaten'
person_location = search_widgets_by_key(
self.auto_form.widget_registry, f"Stammdaten{COLUMN_SEP}aufenthaltsort"
@@ -3028,9 +2970,6 @@ class Page_InitRecPerson(QWidget):
self.selection_county.setProperty("styleClass", "stempel")
self.selection_county.setEnabled(False)
# def _custom_set_company_name_contact_person(self, value: str) -> None:
# self.text_widget_set.setText(str(value))
def _custom_county_selection(self, idx: int) -> None:
value = self.person_location.itemData(idx)
if value == "Inland":
@@ -3207,7 +3146,7 @@ class Page_CompanyProfile(QWidget):
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
)
left_layout.addWidget(master_data_label)
# self.table_master_data = MasterDataTable()
default_labels = tuple(self.COMPANY_FEATURES_MASTER_DATA.values())
self.table_master_data = MasterDataFormWidget(default_labels)
left_layout.addWidget(self.table_master_data)
@@ -3254,7 +3193,7 @@ class Page_CompanyProfile(QWidget):
bottom_right_layout.addWidget(individual_consultancy_label)
self.add_btn_individual = QPushButton("+ Hinzufügen")
self.add_btn_individual.clicked.connect(
lambda: self._request_consulting(ConsultingType.INDIVIUAL)
lambda: self._request_consulting(ConsultingType.INDIVIDUAL)
)
self.add_btn_individual.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
@@ -3317,7 +3256,10 @@ class Page_CompanyProfile(QWidget):
"tried to call consulting from comp profile without set ID"
)
new_state = Page_Consulting_State(
session=self.STATE.session, un_id=self.STATE.rec_id, cons_type=cons_type
session=self.STATE.session,
vorgang_id=None,
un_id=self.STATE.rec_id,
beratungs_typ=cons_type,
)
self.consulting_requested.emit(new_state)
@@ -3340,7 +3282,7 @@ class Page_CompanyProfile(QWidget):
self._enable_add_btns()
init_rec = backend.initrec_company_get_initial_recording(self.STATE.rec_id)
ma_id = cast(ExtMaId, init_rec["Partnersuche__un_suche"])
ma_id = cast("ExtMaId", init_rec["Partnersuche__un_suche"])
assert ma_id is not None, "no ma_id obtained"
comp_info = backend.initrec_comp_search_get_info(ma_id)
@@ -3598,7 +3540,7 @@ class Page_Consulting(QWidget):
back_requested = Signal(bool) # back to init rec (bool: reset yes/no)
# TODO update should trigger something else
# more like listening to 'child' ("Beratungen")
# update_triggered = Signal() # form saved (data changed for front page)
update_triggered = Signal() # form saved (data changed for front page)
def __init__(
self,
@@ -3606,7 +3548,11 @@ class Page_Consulting(QWidget):
):
super().__init__()
# state management
self.STATE = Page_Consulting_State(session=session)
self.STATE = Page_Consulting_State(
session=session,
vorgang_id=None,
beratungs_typ=ConsultingType.PAUSCHAL,
)
# Hauptlayout der Seite
outer_layout = QHBoxLayout(self)
@@ -3695,6 +3641,19 @@ class Page_Consulting(QWidget):
container_layout.addSpacing(30)
# --- BUTTONS ---
self.edit_buttons = EditButtons(add_reset_btn=False)
# reset_btn = self.edit_buttons.reset_btn
# assert reset_btn
self.save_btn = self.edit_buttons.save_btn
# self.reset_btn = reset_btn
self.delete_btn = self.edit_buttons.delete_btn
self.save_btn.clicked.connect(self.save_data)
# self.reset_btn.clicked.connect(self.reset_form)
self.delete_btn.clicked.connect(self._delete_data)
container_layout.addWidget(self.edit_buttons)
type_group_box = QGroupBox("Typ")
type_layout = QVBoxLayout(type_group_box)
container_layout.addWidget(type_group_box)
@@ -3712,8 +3671,8 @@ class Page_Consulting(QWidget):
type_layout.addWidget(self.type_individual_btn)
# container_layout.addSpacing(50)
table_state = Page_Consulting_Table_State()
self.table_sessions = Page_Consulting_Table(site_state=self.STATE, state=table_state)
table_state = Page_Consulting_Table_State(session=self.STATE.session)
self.table_sessions = Page_Consulting_Table(state=table_state)
self.STATE.child_modules.append(self.table_sessions)
container_layout.addWidget(self.table_sessions)
@@ -3748,6 +3707,28 @@ class Page_Consulting(QWidget):
# font-weight: bold;
# }
# """)
self._sync_state_to_GUI()
def _delete_data(self) -> None:
logger_page_consulting.debug("[Consulting Page] Call data delete...")
def _disable_save(self) -> None:
self.save_btn.setEnabled(False)
self.save_btn.setText(self.edit_buttons.save_btn_txt_disabled)
def _activate_delete(self) -> None:
if self.STATE.un_id is not None or self.STATE.pers_id is not None:
self.delete_btn.setEnabled(True)
else:
self.delete_btn.setEnabled(False)
def _enable_save(
self,
timeout: int = 3000,
) -> None:
QTimer.singleShot(timeout, lambda: self.save_btn.setEnabled(True))
QTimer.singleShot(timeout + 1, lambda: self.save_btn.setShortcut("Ctrl+S"))
self.save_btn.setText(self.edit_buttons.save_btn_txt_enabled)
def type_consulting_changed(
self,
@@ -3759,12 +3740,12 @@ class Page_Consulting(QWidget):
if button_id == 0:
cons_type = ConsultingType.PAUSCHAL
elif button_id == 1:
cons_type = ConsultingType.INDIVIUAL
cons_type = ConsultingType.INDIVIDUAL
else:
raise RuntimeError(f"No consulting type defined for button id: {button_id}")
logger_page_consulting.debug("[Consulting Page] Selected cons_type: %s", cons_type)
self.STATE.cons_type = cons_type
self.STATE.beratungs_typ = cons_type
def _debug_get_state(self) -> None:
logger_page_consulting.debug("[Consulting Page] Call get state on table...")
@@ -3782,10 +3763,10 @@ class Page_Consulting(QWidget):
logger_page_consulting.debug("[Consulting Page] Page errors:\n%s", pformat(errors))
def _sync_state_to_GUI(self) -> None:
match self.STATE.cons_type:
match self.STATE.beratungs_typ:
case ConsultingType.PAUSCHAL:
self.type_pauschal_btn.setChecked(True)
case ConsultingType.INDIVIUAL:
case ConsultingType.INDIVIDUAL:
self.type_individual_btn.setChecked(True)
# deactivate if at least one ID is set (type is pre-defined by how
@@ -3803,6 +3784,7 @@ class Page_Consulting(QWidget):
# this method
table_state = self.table_sessions.get_state()
self.STATE.cons_sessions = table_state.row_states
self._activate_delete()
def validate(self) -> list[str]:
errors: list[str] = []
@@ -3817,7 +3799,10 @@ class Page_Consulting(QWidget):
logger_page_consulting.debug("[Consulting] Loading with request:\n%s", new_state)
set_page_state(self.STATE, new_state)
# child modules
table_state = Page_Consulting_Table_State(row_states=self.STATE.cons_sessions)
table_state = Page_Consulting_Table_State(
session=self.STATE.session,
row_states=self.STATE.cons_sessions,
)
self.table_sessions.load_state(table_state)
self._sync_state_to_GUI()
@@ -3827,6 +3812,98 @@ class Page_Consulting(QWidget):
return self.STATE
def save_data(self) -> None:
logger_page_consulting.debug("[Consulting Page] Call saving data...")
self._disable_save()
errors = self.validate()
if errors:
logger_page_consulting.error(
"[Consulting Page] Error during GUI validation phase:\n%s", pformat(errors)
)
GUI_validation_error_handling(errors)
self._enable_save()
return
logger_page_consulting.info("[Consulting Page] Success! All data is valid")
logger_page_consulting.info("[Consulting Page] Get form data call...")
state = self.get_state()
logger_page_consulting.debug("[Consulting Page] Get state:\n%s", state)
try:
logger_page_consulting.debug("[Consulting Page] Call Pydantic...")
pydantic_sessions: list[Beratungsgespraech_Einzelgespraech] = []
for row in self.STATE.cons_sessions:
cons_session_dict = dc.asdict(row)
cons_session = Beratungsgespraech_Einzelgespraech(
vorgang_id=self.STATE.vorgang_id,
**cons_session_dict,
)
pydantic_sessions.append(cons_session)
consulting_process = Beratungsgespraech_Vorgang(
vorgang_id=self.STATE.vorgang_id,
un_id=self.STATE.un_id,
pers_id=self.STATE.pers_id,
titel=self.STATE.titel,
beratungs_typ=self.STATE.beratungs_typ, # type: ignore
beratungen=pydantic_sessions,
)
except ValidationError as e:
logger_auto_form.error(
"[Consulting Page] Error during Pydantic validation phase:\n%s",
pformat(e.errors()),
)
pydantic_validation_error_handling(e)
else:
if DEBUG_NO_DATABASE:
return
# logger_page_consulting.debug(
# "[Consulting Page] Validated Pydantic successfully. Model:\n%s",
# pformat(consulting_process.model_dump()),
# )
# !! should be placed in a try block to handle errors
backend.page_consulting_to_db(consulting_process)
# # !! this code is only called if the 'try' block was successful
# # save data to database
# # db_data = validated_data.to_db(exclude=self.cfg.ignored_keys)
# # logger_auto_form.debug(
# # "[Consulting Page] Form data with 'exlude' (must be saved in the database):\n%s",
# # pformat(db_data),
# # )
# if self.STATE.rec_id is None:
# logger_auto_form.debug("[Consulting Page] Insert triggered...")
# # should return "vorgang_id"
# # 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("[Consulting Page] Update triggered...")
# # self.cfg.data_update(self.STATE.rec_id, db_data)
# logger_auto_form.info("[Consulting Page] Data saved successfully")
# self.update_triggered.emit()
# self._activate_delete()
finally:
# always re-enable save, even if error occurred
self._enable_save()
def load_database(
self,
cons_id: ConsId,
) -> None:
# TODO add routine
# load data model from database
consulting_process = backend.page_consulting_from_db(cons_id)
# instantiate state from data model
...
class Page_Consulting_ConsultingSession(QWidget):
"""child of 'Page_Consulting_DynamicTable'"""
@@ -3835,13 +3912,10 @@ class Page_Consulting_ConsultingSession(QWidget):
def __init__(
self,
site_state: Page_Consulting_State,
state: Page_Consulting_ConsultingSession_State,
) -> None:
super().__init__()
assert isinstance(site_state, Page_Consulting_State)
assert isinstance(state, Page_Consulting_ConsultingSession_State)
self.SITE_STATE = site_state
self.STATE = state
layout = QHBoxLayout(self)
@@ -3866,7 +3940,7 @@ class Page_Consulting_ConsultingSession(QWidget):
"Zeitstempel",
FormFieldType.DATETIME_FLEXIBLE,
required=True,
enable_uuid_key=True,
key="zeitstempel",
)
_, self.timestamp = build_and_add_widget(
timestamp_def,
@@ -3880,7 +3954,7 @@ class Page_Consulting_ConsultingSession(QWidget):
FormFieldType.TEXT,
placeholder="Freitext",
required=True,
enable_uuid_key=True,
key="ansprechpartner",
)
_, self.contact = build_and_add_widget(
contact_def,
@@ -3893,7 +3967,7 @@ class Page_Consulting_ConsultingSession(QWidget):
FormFieldType.DROPDOWN,
options=[("Persönlich", None), ("Telefon", None), ("Mail", None)],
required=True,
enable_uuid_key=True,
key="kommunikationsweg",
)
_, self.contact_type = build_and_add_widget(
contact_type_def,
@@ -3906,7 +3980,7 @@ class Page_Consulting_ConsultingSession(QWidget):
FormFieldType.TEXT,
placeholder="CRM Links",
required=True,
enable_uuid_key=True,
key="thema_crm_matrix",
)
_, self.crm_link = build_and_add_widget(
crm_link_def,
@@ -3917,7 +3991,7 @@ class Page_Consulting_ConsultingSession(QWidget):
plain_text_def = FormField[QPlainTextEdit](
"Anmerkungen",
FormFieldType.LONGTEXT,
enable_uuid_key=True,
key="anmerkungen",
)
_, self.plain_text = build_and_add_widget(
plain_text_def,
@@ -3928,7 +4002,7 @@ class Page_Consulting_ConsultingSession(QWidget):
response_def = FormField[QPlainTextEdit](
"Rückmeldung",
FormFieldType.LONGTEXT,
enable_uuid_key=True,
key="rueckmeldung",
)
_, self.response = build_and_add_widget(
response_def,
@@ -3983,16 +4057,18 @@ class Page_Consulting_ConsultingSession(QWidget):
set_widget_value(self.user_field, self.STATE.nutzer_name)
set_widget_value(self.timestamp, self.STATE.zeitstempel.astimezone(TIMEZONE_CEST))
set_widget_value(self.contact, self.STATE.ansprechpartner)
set_widget_value(self.contact_type, self.STATE.interaction_scheme)
set_widget_value(self.crm_link, self.STATE.topic)
set_widget_value(self.contact_type, self.STATE.kommunikationsweg)
set_widget_value(self.crm_link, self.STATE.thema_crm_matrix)
set_widget_value(self.plain_text, self.STATE.anmerkungen)
set_widget_value(self.response, self.STATE.rueckmeldung)
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)
self.STATE.topic = get_widget_value(self.crm_link) # TODO: change to correct link
self.STATE.kommunikationsweg = get_widget_value(self.contact_type)
self.STATE.thema_crm_matrix = get_widget_value(
self.crm_link
) # TODO: change to correct link
self.STATE.anmerkungen = get_widget_value(self.plain_text)
self.STATE.rueckmeldung = get_widget_value(self.response)
@@ -4050,17 +4126,14 @@ class Page_Consulting_Table(QWidget):
def __init__(
self,
site_state: Page_Consulting_State,
state: Page_Consulting_Table_State,
) -> None:
super().__init__()
self.STATE = state
self.setWindowTitle("Native Dynamische Liste")
self.resize(600, 400)
self.STATE = state
# TODO eliminate site state
self.SITE_STATE = site_state
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(0, 15, 0, 15)
main_layout.setSpacing(10)
@@ -4215,11 +4288,13 @@ class Page_Consulting_Table(QWidget):
) -> None:
if row_state is None:
row_state = Page_Consulting_ConsultingSession_State(
nutzer_id=self.SITE_STATE.session.user_id,
nutzer_name=self.SITE_STATE.session.user_name,
session=self.STATE.session,
beratung_id=None,
nutzer_id=self.STATE.session.user_id,
nutzer_name=self.STATE.session.user_name,
)
row = Page_Consulting_ConsultingSession(site_state=self.SITE_STATE, state=row_state)
row = Page_Consulting_ConsultingSession(state=row_state)
row.delete_request.connect(self.remove_row)
index = self.container_layout.count() - 1
@@ -4254,16 +4329,6 @@ class Page_Consulting_Table(QWidget):
for row in self.STATE.child_modules:
row_data.append(row.get_state())
# TODO remove
# for i in range(self.container_layout.count() - 1):
# item = self.container_layout.itemAt(i)
# assert item, "item not found"
# widget = item.widget()
# assert widget, "no widget obtained"
# if isinstance(widget, Page_Consulting_ConsultingSession):
# row_data.append(widget.get_state())
assert len(row_data) == len(self.STATE.child_modules), (
"mismatch in row count and data length"
)
@@ -4674,21 +4739,6 @@ def GUI_validation_error_handling(
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,
@@ -4698,8 +4748,6 @@ class EditButtons(QWidget):
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..."
@@ -4707,9 +4755,7 @@ class EditButtons(QWidget):
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:
@@ -4719,66 +4765,15 @@ class EditButtons(QWidget):
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):
"""forwards internal Qt C++ warnings to Python loggers"""
if mode == QtMsgType.QtInfoMsg:
+1
View File
@@ -10,6 +10,7 @@ if TYPE_CHECKING:
UserId: TypeAlias = int
RecId: TypeAlias = int
ConsId: TypeAlias = int
ExtMaId: TypeAlias = int
ExtAnId: TypeAlias = int