minor and major reworks: rename swb to SRU, add a test for pdf parsing

major: rework mail to send mail as plaintext instead of html, preventing the bleed-in of html text
This commit is contained in:
2025-10-07 14:15:10 +02:00
parent 0df7fd9fe6
commit 06965db26a
25 changed files with 1174 additions and 303 deletions

View File

@@ -12,20 +12,21 @@ __all__ = [
"ElsaAddEntry",
"ApparatExtendDialog",
"DocumentPrintDialog",
"NewEditionDialog",
"Settings",
]
from .about import About
from .app_ext import ApparatExtendDialog
from .bookdata import BookDataUI
from .docuprint import DocumentPrintDialog
from .elsa_add_entry import ElsaAddEntry
from .elsa_gen_confirm import ElsaGenConfirm
from .login import LoginDialog
from .mail import Mail_Dialog
from .mailTemplate import MailTemplateDialog
from .medienadder import MedienAdder
from .newEdition import NewEditionDialog
from .parsed_titles import ParsedTitles
from .popup_confirm import ConfirmDialog as popus_confirm
from .reminder import ReminderDialog
from .about import About
from .elsa_gen_confirm import ElsaGenConfirm
from .elsa_add_entry import ElsaAddEntry
from .app_ext import ApparatExtendDialog
from .docuprint import DocumentPrintDialog
from .settings import Settings

View File

@@ -2,7 +2,8 @@ from natsort import natsorted
from PySide6 import QtWidgets
from src import Icon
from src.backend import Database, Semester
from src.backend import Database
from src.logic import Semester
from src.utils.richtext import SemapSchilder, SemesterDocument
from .dialog_sources.documentprint_ui import Ui_Dialog

View File

@@ -1,4 +1,6 @@
import os
import re
import smtplib
import sys
import loguru
@@ -7,7 +9,7 @@ from PySide6 import QtWidgets
from src import LOG_DIR, Icon
from src import settings as config
from .dialog_sources.Ui_mail_preview import Ui_eMailPreview as MailPreviewDialog
from .dialog_sources.mail_preview_ui import Ui_eMailPreview as MailPreviewDialog
from .mailTemplate import MailTemplateDialog
log = loguru.logger
@@ -15,37 +17,61 @@ log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
CSS_RESET = "<style>html,body{margin:0;padding:0}p{margin:0}</style>"
empty_signature = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
empty_signature = """"""
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style
type="text/css">
p, li { white-space: pre-wrap; }
def _escape_braces_in_style(html: str) -> str:
"""
Double curly braces ONLY inside <style>...</style> blocks so that
str.format(...) won't treat CSS as placeholders. The doubled braces
will automatically render back to single braces after formatting.
"""
hr { height: 1px; border-width: 0; }
def repl(m):
start, css, end = m.group(1), m.group(2), m.group(3)
css_escaped = css.replace("{", "{{").replace("}", "}}")
return f"{start}{css_escaped}{end}"
li.unchecked::marker { content: "\2610"; }
return re.sub(
r"(<style[^>]*>)(.*?)(</style>)",
repl,
html,
flags=re.IGNORECASE | re.DOTALL,
)
li.checked::marker { content: "\2612"; }
</style></head><body style=" font-family:''Segoe UI''; font-size:9pt; font-weight:400;
font-style:normal;">
def _split_eml_headers_body(eml_text: str) -> tuple[str, str]:
"""
Return (headers, body_html). Robustly split on first blank line.
Accepts lines that contain only spaces/tabs as the separator.
"""
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px;
margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>
"""
parts = re.split(r"\r?\n[ \t]*\r?\n", eml_text, maxsplit=1)
if len(parts) == 2:
return parts[0], parts[1]
# Fallback: try to split right after the Content-Transfer-Encoding line
m = re.search(
r"(?:^|\r?\n)Content-Transfer-Encoding:.*?(?:\r?\n)",
eml_text,
flags=re.I | re.S,
)
if m:
return eml_text[: m.end()], eml_text[m.end() :]
return "", eml_text # last resort: treat entire content as body
class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
def __init__(
self,
app_id,
app_name,
app_subject,
prof_name,
prof_mail,
app_id=None,
app_name=None,
app_subject=None,
prof_name=None,
prof_mail=None,
accepted_books=None,
ordered_books=None,
parent=None,
default_mail="Information zum Semesterapparat",
):
@@ -58,6 +84,7 @@ class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
self.subject = app_subject
self.profname = prof_name
self.books = accepted_books if accepted_books is not None else []
self.ordered_books = ordered_books if ordered_books is not None else []
self.mail_data = ""
self.signature = self.determine_signature()
self.prof_mail = prof_mail
@@ -65,52 +92,29 @@ class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
self.prof_name.setText(prof_name)
self.mail_name.setText(self.prof_mail)
self.load_mail_templates()
# if none of the radio buttons is checked, disable the accept button of the dialog
self.setWindowIcon(Icon("mail").icon)
self.btn_okay.setEnabled(False)
Icon("edit_note", self.newTemplate)
self.newTemplate.clicked.connect(self.open_new_template)
if default_mail is not None:
# get the nearest match to the default mail
for i in range(self.comboBox.count()):
if default_mail in self.comboBox.itemText(i):
default_mail = self.comboBox.itemText(i)
break
self.comboBox.setCurrentText(default_mail)
self.comboBox.currentIndexChanged.connect(self.set_mail)
# re-render when user changes greeting via radio buttons
self.gender_female.clicked.connect(self.set_mail)
self.gender_male.clicked.connect(self.set_mail)
self.gender_non.clicked.connect(self.set_mail)
# reflect initial state (OK disabled until a greeting is chosen)
self._update_ok_button()
self.btn_okay.clicked.connect(self.createAndSendMail)
def open_new_template(self):
log.info("Opening new template dialog")
# TODO: implement new mail template dialog
dialog = MailTemplateDialog()
dialog.updateSignal.connect(self.load_mail_templates)
dialog.exec()
pass
def determine_signature(self):
if config.mail.signature is empty_signature or config.mail.signature == "":
return """Mit freundlichen Grüßen
Ihr Semesterapparatsteam
Mail: semesterapparate@ph-freiburg.de
Tel.: 0761/682-778 | 07617682-545"""
else:
return config.mail.signature
def load_mail_templates(self):
# print("loading mail templates")
log.info("Loading mail templates")
mail_templates = os.listdir("mail_vorlagen")
log.info(f"Mail templates: {mail_templates}")
self.comboBox.clear()
for template in mail_templates:
self.comboBox.addItem(template)
# add these helpers inside Mail_Dialog
def get_greeting(self):
prof = self.profname.split(" ")[0]
if self.gender_male.isChecked():
@@ -124,45 +128,104 @@ Tel.: 0761/682-778 | 07617682-545"""
name = f"{self.profname.split(' ')[1]} {self.profname.split(' ')[0]}"
return f"Guten Tag {name},"
def _update_ok_button(self):
checked = (
self.gender_male.isChecked()
or self.gender_female.isChecked()
or self.gender_non.isChecked()
)
self.btn_okay.setEnabled(checked)
def _on_gender_toggled(self, checked: bool):
# Only refresh when a button becomes checked
if checked:
self.set_mail()
def open_new_template(self):
log.info("Opening new template dialog")
dialog = MailTemplateDialog()
dialog.updateSignal.connect(self.load_mail_templates)
dialog.exec()
def determine_signature(self):
# use equality, not identity
if (
config.mail.signature == empty_signature
or config.mail.signature.strip() == ""
):
return """Mit freundlichen Grüßen
Ihr Semesterapparatsteam
Mail: semesterapparate@ph-freiburg.de
Tel.: 0761/682-778 | 0761/682-545"""
else:
return config.mail.signature
def load_mail_templates(self):
log.info("Loading mail templates")
mail_templates = [
f for f in os.listdir("mail_vorlagen") if f.lower().endswith(".eml")
]
log.info(f"Mail templates: {mail_templates}")
self.comboBox.clear()
for template in mail_templates:
self.comboBox.addItem(template)
def set_mail(self):
log.info("Setting mail")
self._update_ok_button() # keep OK enabled state in sync
email_template = self.comboBox.currentText()
if email_template == "":
if not email_template:
log.debug("No mail template selected")
return
with open(f"mail_vorlagen/{email_template}", "r", encoding="utf-8") as f:
mail_template = f.read()
eml_text = f.read()
# header label for UI (unchanged)
email_header = email_template.split(".eml")[0]
if "{AppNr}" in email_template:
email_header = email_template.split(".eml")[0]
email_header = email_header.format(AppNr=self.appid, AppName=self.appname)
email_header = email_header.format(AppNr=self.appid, AppName=self.appname)
self.mail_header.setText(email_header)
self.mail_data = mail_template.split("<html>")[0]
mail_html = mail_template.split("<html>")[1]
mail_html = "<html>" + mail_html
Appname = self.appname
mail_html = mail_html.format(
Profname=self.profname.split(" ")[0],
Appname=Appname,
AppNr=self.appid,
AppSubject=self.subject,
greeting=self.get_greeting(),
signature=self.signature,
newEditions="<br>".join(
[
f"{book.title} von {book.author} (ISBN: {book.isbn}, Auflage: {book.edition}, In Bibliothek: {'ja' if getattr(book, 'library_location', 1) == 1 else 'nein'})"
for book in self.books
]
)
if self.books
else "keine neuen Auflagen gefunden",
)
self.mail_body.setHtml(mail_html)
headers, body_html = _split_eml_headers_body(eml_text)
body_html = _escape_braces_in_style(body_html)
# compute greeting from the current toggle selection
greeting = self.get_greeting()
try:
body_html = body_html.format(
Profname=self.profname.split(" ")[
0
], # last name if your template uses {Profname}
Appname=self.appname,
AppNr=self.appid,
AppSubject=self.subject,
greeting=greeting,
signature=self.signature,
newEditions="\n".join(
[
f"- {book.title} (ISBN: {','.join(book.isbn)}, Auflage: {book.edition if book.edition else 'nicht bekannt'}, In Bibliothek: {'ja' if getattr(book, 'signature', None) is not None and 'Handbibliothek' not in str(book.library_location) else 'nein'}, Typ: {book.get_book_type()}) Aktuelle Auflage: {book.old_book.edition if book.old_book and book.old_book.edition else 'nicht bekannt'}"
for book in (self.books or [])
]
)
if self.books
else "keine neuen Auflagen gefunden",
newEditionsOrdered="\n".join(
[
f" - {book.title}, ISBN: {','.join(book.isbn)}, Bibliotheksstandort : {book.library_location if book.library_location else 'N/A'}, Link: {book.link}"
for book in (self.ordered_books or [])
]
),
)
except Exception as e:
log.error(f"Template formatting failed: {e}")
self.mail_body.setPlainText(body_html)
def createAndSendMail(self):
log.info("Sending mail")
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
@@ -176,31 +239,29 @@ Tel.: 0761/682-778 | 07617682-545"""
message["From"] = sender_email
message["To"] = self.prof_mail
message["Subject"] = self.mail_header.text()
# include a Fcc to the senders sent folder
message["cc"] = "semesterapparate@ph-freiburg.de"
message["Cc"] = "semesterapparate@ph-freiburg.de"
mail_body = self.mail_body.toPlainText()
# strange_string = """p, li { white-space: pre-wrap; }
# hr { height: 1px; border-width: 0; }
# li.unchecked::marker { content: "\2610"; }
# li.checked::marker { content: "\2612"; }
# """
# mail_body.replace(strange_string, "")
message.attach(MIMEText(mail_body, "Plain", "utf-8"))
mail_body = self.mail_body.toHtml()
message.attach(MIMEText(mail_body, "html"))
mail = message.as_string()
with smtplib.SMTP_SSL(smtp_server, port) as server:
server.connect(smtp_server, port)
# server.connect(smtp_server, port)
# server.auth(mechanism="PLAIN")
server.connect(smtp_server, port) # not needed for SMTP_SSL
if config.mail.use_user_name is True:
# print(config["mail"]["user_name"])
server.login(config.mail.user_name, password)
else:
server.login(sender_email, password)
server.sendmail(sender_email, tolist, mail)
# print("Mail sent")
# end active process
server.quit()
pass
log.info("Mail sent, closing connection to server and dialog")
# close the dialog
self.accept()
@@ -225,8 +286,6 @@ def launch_gui(
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Mail_Dialog()

View File

@@ -7,7 +7,7 @@ from qtqdm import Qtqdm, QtqdmProgressBar
from src.logic import BookData
from src.logic.lehmannsapi import LehmannsClient
from src.logic.swb import SWB
from src.logic.SRU import SWB
class CheckThread(QtCore.QThread):

View File

@@ -250,7 +250,7 @@
<rect>
<x>0</x>
<y>180</y>
<width>1261</width>
<width>1412</width>
<height>511</height>
</rect>
</property>
@@ -275,11 +275,30 @@
</item>
<item>
<widget class="QCheckBox" name="chkbx_show_del_media">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>WIP - Broken</string>
</property>
<property name="text">
<string>gel. Medien anzeigen</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkbx_show_only_wit_neweditions">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>WIP - Broken</string>
</property>
<property name="text">
<string>Nur Titel mit Neuauflagen anzeigen</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
@@ -298,8 +317,11 @@
</item>
<item>
<widget class="QPushButton" name="btn_reserve">
<property name="toolTip">
<string>Dieser Knopf prüft alle Werke, die mit einem roten X vermerkt sind. Sollten diese inzwischen im Apparat sein, wird dies aktualisiert</string>
</property>
<property name="text">
<string>im Apparat?</string>
<string>Medien mit ❌ im Apparat?</string>
</property>
</widget>
</item>

View File

@@ -157,7 +157,7 @@ class Ui_MainWindow(object):
self.gridLayoutWidget_2 = QWidget(self.createApparat)
self.gridLayoutWidget_2.setObjectName(u"gridLayoutWidget_2")
self.gridLayoutWidget_2.setEnabled(True)
self.gridLayoutWidget_2.setGeometry(QRect(0, 180, 1261, 511))
self.gridLayoutWidget_2.setGeometry(QRect(0, 180, 1412, 511))
self.gridLayout_2 = QGridLayout(self.gridLayoutWidget_2)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
@@ -169,9 +169,16 @@ class Ui_MainWindow(object):
self.chkbx_show_del_media = QCheckBox(self.gridLayoutWidget_2)
self.chkbx_show_del_media.setObjectName(u"chkbx_show_del_media")
self.chkbx_show_del_media.setEnabled(False)
self.horizontalLayout_5.addWidget(self.chkbx_show_del_media)
self.chkbx_show_only_wit_neweditions = QCheckBox(self.gridLayoutWidget_2)
self.chkbx_show_only_wit_neweditions.setObjectName(u"chkbx_show_only_wit_neweditions")
self.chkbx_show_only_wit_neweditions.setEnabled(False)
self.horizontalLayout_5.addWidget(self.chkbx_show_only_wit_neweditions)
self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_3)
@@ -880,8 +887,18 @@ class Ui_MainWindow(object):
___qtablewidgetitem4.setText(QCoreApplication.translate("MainWindow", u"Dauerapparat", None));
___qtablewidgetitem5 = self.tableWidget_apparate.horizontalHeaderItem(5)
___qtablewidgetitem5.setText(QCoreApplication.translate("MainWindow", u"KontoNr", None));
#if QT_CONFIG(tooltip)
self.chkbx_show_del_media.setToolTip(QCoreApplication.translate("MainWindow", u"WIP - Broken", None))
#endif // QT_CONFIG(tooltip)
self.chkbx_show_del_media.setText(QCoreApplication.translate("MainWindow", u"gel. Medien anzeigen", None))
self.btn_reserve.setText(QCoreApplication.translate("MainWindow", u"im Apparat?", None))
#if QT_CONFIG(tooltip)
self.chkbx_show_only_wit_neweditions.setToolTip(QCoreApplication.translate("MainWindow", u"WIP - Broken", None))
#endif // QT_CONFIG(tooltip)
self.chkbx_show_only_wit_neweditions.setText(QCoreApplication.translate("MainWindow", u"Nur Titel mit Neuauflagen anzeigen", None))
#if QT_CONFIG(tooltip)
self.btn_reserve.setToolTip(QCoreApplication.translate("MainWindow", u"Dieser Knopf pr\u00fcft alle Werke, die mit einem roten X vermerkt sind. Sollten diese inzwischen im Apparat sein, wird dies aktualisiert", None))
#endif // QT_CONFIG(tooltip)
self.btn_reserve.setText(QCoreApplication.translate("MainWindow", u"Medien mit \u274c im Apparat?", None))
self.label_info.setText(QCoreApplication.translate("MainWindow", u"Medien werden hinzugef\u00fcgt", None))
self.progress_label.setText(QCoreApplication.translate("MainWindow", u"Medium x/y", None))
self.label_20.setText(QCoreApplication.translate("MainWindow", u"Medien werden gepr\u00fcft", None))

View File

@@ -26,7 +26,6 @@ from src.backend import (
)
from src.backend.create_file import recreateFile
from src.backend.delete_temp_contents import delete_temp_contents as tempdelete
from src.backend.semester import Semester
from src.logic import (
APP_NRS,
Apparat,
@@ -34,7 +33,9 @@ from src.logic import (
BookData,
Prof,
SemapDocument,
Semester,
csv_to_list,
eml_to_semap,
pdf_to_semap,
word_to_semap,
)
@@ -207,6 +208,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
self.progressBar.setMinimum(0)
self.avail_status.hide()
self.chkbx_show_del_media.hide()
self.chkbx_show_only_wit_neweditions.hide()
self.automation_add_selected_books.hide()
# self.btn_del_select_apparats.setEnabled(False)
@@ -896,7 +898,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
)
# thread = QThread()
appnumber = self.active_apparat
appnumber = self.drpdwn_app_nr.currentText()
# #log.debug(links)
self.availChecker = AvailChecker(links, appnumber, books=books)
# availcheck.moveToThread(thread)
@@ -939,16 +941,14 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
self.validate_semester()
def update_app_media_list(self):
deleted = 0 if not self.chkbx_show_del_media.isChecked() else 1
app_id = self.db.getId(self.app_name.text())
prof_id = self.db.getProfId(self.profdata)
books: list[dict[int, BookData, int]] = self.db.getBooks(
app_id, prof_id, deleted
)
books: list[dict[int, BookData, int]] = self.db.getBooks(app_id, prof_id, 0)
# # #log.debug(books)
# take the dataclass from the tuple
# booklist:list[BookData]=[book[0] for book in books]
self.tableWidget_apparat_media.clearContents()
self.tableWidget_apparat_media.setRowCount(0)
for book in books:
book["id"]
@@ -1198,6 +1198,8 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
self.db.addBookToDatabase(
bookdata=book, app_id=app_id, prof_id=prof_id
)
if file_type == "eml":
data = eml_to_semap(file)
self.update_app_media_list()
# #log.debug(len(signatures))
@@ -1590,8 +1592,8 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
mail_data = {
"prof_name": "Erwerbung",
"prof_mail": "erw@ph-freiburg.de",
"app_id": app_nr,
"app_name": self.db.getApparatName(app_id, prof_id),
"app_nr": app_nr,
"app_name": self.db.getApparatName(app_nr, prof_id),
}
orderDialog = NewEditionDialog(app_id, mail_data)
orderDialog.exec()
@@ -1672,12 +1674,13 @@ WHERE m.id = ?""",
newEditionChecker.exec()
accepted_books = newEditionChecker.accepted_books
print(accepted_books)
if accepted_books == []:
return
for book in accepted_books:
oldBookId = self.db.getBookIdByPPN(book.old_book.ppn)
apparats_id = self.db.getId(
self.db.getApparatNameByAppNr(book.old_book.library_location)
)
self.db.insertNewEdition(book, oldBookId, apparats_id)
pass
@@ -1763,11 +1766,17 @@ WHERE m.id = ?""",
apparat_add_action = QtGui.QAction("Zum Apparat hinzufügen")
apparat_move_action = QtGui.QAction("In Apparat verschieben")
apparat_copy_action = QtGui.QAction("In Apparat kopieren")
replace_old_editions = QtGui.QAction("Neuauflagen ersetzen")
apparatmenu = menu.addMenu("Apparate")
generalmenu = menu.addMenu("Allgemeines")
apparatmenu.addActions( # type: ignore
[apparat_add_action, apparat_copy_action, apparat_move_action]
[
apparat_add_action,
apparat_copy_action,
apparat_move_action,
replace_old_editions,
]
)
generalmenu.addActions([edit_action, delete_action, update_data_action]) # type: ignore
# disable apparat_add_action
@@ -1778,8 +1787,37 @@ WHERE m.id = ?""",
apparat_copy_action.triggered.connect(self.copy_to_apparat) # type: ignore
apparat_move_action.triggered.connect(self.move_to_apparat) # type: ignore
update_data_action.triggered.connect(self.update_data) # type: ignore
replace_old_editions.triggered.connect(self.replace_old_edition) # type: ignore
menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) # type: ignore
def replace_old_edition(self):
# open dialog
dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Neuauflagen:")
layout = QtWidgets.QVBoxLayout()
label = QtWidgets.QLabel("Folgende Medien haben Neuauflagen:")
layout.addWidget(label)
table = QtWidgets.QTableWidget()
table.setColumnCount(4)
table.setHorizontalHeaderLabels(["Titel", "Auflage", "Signatur", "Neues Werk"])
table.horizontalHeader().setStretchLastSection(True)
new_editions = self.db.getBooksWithNewEditions(
self.active_apparat,
)
for book in new_editions:
table.insertRow(0)
table.setItem(0, 0, QtWidgets.QTableWidgetItem(book[0].title))
table.setItem(0, 1, QtWidgets.QTableWidgetItem(str(book[0].edition)))
table.setItem(0, 2, QtWidgets.QTableWidgetItem(book[0].signature))
new_ed_data = (
f"{book[1].title} (Auflage {book[1].edition}, {book[1].signature})"
)
table.setItem(0, 3, QtWidgets.QTableWidgetItem(new_ed_data))
layout.addWidget(table)
dialog.setLayout(layout)
dialog.exec()
def update_data(self):
signatures = [
self.tableWidget_apparat_media.item(row, 1).text()

View File

@@ -1,9 +1,10 @@
from .widget_sources.admin_query_ui import Ui_Form
from PySide6 import QtCore, QtWidgets
from PySide6 import QtWidgets, QtCore
from src import Icon
from src.backend import Database
from .widget_sources. import Ui_Form
class AdminQueryWidget(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent=None):
@@ -22,7 +23,7 @@ class AdminQueryWidget(QtWidgets.QWidget, Ui_Form):
return
data = self.db.query_db(request_text)
print(data)
# print(data)
table_names = (
request_text.lower().split("select")[1].split("from")[0].split(",")
)

View File

@@ -7,8 +7,8 @@ from PySide6.QtCore import QDate
from PySide6.QtGui import QRegularExpressionValidator
from src import LOG_DIR, Icon
from src.backend import Database, Semester, recreateElsaFile
from src.logic import Prof, elsa_word_to_csv
from src.backend import Database, recreateElsaFile
from src.logic import Prof, Semester, elsa_word_to_csv
from src.ui.dialogs import ElsaAddEntry, popus_confirm
from src.ui.widgets.filepicker import FilePicker
from src.ui.widgets.graph import DataQtGraph

View File

@@ -8,7 +8,7 @@ from PySide6.QtCharts import QCategoryAxis, QChart, QChartView, QLineSeries, QVa
from PySide6.QtGui import QColor, QPainter, QPen
from src import LOG_DIR
from src.backend.semester import Semester
from src.logic.semester import Semester
log = loguru.logger
log.remove()

View File

@@ -1,4 +1,5 @@
import sys
from typing import List
import loguru
from natsort import natsorted
@@ -6,8 +7,9 @@ from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import Signal
from src import LOG_DIR
from src.backend import Database, Semester
from src.logic import BookData, Prof, custom_sort, sort_semesters_list
from src.backend import Database
from src.logic import BookData, Prof, Semester, custom_sort, sort_semesters_list
from src.logic.dataclass import Apparat
from src.ui.dialogs import ApparatExtendDialog, Mail_Dialog, ReminderDialog
from src.ui.widgets import DataQtGraph, StatusWidget
from src.ui.widgets.signature_update import UpdaterThread
@@ -343,8 +345,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
apparats = natsorted(appnrs)
apparats = [str(apparat) for apparat in apparats]
self.box_appnrs.addItems(apparats)
persons = self.db.getProfs()
persons = sorted(persons, key=lambda x: x.lastname)
persons: List[Prof] = sorted(self.db.getProfs(), key=lambda x: x.lastname)
self.box_person.addItems(
[f"{person.lastname}, {person.firstname}" for person in persons]
)
@@ -398,7 +399,12 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
selected_apparat_rows = []
for i in range(self.tableWidget.rowCount()):
if self.tableWidget.cellWidget(i, 0).isChecked():
selected_apparats.append(self.tableWidget.item(i, 2).text())
selected_apparats.append(
Apparat(
appnr=self.tableWidget.item(i, 2).text(),
name=self.tableWidget.item(i, 1).text(),
)
)
selected_apparat_rows.append(i)
# delete all selected apparats
# # ##print(selected_apparats)

View File

@@ -8,7 +8,7 @@ from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
from src import LOG_DIR
from src.backend.catalogue import Catalogue
from src.backend.database import Database
from src.logic.swb import SWB
from src.logic.SRU import SWB
from .widget_sources.admin_update_signatures_ui import Ui_Dialog