From d35b2e816ea2c993f95d63e0b7ebbcddf1262d69 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Mon, 22 Sep 2025 09:47:18 +0200 Subject: [PATCH] UI: refactor mail template dialog for plaintext handling, improve logging, and update UI elements --- src/ui/dialogs/mailTemplate.py | 160 ++++++++++------------------ src/ui/dialogs/newEdition.py | 113 ++++++++++++++++++++ src/ui/widgets/new_edition_check.py | 45 ++++++-- src/ui/widgets/searchPage.py | 13 ++- src/ui/widgets/signature_update.py | 130 +++++++++++++++++++--- src/ui/widgets/welcome_wizard.py | 28 +++-- 6 files changed, 342 insertions(+), 147 deletions(-) create mode 100644 src/ui/dialogs/newEdition.py diff --git a/src/ui/dialogs/mailTemplate.py b/src/ui/dialogs/mailTemplate.py index c8aae04..32c0dd5 100644 --- a/src/ui/dialogs/mailTemplate.py +++ b/src/ui/dialogs/mailTemplate.py @@ -1,23 +1,18 @@ import os +import re +import sys -from PySide6 import QtGui, QtWidgets, QtCore +from loguru import logger as log +from PySide6 import QtCore, QtWidgets from src import Icon from .dialog_sources import NewMailTemplateDesignerDialog -import sys -from loguru import logger as log - logger = log logger.remove() logger.add("logs/application.log", rotation="1 week", retention="1 month", enqueue=True) -log.add( - f"logs/mail.log", - enqueue=True, -) - -# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO") +log.add("logs/mail.log", enqueue=True) logger.add(sys.stdout) @@ -28,35 +23,34 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog): super().__init__(parent) self.setupUi(self) self.setWindowIcon(Icon("edit_note").icon) - self.setWindowTitle("Mailvorlage erstellen") + self.setWindowTitle("Mailvorlage erstellen (Text)") + + # placeholders UI self.placeholder_list.addItem("") self.placeholder_list.setCurrentText("") self.insertPlaceholder.clicked.connect(self.insert_placeholder) self.placeholder_list.currentTextChanged.connect(self.updateDescription) - self.bold.clicked.connect(self.setFontBold) - self.italic.clicked.connect(self.setTextFontItalic) - self.underlined.clicked.connect(self.setTextFontUnderline) - self.testTemplate.clicked.connect(self.test_template) - self.fontBox.currentFontChanged.connect(self.setCurrentFont) - self.fontSize.currentTextChanged.connect(self.setFontSize) - # buttonbox - # save button + + # formatting buttons (kept enabled for UX, but saving uses plain text) + + # buttons self.buttonBox.button( QtWidgets.QDialogButtonBox.StandardButton.Save ).clicked.connect(self.save_template) - # discard button self.buttonBox.button( QtWidgets.QDialogButtonBox.StandardButton.Discard ).clicked.connect(self.discard_changes) - # cancel button self.buttonBox.button( QtWidgets.QDialogButtonBox.StandardButton.Cancel ).clicked.connect(self.closeNow) - log.info("Mail template dialog setup complete") + log.info("Mail template dialog (plaintext) setup complete") + + def _normalize_newlines(self, s: str) -> str: + # Convert any CRLF/CR to LF then back to CRLF for .eml + s = s.replace("\\r\\n", "\\n").replace("\\r", "\\n") + return s def save_template(self): - # print("save triggered") - # create a dialog to ask for the name of the template dialog = QtWidgets.QInputDialog() dialog.setInputMode(QtWidgets.QInputDialog.InputMode.TextInput) dialog.setLabelText("Bitte geben Sie den Namen des Templates ein:") @@ -66,12 +60,11 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog): dialog.setWindowIcon(Icon("save").icon) save = dialog.exec() template_name = dialog.textValue() - log.info("Saving template") + log.info("Saving plaintext template") if template_name != "" and save == 1: template = template_name + ".eml" if template in os.listdir("mail_vorlagen"): log.error("Template already exists") - # warning dialog dialog = QtWidgets.QMessageBox() dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) Icon("warning", dialog) @@ -87,36 +80,34 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog): ret = dialog.exec() if ret == QtWidgets.QMessageBox.StandardButton.No: return - mail = f"""Subject: {self.subject.text()} -MIME-Version: 1.0 -Content-Type: text/html; charset="UTF-8" -Content-Transfer-Encoding: 8bit -{self.templateEdit.toHtml()}""" - html_head = """ - - - - """ - mail_base = mail.split("")[0] - mail_body = mail.split("")[1] - mail = mail_base + html_head + mail_body - mail = ( - mail.replace("<", "<") - .replace(">", ">") - .replace(""", '"') - .replace("&", "&") - ) - with open(f"mail_vorlagen/{template}", "w", encoding="utf-8") as f: - f.write(mail) + + # Build plaintext from editor + body_text = self.templateEdit.toPlainText() + body_text = self._normalize_newlines(body_text) + + # Build EML headers and payload (headers, then one blank line, then body) + mail_headers = f""" + Subject: {self.subject.text()} + """ + mail_body = body_text + + eml = mail_headers + "\n\n" + mail_body + eml = re.sub(r" +", " ", eml) # remove multiple spaces + eml = re.sub(r"\n +", "\n", eml) # + print(eml) + + with open( + f"mail_vorlagen/{template}", "w", encoding="utf-8", newline="" + ) as f: + f.write(eml) + self.updateSignal.emit() self.close() - log.success(f"Template {template} saved successfully") + # log.success(f"Template {template} saved successfully (plaintext)") else: - # warning dialog dialog = QtWidgets.QMessageBox() dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) dialog.setWindowIcon(Icon("warning").icon) - dialog.setText("Bitte geben Sie einen Namen für das Template ein.") dialog.setWindowTitle("Fehler beim Speichern") dialog.exec() @@ -145,52 +136,20 @@ Content-Transfer-Encoding: 8bit self.close() def updateDescription(self): - # print("update triggered") - # print(self.placeholder_list.currentText()) placeholders = { - "anrede": "Die Anrede beinhaltet sowohl Person als auch Sehr geehrte/r; dargestellt als: {greeting}", + "anrede": "Die Anrede inkl. 'Sehr geehrte/r' oder neutral; dargestellt als: {greeting}", "apparatsfach": "Das Fach, in welchem der Apparat angelegt wurde; dargestellt als: {AppSubject}", "apparatsname": "Der Name des Apparats; dargestellt als: {Appname}", "apparatsnummer": "Die Nummer des Apparats; dargestellt als: {AppNr}", "dozentname": "Der Name des Dozenten / der Dozentin; dargestellt als: {Profname}", - "signatur": "Die persönliche / allgemeine Signatur am ende der Mail; dargestellt als: {signature}", + "signatur": "Ihre Signatur; dargestellt als: {signature}", "": " ", } - for ( - key, - item, - ) in placeholders.items(): + for key, item in placeholders.items(): if key in self.placeholder_list.currentText().lower(): self.lineEdit.setText(item) break - def setCurrentFont(self): - font = self.fontBox.currentFont() - font.setPointSize(int(self.fontSize.currentText())) - self.templateEdit.setFont(font) - - def setFontSize(self): - size = self.fontSize.currentText() - self.templateEdit.setFontPointSize(int(size)) - - def setFontBold(self): - if self.bold.isChecked(): - self.templateEdit.setFontWeight(QtGui.QFont.Weight.Bold) - else: - self.templateEdit.setFontWeight(QtGui.QFont.Weight.Normal) - - def setTextFontItalic(self): - if self.italic.isChecked(): - self.templateEdit.setFontItalic(True) - else: - self.templateEdit.setFontItalic(False) - - def setTextFontUnderline(self): - if self.underlined.isChecked(): - self.templateEdit.setFontUnderline(True) - else: - self.templateEdit.setFontUnderline(False) - def test_template(self): placeholders = [ "{greeting}", @@ -201,35 +160,29 @@ Content-Transfer-Encoding: 8bit "{signature}", ] mail_subject = self.subject.text() - mail_body = self.templateEdit.toHtml() + mail_body = self.templateEdit.toPlainText() missing_body = [] missing_subject = [] try: - assert placeholders[2] in mail_subject + assert "{Appname}" in mail_subject or "{AppName}" in mail_subject except AssertionError: - missing_subject.append(placeholders[2]) - # check if all placeholders are in the mail body + missing_subject.append("{Appname}") for placeholder in placeholders: try: assert placeholder in mail_body except AssertionError: missing_body.append(placeholder) - if missing_body != []: - # warning dialog - Icon("template_fail", self.testTemplate) + if missing_body: dialog = QtWidgets.QMessageBox() dialog.setWindowIcon(Icon("warning").icon) - dialog.setText("Folgende Platzhalter fehlen im Template:") - missing = ( "Betreff:\n" - + "\n".join(missing_subject) - + "\n\n" + + "\\n".join(missing_subject) + + "\\n\\n" + "Mailtext:\n" - + "\n".join(missing_body) + + "\\n".join(missing_body) ) - dialog.setInformativeText(f"{missing}") dialog.setWindowTitle("Fehlende Platzhalter") dialog.exec() @@ -238,21 +191,16 @@ Content-Transfer-Encoding: 8bit self.testTemplate.setText("✔") def insert_placeholder(self): - placeholder = { + placeholder_map = { "anrede": "{greeting}", "apparatsfach": "{AppSubject}", "apparatsname": "{Appname}", "apparatsnummer": "{AppNr}", "dozentname": "{Profname}", - "signatur": """
-- 
-{signature}
-
""", + "signatur": "{signature}", } cursor = self.templateEdit.textCursor() - for ( - key, - item, - ) in placeholder.items(): + for key, item in placeholder_map.items(): if key in self.placeholder_list.currentText().lower(): cursor.insertText(item) break diff --git a/src/ui/dialogs/newEdition.py b/src/ui/dialogs/newEdition.py new file mode 100644 index 0000000..56acf5e --- /dev/null +++ b/src/ui/dialogs/newEdition.py @@ -0,0 +1,113 @@ +import sys + +import loguru +from PySide6 import QtCore, QtWidgets + +from src import LOG_DIR +from src.backend.database import Database +from src.backend.catalogue import Catalogue + +from src.ui.dialogs.mail import Mail_Dialog + +from .dialog_sources.order_neweditions_ui import Ui_Dialog + +log = loguru.logger +log.remove() +log.add(sys.stdout, level="INFO") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") + + +class NewEditionDialog(QtWidgets.QDialog, Ui_Dialog): + def __init__(self, app_id, mail_data): + super().__init__() + self.setupUi(self) + self.setWindowTitle("Neuauflagen bestellen") + self.db = Database() + self.catalogue = Catalogue() + self.app_id = app_id + self.mail_data = mail_data + self.books = self.db.getNewEditionsByApparat(app_id) + self.pushButton.clicked.connect(self.orderBooks) + self.populateTable() + + def populateTable(self): + for book in self.books: + signature = book.signature + if signature is None or signature == "None": + signature = self.catalogue.get_signature(book.ppn) + link_label = QtWidgets.QLabel() + link = ( + book.link + if book.link != "SWB" + else f"https://www.lehmanns.de/search/quick?mediatype_id=&q={book.isbn[0]}" + ) + + link_label.setText(f'Lehmanns.de') + link_label.setOpenExternalLinks(True) + link_label.setTextFormat(QtCore.Qt.TextFormat.RichText) + link_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + link_label.setTextInteractionFlags( + QtCore.Qt.TextInteractionFlag.TextBrowserInteraction + ) + self.tableWidget.insertRow(0) + # first column is checkbox for ordering + checkbox = QtWidgets.QCheckBox() + + checkbox.setChecked(False) + self.tableWidget.setCellWidget(0, 0, checkbox) + self.tableWidget.setItem( + 0, + 1, + QtWidgets.QTableWidgetItem( + str(book.signature if book.signature else "") + ), + ) + self.tableWidget.setItem(0, 2, QtWidgets.QTableWidgetItem(book.title)) + self.tableWidget.setItem( + 0, 3, QtWidgets.QTableWidgetItem(",".join(book.isbn)) + ) + self.tableWidget.setItem(0, 4, QtWidgets.QTableWidgetItem(book.author)) + self.tableWidget.setItem(0, 5, QtWidgets.QTableWidgetItem(book.edition)) + self.tableWidget.setItem(0, 6, QtWidgets.QTableWidgetItem(book.library_location)) + self.tableWidget.setCellWidget(0, 7, link_label) + + def orderBooks(self): + ordered_books = [] + for row in range(self.tableWidget.rowCount()): + checkbox = self.tableWidget.cellWidget(row, 0) + if checkbox.isChecked(): + book = self.books[row] + book.link = ( + book.link + if book.link != "SWB" + else f"https://www.lehmanns.de/search/quick?mediatype_id=&q={book.isbn[0]}" + ) + print(f"Bestelle Neuauflage für {book.title} ({book.edition})") + ordered_books.append(book) + # Process ordered_books as needed + # editionId = self.db.getNewEditionId(book) + # self.db.setOrdered(editionId) + + self.mail = Mail_Dialog( + app_id=self.mail_data.get("app_nr"), + prof_name=self.mail_data.get("prof_name"), + prof_mail=self.mail_data.get("prof_mail"), + app_name=self.mail_data.get("app_name"), + default_mail="Bitte um Bestellung", + ordered_books=ordered_books, + ) + self.mail.exec() + + +def launch(): + app = QtWidgets.QApplication.instance() + if app is None: + app = QtWidgets.QApplication([]) + mail_data = { + "prof_name": "Erwerbung", + "prof_mail": "carola.wiestler@ph-freiburg.de", + "app_nr": 131, + "app_name": "Beratung und Teamarbeit", + } + dialog = NewEditionDialog(app_id=18, mail_data=mail_data) + dialog.exec() diff --git a/src/ui/widgets/new_edition_check.py b/src/ui/widgets/new_edition_check.py index 4d94e27..787c138 100644 --- a/src/ui/widgets/new_edition_check.py +++ b/src/ui/widgets/new_edition_check.py @@ -3,6 +3,8 @@ from typing import List from PySide6 import QtWidgets from PySide6.QtCore import Qt +from src import Icon +from src.backend.catalogue import Catalogue from src.logic import BookData from .widget_sources.new_edition_check_book_ui import ( @@ -11,14 +13,20 @@ from .widget_sources.new_edition_check_book_ui import ( from .widget_sources.new_edition_check_found_result_ui import ( Ui_Dialog as Ui_NewEditionCheckFoundResult, ) +from .widget_sources.new_edition_check_selector_ui import ( + Ui_Dialog as Ui_NewEditionCheckSelector, +) from .widget_sources.new_edition_check_ui import Ui_Dialog as Ui_NewEditionCheck -from .widget_sources.new_edition_check_selector_ui import Ui_Dialog as Ui_NewEditionCheckSelector + +cat = Catalogue() + class NewEditionCheckSelector(QtWidgets.QDialog, Ui_NewEditionCheckSelector): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self.setWindowTitle("Neuauflagen prüfen") + self.setWindowIcon(Icon("confirm_type").icon) self.btn_apparat.clicked.connect(self.select_apparat) self.btn_prof.clicked.connect(self.select_professor) self.selection = None @@ -31,6 +39,7 @@ class NewEditionCheckSelector(QtWidgets.QDialog, Ui_NewEditionCheckSelector): self.selection = "professor" self.accept() + class NewEditionCheckFoundResult(QtWidgets.QDialog, Ui_NewEditionCheckFoundResult): def __init__(self, book: BookData, parent=None): assert isinstance(book, BookData) @@ -44,6 +53,7 @@ class NewEditionCheckFoundResult(QtWidgets.QDialog, Ui_NewEditionCheckFoundResul self.line_publisher.setText(self.book.publisher if self.book.publisher else "") self.line_year.setText(self.book.year if self.book.year else "") self.line_pages.setText(self.book.pages if self.book.pages else "") + self.line_author.setText(self.book.author if self.book.author else "") link = self.book.link if self.book.link else "" if self.book.link != "SWB": link = f"Lehmanns" @@ -66,7 +76,17 @@ class NewEditionCheckFoundResult(QtWidgets.QDialog, Ui_NewEditionCheckFoundResul self.in_library.setText( "Diese Neuauflage ist bereits in der Bibliothek vorhanden." ) - self.book.library_location = 1 + self.book.link == f"https://www.lehmanns.de/search/quick?mediatype_id=&q={self.book.isbn[0]}" + if ( + self.book.link == "SWB" + and self.book.signature is not None + and self.book.signature != "" + and self.book.library_location not in (0, "0", None) + ): + self.in_library.setText( + f"Diese Neuauflage ist bereits in der Bibliothek vorhanden, und an diesem Standort: {self.book.library_location}." + ) + f"https://www.lehmanns.de/search/quick?mediatype_id=&q={self.book.isbn[0]}" pass @@ -78,7 +98,10 @@ class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook): self.book = book self.accepted_books = [] self.responses = responses - self.line_author.setText(self.book.author) + author = self.book.author if self.book.author else cat.get_author(self.book.ppn) + if self.book.author is None and author is not None: + self.book.author = author + self.line_author.setText(author if author else "") self.line_title.setText(self.book.title) self.line_ppn.setText(self.book.ppn if self.book.ppn else "") self.line_signature.setText(self.book.signature if self.book.signature else "") @@ -103,6 +126,9 @@ class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook): self.label_book_index.setText(f"1 / {self.stackedWidget.count()}") self.btn_next.clicked.connect(self.next) self.btn_prev.clicked.connect(self.previous) + if self.stackedWidget.count() <= 1: + self.btn_next.hide() + self.btn_prev.hide() def next(self): index = self.stackedWidget.currentIndex() @@ -110,6 +136,7 @@ class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook): index += 1 self.stackedWidget.setCurrentIndex(index) self.label_book_index.setText(f"{index + 1} / {self.stackedWidget.count()}") + self.btn_prev.show() if index == self.stackedWidget.count() - 1: self.btn_next.hide() @@ -119,6 +146,9 @@ class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook): index -= 1 self.stackedWidget.setCurrentIndex(index) self.label_book_index.setText(f"{index + 1} / {self.stackedWidget.count()}") + self.btn_next.show() + if index == 0: + self.btn_prev.hide() if index < self.stackedWidget.count() - 1: self.btn_next.show() @@ -130,7 +160,8 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck): super().__init__(parent) self.setupUi(self) self.results = results - self.setWindowTitle("Prüfung auf Neuauflagen") + self.setWindowIcon(Icon("results").icon) + self.setWindowTitle(f"Neuauflagen prüfen ({len(self.results)})") # remove pages from stacked widget for _ in range(self.stackedWidget.count()): widget = self.stackedWidget.widget(0) @@ -138,6 +169,7 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck): widget.deleteLater() for resultset in self.results: book, responses = resultset + # print(book, responses) self.stackedWidget.addWidget( NewEditionCheckBook(parent=self, book=book, responses=responses) ) @@ -170,7 +202,7 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck): self.progressBar.setValue(index + 1) def accept(self) -> None: - print("finished checking for new editions") + # print("finished checking for new editions") accepted_books = [] for i in range(self.stackedWidget.count()): book_widget = self.stackedWidget.widget(i) @@ -179,7 +211,8 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck): found_widget = book_widget.stackedWidget.widget(j) if isinstance(found_widget, NewEditionCheckFoundResult): if found_widget.checkBox.isChecked(): + found_widget.book.old_book = book_widget.book accepted_books.append(found_widget.book) super().accept() - print("accepted", len(accepted_books), "new editions") + # print("accepted", len(accepted_books), "new editions") self.accepted_books = accepted_books diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 2948a77..63992a0 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -194,7 +194,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): appnr = self.tableWidget.item(tableposition, 2).text() if reminder.result() == QtWidgets.QDialog.DialogCode.Accepted: data = reminder.return_message() - # #print(data) + # ##print(data) self.db.addMessage( data, "admin", @@ -222,7 +222,6 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): params = {key: value for key, value in params.items() if value is not None} log.info(params) retdata = self.db.searchBook(params) - log.info(retdata) if retdata == [] or retdata is None: self.no_result.setText("Keine Ergebnisse gefunden") return @@ -235,7 +234,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): self.book_search_result.setItem( 0, 1, QtWidgets.QTableWidgetItem(book[0].signature) ) - # #print(book[1]) + # ##print(book[1]) self.book_search_result.setItem( 0, 2, @@ -281,7 +280,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): self.btn_notify_for_deletion.setEnabled(False) def setStatisticTableSize(self): - # # #print(self.statistics_table.size(), self.statistics_table.rowCount()) + # # ##print(self.statistics_table.size(), self.statistics_table.rowCount()) size = self.statistics_table.size() h = size.height() w = size.width() @@ -402,7 +401,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): selected_apparats.append(self.tableWidget.item(i, 2).text()) selected_apparat_rows.append(i) # delete all selected apparats - # # #print(selected_apparats) + # # ##print(selected_apparats) log.info(f"Deleting apparats: {selected_apparats}") for apparat in selected_apparats: self.db.deleteApparat(apparat, self.semester) @@ -594,13 +593,13 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): "Dieser Semesterapparat kann nicht gelöscht werden, da er bereits gelöscht wurde" ) elif parent_depth == 2: - # #print("depth", parent_depth) + # ##print("depth", parent_depth) # apparat selected case - open the apparat in the frame self.apparat_open.emit(apparat) return def emit_signal(self, *args): - # #print("emit_signal", *args) + # ##print("emit_signal", *args) self.apparat_open.emit(args[1]) diff --git a/src/ui/widgets/signature_update.py b/src/ui/widgets/signature_update.py index fc3cb5d..135eeae 100644 --- a/src/ui/widgets/signature_update.py +++ b/src/ui/widgets/signature_update.py @@ -1,10 +1,28 @@ -from PySide6 import QtCore, QtWidgets +import sys +from datetime import datetime +import loguru +from PySide6 import QtCore, QtWidgets +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 .widget_sources.admin_update_signatures_ui import Ui_Dialog +log = loguru.logger +log.remove() +log.add(sys.stdout, level="INFO") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") + +log.add( + f"{LOG_DIR}/{datetime.now().strftime('%Y-%m-%d')}.log", + rotation="1 day", + retention="1 month", +) + class UpdaterThread(QtCore.QThread): progress = QtCore.Signal(int) @@ -19,17 +37,66 @@ class UpdaterThread(QtCore.QThread): def run(self): total_books = len(self.books) for index, book in enumerate(self.books): - id = book["id"] - bookdata = book["bookdata"] - ppn = bookdata.link.split("kid=")[-1] - result = self.catalogue.get_book(ppn) - if result: - bookdata.signature = result.signature - print(bookdata) - self.db.updateBookdata(id, bookdata) - else: - print(f"No result for {ppn}") - self.db.deleteBook(id) + try: + id = book["id"] + bookdata = book["bookdata"] + ppn = bookdata.ppn + result = self.catalogue.get_book(ppn) + if result: + log.debug(f"Updating book {id} with ppn {ppn}") + bookdata.signature = result.signature + # #print(bookdata) + self.db.updateBookdata(id, bookdata) + else: + log.debug(f"No result for {ppn}") + # #print(f"No result for {ppn}") + # self.db.deleteBook(id) + except Exception as e: + log.error(f"Error updating book {book}: {e}") + self.progress.emit(index + 1) + self.currtot.emit(index + 1, total_books) + + +class CompleterThread(QtCore.QThread): + progress = QtCore.Signal(int) + currtot = QtCore.Signal(int, int) + + def __init__(self, books=None): + super().__init__() + self.books = books + self.db = Database() + self.catalogue = Catalogue() + self.swb = SWB() + + def run(self): + total_books = len(self.books) + for index, book in enumerate(self.books): + try: + id = book["id"] + bookdata = book["bookdata"] + + ppn = bookdata.ppn + cat_book = self.catalogue.get_book(f"kid:{ppn}") + + swb_version = self.swb.getBooks(["pica.bib=20735", f"pica.ppn={ppn}"])[ + 0 + ] + if cat_book: + merged = cat_book.merge(swb_version) + else: + merged = swb_version + + # compare original_book with merged, if different, update db + if bookdata != merged: + # #print(f"Updating book {id} with ppn {ppn}") + # #print("Original book:", bookdata) + # #print("Merged book:", merged) + self.db.updateBookdata(id, merged) + except Exception as e: + log.error(f"Error updating book {book}: {e}") + # else: + # #print(f"No result for {ppn}") + # self.db.deleteBook(id) self.progress.emit(index + 1) self.currtot.emit(index + 1, total_books) @@ -40,20 +107,49 @@ class UpdateSignatures(QtWidgets.QDialog, Ui_Dialog): self.setupUi(self) self.setWindowTitle("Updating signatures...") self.progressBar.setValue(0) - self.pushButton.clicked.connect(self.start) + self.btn_update_signatures.clicked.connect(self.update_signatures) + self.btn_add_missing_data.clicked.connect(self.add_missing) self.db = Database() self.catalogue = Catalogue() + self.player = QMediaPlayer() + self.audio_output = QAudioOutput() - def start(self): + def play_sound(self, sound_file: str): + self.player.setAudioOutput(self.audio_output) + self.audio_output.setVolume(50) + self.player.setSource(QtCore.QUrl.fromLocalFile(f"src/sounds/{sound_file}")) + self.player.play() + + def update_signatures(self): books = self.db.getAllBooks() total_books = len(books) self.progressBar.setMaximum(total_books) self.updater = UpdaterThread(books) self.updater.progress.connect(self.update_progress) + self.updater.finished.connect(self.updater.deleteLater) self.updater.start() + def add_missing(self): + books = self.db.getAllBooks() + total_books = len(books) + self.progressBar.setMaximum(total_books) + incomplete_books = [ + book + for book in books + if any( + value in (None, "", "None") + for value in book["bookdata"].__dict__.values() + ) + ] + self.completionist = CompleterThread(incomplete_books) + self.completionist.progress.connect(self.update_progress) + self.completionist.finished.connect(self.completionist.deleteLater) + self.completionist.start() + def update_progress(self, value): self.progressBar.setValue(value) - if value >= self.progressBar.maximum(): - self.pushButton.setText("Done") - self.pushButton.setEnabled(False) + if value <= self.progressBar.maximum(): + self.btn_update_signatures.setEnabled(False) + if value == self.progressBar.maximum(): + self.btn_update_signatures.setEnabled(True) + self.play_sound("ding.mp3") diff --git a/src/ui/widgets/welcome_wizard.py b/src/ui/widgets/welcome_wizard.py index 571ce01..a8f47f4 100644 --- a/src/ui/widgets/welcome_wizard.py +++ b/src/ui/widgets/welcome_wizard.py @@ -1,12 +1,15 @@ -from typing import Any -from .widget_sources.welcome_wizard_ui import Ui_Wizard -from PySide6 import QtWidgets, QtCore, QtGui -from src import settings, LOG_DIR -from src.backend import Database import sys -from appdirs import AppDirs from pathlib import Path +from typing import Any + import loguru +from appdirs import AppDirs +from PySide6 import QtCore, QtWidgets + +from src import LOG_DIR, settings +from src.backend import Database + +from .widget_sources.welcome_wizard_ui import Ui_Wizard appdirs = AppDirs("SemesterApparatsManager", "SAM") @@ -193,15 +196,17 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): settings.set_openai_attr("api_key", openai_api_key) settings.set_openai_attr("model", openai_model) # save settings to file - print("Saving settings...") + # print("Saving settings...") settings.save() def open_database_settings(self): - #open filepicker dialog to select database file folder + # open filepicker dialog to select database file folder file_dialog = QtWidgets.QFileDialog(self, "Select Database File") file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory) file_dialog.setViewMode(QtWidgets.QFileDialog.ViewMode.List) - file_dialog.setWindowFlags(file_dialog.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint) + file_dialog.setWindowFlags( + file_dialog.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint + ) # set start dir to appdir.user_data_dir file_dialog.setDirectory(str(appdirs.user_data_dir)) if file_dialog.exec(): @@ -212,7 +217,7 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): self.settings_database.setText(db_path) def open_temp_settings(self): - #open filepicker dialog to select temporary directory + # open filepicker dialog to select temporary directory dir_dialog = QtWidgets.QFileDialog(self, "Select Temporary Directory") dir_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory) dir_dialog.setViewMode(QtWidgets.QFileDialog.ViewMode.List) @@ -224,6 +229,7 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): temp_path = selected_dirs[0] self.settings_temp.setText(temp_path) + def launch_wizard(): """Launch the welcome wizard.""" app = QtWidgets.QApplication.instance() @@ -235,4 +241,4 @@ def launch_wizard(): wizard.setWizardStyle(QtWidgets.QWizard.WizardStyle.ModernStyle) wizard.setStartId(0) wizard.show() - return wizard.exec() \ No newline at end of file + return wizard.exec()