small refactoring with more precise declarations

This commit is contained in:
2026-06-17 16:54:54 +02:00
parent f24c4f0ff7
commit decfdbffd1
2 changed files with 101 additions and 41 deletions

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
import dataclasses as dc import dataclasses as dc
import datetime import datetime
from typing import Any, TypedDict, cast from typing import Any, TypeAlias, TypedDict, cast
import polars as pl import polars as pl
import sqlalchemy as sql import sqlalchemy as sql
@@ -10,6 +10,8 @@ import sqlalchemy as sql
from wce_crm import db from wce_crm import db
from wce_crm.logging import logger_back as logger from wce_crm.logging import logger_back as logger
InitRecId: TypeAlias = int
class CompanyInfo(TypedDict): class CompanyInfo(TypedDict):
ma_id: str ma_id: str
@@ -151,43 +153,63 @@ def initrec_comp_contact_person_search_get_info(
def initrec_insert_initial_recording( def initrec_insert_initial_recording(
data: dict[str, Any], data: dict[str, Any],
) -> None: ) -> InitRecId:
logger.debug("[Call backend] insert_initial_recording") logger.debug("[Call backend] insert_initial_recording")
stmt = db.grunderfassung.insert().values(data) stmt = db.grunderfassung.insert()
with db.ENGINE.begin() as conn: with db.ENGINE.begin() as conn:
conn.execute(stmt) ret = conn.execute(stmt, data)
if ret.rowcount == 0:
raise IOError("Entry was not inserted correctly")
prim_keys = ret.inserted_primary_key
assert prim_keys
return prim_keys[0]
def initrec_update_initial_recording( def initrec_update_initial_recording(
id_: int, id_: InitRecId,
data: dict[str, Any], data: dict[str, Any],
) -> None: ) -> None:
logger.debug("[Call backend] update_initial_recording") logger.debug("[Call backend] update_initial_recording")
stmt = ( stmt = db.grunderfassung.update().where(db.grunderfassung.c.erfassung_id == id_)
db.grunderfassung.update().where(db.grunderfassung.c.erfassung_id == id_).values(data)
)
with db.ENGINE.begin() as conn: with db.ENGINE.begin() as conn:
conn.execute(stmt) conn.execute(stmt, data)
def initrec_get_initial_recording( def initrec_get_initial_recording(
id_: int, id_: InitRecId,
) -> dict[str, Any]: ) -> dict[str, Any]:
logger.debug("[Call backend] get_initial_recording") logger.debug("[Call backend] get_initial_recording")
stmt = db.grunderfassung.select().where(db.grunderfassung.c.erfassung_id == id_) stmt = db.grunderfassung.select().where(db.grunderfassung.c.erfassung_id == id_)
with db.ENGINE.begin() as conn: with db.ENGINE.begin() as conn:
ret = conn.execute(stmt) ret = conn.execute(stmt)
row = ret.fetchone() if ret.rowcount == 0:
if row is None:
raise KeyError(f"Database ID {id_} not found") raise KeyError(f"Database ID {id_} not found")
row = ret.fetchone()
assert row, "row was not obtained"
return row._asdict() return row._asdict()
def initrec_delete_initial_recording(
id_: InitRecId,
) -> None:
logger.debug("[Call backend] delete_initial_recording")
stmt = db.grunderfassung.delete().where(db.grunderfassung.c.erfassung_id == id_)
with db.ENGINE.begin() as conn:
ret = conn.execute(stmt)
if ret.rowcount == 0:
raise KeyError(f"Database ID {id_} not found for deletion")
@dc.dataclass(slots=True) @dc.dataclass(slots=True)
class FrontpageCompany: class FrontpageCompany:
erfassung_id: int erfassung_id: InitRecId
# ma_id: int # ma_id: int
name: str name: str
Metadaten_aktualisierung: datetime.datetime Metadaten_aktualisierung: datetime.datetime

View File

@@ -53,7 +53,7 @@ from PySide6.QtWidgets import (
) )
import wce_crm.constants import wce_crm.constants
from wce_crm.backend import backend as be_init_rec from wce_crm.backend import backend
from wce_crm.data_models import COLUMN_SEP, FlatBaseModel, Grunderfassung from wce_crm.data_models import COLUMN_SEP, FlatBaseModel, Grunderfassung
from wce_crm.form_defs import ( from wce_crm.form_defs import (
INITREC_COMP, INITREC_COMP,
@@ -226,7 +226,7 @@ class AutoFormInsert(Protocol):
def __call__( def __call__(
self, self,
data: dict[str, Any], data: dict[str, Any],
) -> None: ... ) -> backend.InitRecId: ...
class AutoFormUpdate(Protocol): class AutoFormUpdate(Protocol):
@@ -244,12 +244,20 @@ class AutoFormGet(Protocol):
) -> dict[str, Any]: ... ) -> dict[str, Any]: ...
class AutoFormDelete(Protocol):
def __call__(
self,
id_: int,
) -> None: ...
@dc.dataclass(slots=True) @dc.dataclass(slots=True)
class AutoFormConfig: class AutoFormConfig:
model: type[FlatBaseModel] model: type[FlatBaseModel]
data_insert: AutoFormInsert data_insert: AutoFormInsert
data_update: AutoFormUpdate data_update: AutoFormUpdate
data_get: AutoFormGet data_get: AutoFormGet
data_delete: AutoFormDelete
form_fields: Sequence[FormField] form_fields: Sequence[FormField]
ignored_keys: Iterable[str] = tuple() ignored_keys: Iterable[str] = tuple()
add_buttons: bool = True add_buttons: bool = True
@@ -1001,7 +1009,7 @@ class Grunderfassung_SuchWidget(CustomWidget):
def fill_out_company( def fill_out_company(
self, self,
data: be_init_rec.CompanyInfo, data: backend.CompanyInfo,
) -> None: ) -> None:
for key, widget in self.company_widgets.items(): for key, widget in self.company_widgets.items():
if key not in data: if key not in data:
@@ -1015,7 +1023,7 @@ class Grunderfassung_SuchWidget(CustomWidget):
def fill_out_person( def fill_out_person(
self, self,
data: be_init_rec.ContactPersonInfo, data: backend.ContactPersonInfo,
) -> None: ) -> None:
for key, widget in self.person_widgets.items(): for key, widget in self.person_widgets.items():
if key not in data: if key not in data:
@@ -1042,7 +1050,7 @@ class Grunderfassung_SuchWidget(CustomWidget):
def update_company_data(self) -> None: def update_company_data(self) -> None:
self.company_search_input.clear() self.company_search_input.clear()
self.company_search_input.addItem(DROPDOWN_DEFAULT, None) self.company_search_input.addItem(DROPDOWN_DEFAULT, None)
search_choices = be_init_rec.initrec_comp_search_choices() search_choices = backend.initrec_comp_search_choices()
for item, db_index in search_choices: for item, db_index in search_choices:
self.company_search_input.addItem(item, db_index) self.company_search_input.addItem(item, db_index)
self.company_search_input.setCurrentIndex(-1) self.company_search_input.setCurrentIndex(-1)
@@ -1053,7 +1061,7 @@ class Grunderfassung_SuchWidget(CustomWidget):
) -> None: ) -> None:
self.person_search_input.clear() self.person_search_input.clear()
self.person_search_input.addItem(DROPDOWN_DEFAULT, None) self.person_search_input.addItem(DROPDOWN_DEFAULT, None)
search_choices = be_init_rec.initrec_comp_contact_person_search_choices(ma_id, True) search_choices = backend.initrec_comp_contact_person_search_choices(ma_id, True)
for item, db_index in search_choices: for item, db_index in search_choices:
self.person_search_input.addItem(item, db_index) self.person_search_input.addItem(item, db_index)
self.person_search_input.setCurrentIndex(0) self.person_search_input.setCurrentIndex(0)
@@ -1066,7 +1074,7 @@ class Grunderfassung_SuchWidget(CustomWidget):
if ma_id is None or index == (-1): if ma_id is None or index == (-1):
self._clear_company_fields() self._clear_company_fields()
return return
data = be_init_rec.initrec_comp_search_get_info( data = backend.initrec_comp_search_get_info(
ma_id=ma_id, ma_id=ma_id,
) )
self.fill_out_company(data) self.fill_out_company(data)
@@ -1081,7 +1089,7 @@ class Grunderfassung_SuchWidget(CustomWidget):
self._clear_person_fields() self._clear_person_fields()
return return
data = be_init_rec.initrec_comp_contact_person_search_get_info( data = backend.initrec_comp_contact_person_search_get_info(
an_id=an_id, an_id=an_id,
) )
self.fill_out_person(data) self.fill_out_person(data)
@@ -1159,7 +1167,7 @@ def search_widgets_by_key(
class AutoForm(QWidget): class AutoForm(QWidget):
"""a widget, which is managed by a code-defined field definition collection""" """a widget, which is managed by a code-defined field definition collection"""
save_clicked_form = Signal() # formular saved (data changed for front page) update_triggered = Signal() # formular saved (data changed for front page)
def __init__( def __init__(
self, self,
@@ -1273,6 +1281,7 @@ class AutoForm(QWidget):
if self.add_buttons: if self.add_buttons:
self.layout_btn = QHBoxLayout() self.layout_btn = QHBoxLayout()
self.main_layout.addLayout(self.layout_btn) self.main_layout.addLayout(self.layout_btn)
# save
self.save_btn_txt_enabled = "Speichern (Strg + S)" self.save_btn_txt_enabled = "Speichern (Strg + S)"
self.save_btn_txt_disabled = "Wird gespeichert..." self.save_btn_txt_disabled = "Wird gespeichert..."
self.save_btn = QPushButton(self.save_btn_txt_enabled) self.save_btn = QPushButton(self.save_btn_txt_enabled)
@@ -1283,6 +1292,7 @@ class AutoForm(QWidget):
) )
self.save_btn.clicked.connect(self.save_data) self.save_btn.clicked.connect(self.save_data)
self.layout_btn.addWidget(self.save_btn) self.layout_btn.addWidget(self.save_btn)
# reset
self.reset_btn = QPushButton("Zurücksetzen (Strg + Z)") self.reset_btn = QPushButton("Zurücksetzen (Strg + Z)")
self.reset_btn.setShortcut("Ctrl+Z") self.reset_btn.setShortcut("Ctrl+Z")
self.reset_btn.setFixedHeight(50) self.reset_btn.setFixedHeight(50)
@@ -1291,6 +1301,15 @@ class AutoForm(QWidget):
) )
self.reset_btn.clicked.connect(self.reset_form) self.reset_btn.clicked.connect(self.reset_form)
self.layout_btn.addWidget(self.reset_btn) 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.layout_btn.addWidget(self.delete_btn)
self.current_id: int = -1 self.current_id: int = -1
@@ -1322,6 +1341,18 @@ class AutoForm(QWidget):
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.save_btn_txt_enabled)
def _activation_delete(self) -> None:
if self.current_id > -1:
self.delete_btn.setEnabled(True)
else:
self.delete_btn.setEnabled(False)
def delete_data(self) -> None:
assert self.current_id > -1, "deletion initialised despite no index set"
self.cfg.data_delete(self.current_id)
self.update_triggered.emit()
self.reset_form()
def load_data( def load_data(
self, self,
lookup_id: int | None = None, lookup_id: int | None = None,
@@ -1354,6 +1385,7 @@ class AutoForm(QWidget):
logger_auto_form.debug("Form data:\n%s", pformat(form_data)) logger_auto_form.debug("Form data:\n%s", pformat(form_data))
self.set_form_data(form_data) self.set_form_data(form_data)
self.current_id = lookup_id self.current_id = lookup_id
self._activation_delete()
def save_data(self) -> None: def save_data(self) -> None:
self._disable_save() self._disable_save()
@@ -1417,13 +1449,16 @@ class AutoForm(QWidget):
if self.current_id < 0: if self.current_id < 0:
logger_auto_form.debug("Insert triggered") logger_auto_form.debug("Insert triggered")
self.cfg.data_insert(db_data) init_rec_id = self.cfg.data_insert(db_data)
assert isinstance(init_rec_id, int)
self.current_id = init_rec_id
else: else:
logger_auto_form.debug("Update triggered") logger_auto_form.debug("Update triggered")
self.cfg.data_update(self.current_id, db_data) self.cfg.data_update(self.current_id, db_data)
logger_auto_form.info("Data saved successfully") logger_auto_form.info("Data saved successfully")
self.save_clicked_form.emit() self.update_triggered.emit()
self._activation_delete()
# self.reset_form() # TODO check if this behaviour is expected # self.reset_form() # TODO check if this behaviour is expected
finally: finally:
# always re-enable save, even if error occurred # always re-enable save, even if error occurred
@@ -1435,6 +1470,7 @@ class AutoForm(QWidget):
def reset_form(self) -> None: def reset_form(self) -> None:
reset_form(self.widget_registry) reset_form(self.widget_registry)
self.current_id = -1 self.current_id = -1
self._activation_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)
@@ -1977,12 +2013,12 @@ class NoScrollFilter(QObject):
class ClickableCell(QFrame): class ClickableCell(QFrame):
"""cell in the table on the startup screen""" """cell in the table on the startup screen"""
clicked = Signal(be_init_rec.FrontpageCompany) clicked = Signal(backend.FrontpageCompany)
def __init__( def __init__(
self, self,
text: str, text: str,
data_record: be_init_rec.FrontpageCompany, data_record: backend.FrontpageCompany,
): ):
super().__init__() super().__init__()
self.data_record = data_record self.data_record = data_record
@@ -2068,9 +2104,10 @@ class NewEntrySelect_view(QWidget):
CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig( CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig(
model=Grunderfassung, model=Grunderfassung,
data_insert=be_init_rec.initrec_insert_initial_recording, data_insert=backend.initrec_insert_initial_recording,
data_update=be_init_rec.initrec_update_initial_recording, data_update=backend.initrec_update_initial_recording,
data_get=be_init_rec.initrec_get_initial_recording, data_get=backend.initrec_get_initial_recording,
data_delete=backend.initrec_delete_initial_recording,
ignored_keys=( ignored_keys=(
"Metadaten_erstellung", "Metadaten_erstellung",
"Metadaten_aktualisierung", "Metadaten_aktualisierung",
@@ -2081,9 +2118,10 @@ CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig(
CONFIG_GRUNDERFASSUNG_PERSONEN: Final[AutoFormConfig] = AutoFormConfig( CONFIG_GRUNDERFASSUNG_PERSONEN: Final[AutoFormConfig] = AutoFormConfig(
model=Grunderfassung, model=Grunderfassung,
data_insert=be_init_rec.initrec_insert_initial_recording, data_insert=backend.initrec_insert_initial_recording,
data_update=be_init_rec.initrec_update_initial_recording, data_update=backend.initrec_update_initial_recording,
data_get=be_init_rec.initrec_get_initial_recording, data_get=backend.initrec_get_initial_recording,
data_delete=backend.initrec_delete_initial_recording,
ignored_keys=( ignored_keys=(
"Metadaten_erstellung", "Metadaten_erstellung",
"Metadaten_aktualisierung", "Metadaten_aktualisierung",
@@ -2100,7 +2138,7 @@ CUSTOM_WIDGETS: Final[dict[str, type[CustomWidget]]] = {
class Page_InitRecCompany(QWidget): class Page_InitRecCompany(QWidget):
back_main_requested = Signal() # back to main page back_main_requested = Signal() # back to main page
back_requested = Signal() # back button back_requested = Signal() # back button
save_clicked_form = Signal() # form saved (data changed for front page) update_triggered = Signal() # form saved (data changed for front page)
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@@ -2164,7 +2202,7 @@ class Page_InitRecCompany(QWidget):
container_layout.addSpacing(20) container_layout.addSpacing(20)
self.auto_form = AutoForm(cfg=CONFIG_GRUNDERFASSUNG_UNTERNEHMEN) self.auto_form = AutoForm(cfg=CONFIG_GRUNDERFASSUNG_UNTERNEHMEN)
container_layout.addWidget(self.auto_form) container_layout.addWidget(self.auto_form)
self.auto_form.save_clicked_form.connect(lambda: self.save_clicked_form.emit()) self.auto_form.update_triggered.connect(lambda: self.update_triggered.emit())
container_layout.addSpacing(15) container_layout.addSpacing(15)
@@ -2229,7 +2267,7 @@ class Page_InitRecCompany(QWidget):
class Page_InitRecPerson(QWidget): class Page_InitRecPerson(QWidget):
back_main_requested = Signal() # back to main page back_main_requested = Signal() # back to main page
back_requested = Signal() # back button back_requested = Signal() # back button
save_clicked_form = Signal() # form saved (data changed for front page) update_triggered = Signal() # form saved (data changed for front page)
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@@ -2293,7 +2331,7 @@ class Page_InitRecPerson(QWidget):
container_layout.addSpacing(20) container_layout.addSpacing(20)
self.auto_form = AutoForm(cfg=CONFIG_GRUNDERFASSUNG_PERSONEN) self.auto_form = AutoForm(cfg=CONFIG_GRUNDERFASSUNG_PERSONEN)
container_layout.addWidget(self.auto_form) container_layout.addWidget(self.auto_form)
self.auto_form.save_clicked_form.connect(lambda: self.save_clicked_form.emit()) self.auto_form.update_triggered.connect(lambda: self.update_triggered.emit())
container_layout.addSpacing(15) container_layout.addSpacing(15)
@@ -2402,13 +2440,13 @@ class MainWindow(QMainWindow):
self.initrec_company = Page_InitRecCompany() self.initrec_company = Page_InitRecCompany()
self.initrec_company.back_main_requested.connect(self.show_main_page) self.initrec_company.back_main_requested.connect(self.show_main_page)
self.initrec_company.back_requested.connect(self.show_new_entry_select) self.initrec_company.back_requested.connect(self.show_new_entry_select)
self.initrec_company.save_clicked_form.connect(self.update_grid) self.initrec_company.update_triggered.connect(self.update_grid)
self.stack.addWidget(self.initrec_company) self.stack.addWidget(self.initrec_company)
# SITE: 'Grunderfassung Person' # SITE: 'Grunderfassung Person'
self.initrec_person = Page_InitRecPerson() self.initrec_person = Page_InitRecPerson()
self.initrec_person.back_main_requested.connect(self.show_main_page) self.initrec_person.back_main_requested.connect(self.show_main_page)
self.initrec_person.back_requested.connect(self.show_new_entry_select) self.initrec_person.back_requested.connect(self.show_new_entry_select)
self.initrec_person.save_clicked_form.connect(self.update_grid) self.initrec_person.update_triggered.connect(self.update_grid)
self.stack.addWidget(self.initrec_person) self.stack.addWidget(self.initrec_person)
def setup_main_page(self): def setup_main_page(self):
@@ -2505,14 +2543,14 @@ class MainWindow(QMainWindow):
def update_grid(self) -> None: def update_grid(self) -> None:
clear_layout(self.grid) clear_layout(self.grid)
data = be_init_rec.front_get_company_list() data = backend.front_get_company_list()
for entry in data: for entry in data:
self.add_row_to_grid(entry) self.add_row_to_grid(entry)
def add_row_to_grid( def add_row_to_grid(
self, self,
entry: be_init_rec.FrontpageCompany, entry: backend.FrontpageCompany,
): ):
row = self.current_row row = self.current_row
@@ -2546,7 +2584,7 @@ class MainWindow(QMainWindow):
def goto_initial_recording( def goto_initial_recording(
self, self,
data: be_init_rec.FrontpageCompany, data: backend.FrontpageCompany,
): ):
if data.is_company: if data.is_company:
self.initrec_company.auto_form.load_data(data.erfassung_id) self.initrec_company.auto_form.load_data(data.erfassung_id)