UI: refactor mail template dialog for plaintext handling, improve logging, and update UI elements

This commit is contained in:
2025-09-22 09:47:18 +02:00
parent 11d5d67538
commit d35b2e816e
6 changed files with 342 additions and 147 deletions

View File

@@ -1,23 +1,18 @@
import os 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 src import Icon
from .dialog_sources import NewMailTemplateDesignerDialog from .dialog_sources import NewMailTemplateDesignerDialog
import sys
from loguru import logger as log
logger = log logger = log
logger.remove() logger.remove()
logger.add("logs/application.log", rotation="1 week", retention="1 month", enqueue=True) logger.add("logs/application.log", rotation="1 week", retention="1 month", enqueue=True)
log.add( log.add("logs/mail.log", enqueue=True)
f"logs/mail.log",
enqueue=True,
)
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
logger.add(sys.stdout) logger.add(sys.stdout)
@@ -28,35 +23,34 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.setWindowIcon(Icon("edit_note").icon) self.setWindowIcon(Icon("edit_note").icon)
self.setWindowTitle("Mailvorlage erstellen") self.setWindowTitle("Mailvorlage erstellen (Text)")
# placeholders UI
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.setCurrentText("") self.placeholder_list.setCurrentText("")
self.insertPlaceholder.clicked.connect(self.insert_placeholder) self.insertPlaceholder.clicked.connect(self.insert_placeholder)
self.placeholder_list.currentTextChanged.connect(self.updateDescription) self.placeholder_list.currentTextChanged.connect(self.updateDescription)
self.bold.clicked.connect(self.setFontBold)
self.italic.clicked.connect(self.setTextFontItalic) # formatting buttons (kept enabled for UX, but saving uses plain text)
self.underlined.clicked.connect(self.setTextFontUnderline)
self.testTemplate.clicked.connect(self.test_template) # buttons
self.fontBox.currentFontChanged.connect(self.setCurrentFont)
self.fontSize.currentTextChanged.connect(self.setFontSize)
# buttonbox
# save button
self.buttonBox.button( self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Save QtWidgets.QDialogButtonBox.StandardButton.Save
).clicked.connect(self.save_template) ).clicked.connect(self.save_template)
# discard button
self.buttonBox.button( self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Discard QtWidgets.QDialogButtonBox.StandardButton.Discard
).clicked.connect(self.discard_changes) ).clicked.connect(self.discard_changes)
# cancel button
self.buttonBox.button( self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Cancel QtWidgets.QDialogButtonBox.StandardButton.Cancel
).clicked.connect(self.closeNow) ).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): def save_template(self):
# print("save triggered")
# create a dialog to ask for the name of the template
dialog = QtWidgets.QInputDialog() dialog = QtWidgets.QInputDialog()
dialog.setInputMode(QtWidgets.QInputDialog.InputMode.TextInput) dialog.setInputMode(QtWidgets.QInputDialog.InputMode.TextInput)
dialog.setLabelText("Bitte geben Sie den Namen des Templates ein:") dialog.setLabelText("Bitte geben Sie den Namen des Templates ein:")
@@ -66,12 +60,11 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog):
dialog.setWindowIcon(Icon("save").icon) dialog.setWindowIcon(Icon("save").icon)
save = dialog.exec() save = dialog.exec()
template_name = dialog.textValue() template_name = dialog.textValue()
log.info("Saving template") log.info("Saving plaintext template")
if template_name != "" and save == 1: if template_name != "" and save == 1:
template = template_name + ".eml" template = template_name + ".eml"
if template in os.listdir("mail_vorlagen"): if template in os.listdir("mail_vorlagen"):
log.error("Template already exists") log.error("Template already exists")
# warning dialog
dialog = QtWidgets.QMessageBox() dialog = QtWidgets.QMessageBox()
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
Icon("warning", dialog) Icon("warning", dialog)
@@ -87,36 +80,34 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog):
ret = dialog.exec() ret = dialog.exec()
if ret == QtWidgets.QMessageBox.StandardButton.No: if ret == QtWidgets.QMessageBox.StandardButton.No:
return return
mail = f"""Subject: {self.subject.text()}
MIME-Version: 1.0 # Build plaintext from editor
Content-Type: text/html; charset="UTF-8" body_text = self.templateEdit.toPlainText()
Content-Transfer-Encoding: 8bit body_text = self._normalize_newlines(body_text)
{self.templateEdit.toHtml()}"""
html_head = """<html> # Build EML headers and payload (headers, then one blank line, then body)
<head> mail_headers = f"""
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> Subject: {self.subject.text()}
</head>
""" """
mail_base = mail.split("<html>")[0] mail_body = body_text
mail_body = mail.split("</head>")[1]
mail = mail_base + html_head + mail_body eml = mail_headers + "\n\n" + mail_body
mail = ( eml = re.sub(r" +", " ", eml) # remove multiple spaces
mail.replace("&lt;", "<") eml = re.sub(r"\n +", "\n", eml) #
.replace("&gt;", ">") print(eml)
.replace("&quot;", '"')
.replace("&amp;", "&") with open(
) f"mail_vorlagen/{template}", "w", encoding="utf-8", newline=""
with open(f"mail_vorlagen/{template}", "w", encoding="utf-8") as f: ) as f:
f.write(mail) f.write(eml)
self.updateSignal.emit() self.updateSignal.emit()
self.close() self.close()
log.success(f"Template {template} saved successfully") # log.success(f"Template {template} saved successfully (plaintext)")
else: else:
# warning dialog
dialog = QtWidgets.QMessageBox() dialog = QtWidgets.QMessageBox()
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
dialog.setWindowIcon(Icon("warning").icon) dialog.setWindowIcon(Icon("warning").icon)
dialog.setText("Bitte geben Sie einen Namen für das Template ein.") dialog.setText("Bitte geben Sie einen Namen für das Template ein.")
dialog.setWindowTitle("Fehler beim Speichern") dialog.setWindowTitle("Fehler beim Speichern")
dialog.exec() dialog.exec()
@@ -145,52 +136,20 @@ Content-Transfer-Encoding: 8bit
self.close() self.close()
def updateDescription(self): def updateDescription(self):
# print("update triggered")
# print(self.placeholder_list.currentText())
placeholders = { 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}", "apparatsfach": "Das Fach, in welchem der Apparat angelegt wurde; dargestellt als: {AppSubject}",
"apparatsname": "Der Name des Apparats; dargestellt als: {Appname}", "apparatsname": "Der Name des Apparats; dargestellt als: {Appname}",
"apparatsnummer": "Die Nummer des Apparats; dargestellt als: {AppNr}", "apparatsnummer": "Die Nummer des Apparats; dargestellt als: {AppNr}",
"dozentname": "Der Name des Dozenten / der Dozentin; dargestellt als: {Profname}", "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 ( for key, item in placeholders.items():
key,
item,
) in placeholders.items():
if key in self.placeholder_list.currentText().lower(): if key in self.placeholder_list.currentText().lower():
self.lineEdit.setText(item) self.lineEdit.setText(item)
break 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): def test_template(self):
placeholders = [ placeholders = [
"{greeting}", "{greeting}",
@@ -201,35 +160,29 @@ Content-Transfer-Encoding: 8bit
"{signature}", "{signature}",
] ]
mail_subject = self.subject.text() mail_subject = self.subject.text()
mail_body = self.templateEdit.toHtml() mail_body = self.templateEdit.toPlainText()
missing_body = [] missing_body = []
missing_subject = [] missing_subject = []
try: try:
assert placeholders[2] in mail_subject assert "{Appname}" in mail_subject or "{AppName}" in mail_subject
except AssertionError: except AssertionError:
missing_subject.append(placeholders[2]) missing_subject.append("{Appname}")
# check if all placeholders are in the mail body
for placeholder in placeholders: for placeholder in placeholders:
try: try:
assert placeholder in mail_body assert placeholder in mail_body
except AssertionError: except AssertionError:
missing_body.append(placeholder) missing_body.append(placeholder)
if missing_body != []: if missing_body:
# warning dialog
Icon("template_fail", self.testTemplate)
dialog = QtWidgets.QMessageBox() dialog = QtWidgets.QMessageBox()
dialog.setWindowIcon(Icon("warning").icon) dialog.setWindowIcon(Icon("warning").icon)
dialog.setText("Folgende Platzhalter fehlen im Template:") dialog.setText("Folgende Platzhalter fehlen im Template:")
missing = ( missing = (
"Betreff:\n" "Betreff:\n"
+ "\n".join(missing_subject) + "\\n".join(missing_subject)
+ "\n\n" + "\\n\\n"
+ "Mailtext:\n" + "Mailtext:\n"
+ "\n".join(missing_body) + "\\n".join(missing_body)
) )
dialog.setInformativeText(f"{missing}") dialog.setInformativeText(f"{missing}")
dialog.setWindowTitle("Fehlende Platzhalter") dialog.setWindowTitle("Fehlende Platzhalter")
dialog.exec() dialog.exec()
@@ -238,21 +191,16 @@ Content-Transfer-Encoding: 8bit
self.testTemplate.setText("") self.testTemplate.setText("")
def insert_placeholder(self): def insert_placeholder(self):
placeholder = { placeholder_map = {
"anrede": "{greeting}", "anrede": "{greeting}",
"apparatsfach": "{AppSubject}", "apparatsfach": "{AppSubject}",
"apparatsname": "{Appname}", "apparatsname": "{Appname}",
"apparatsnummer": "{AppNr}", "apparatsnummer": "{AppNr}",
"dozentname": "{Profname}", "dozentname": "{Profname}",
"signatur": """<pre class="moz-signature" cols="72">-- "signatur": "{signature}",
{signature}
</pre>""",
} }
cursor = self.templateEdit.textCursor() cursor = self.templateEdit.textCursor()
for ( for key, item in placeholder_map.items():
key,
item,
) in placeholder.items():
if key in self.placeholder_list.currentText().lower(): if key in self.placeholder_list.currentText().lower():
cursor.insertText(item) cursor.insertText(item)
break break

View File

@@ -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'<a href="{link}">Lehmanns.de</a>')
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()

View File

@@ -3,6 +3,8 @@ from typing import List
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from src import Icon
from src.backend.catalogue import Catalogue
from src.logic import BookData from src.logic import BookData
from .widget_sources.new_edition_check_book_ui import ( 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 ( from .widget_sources.new_edition_check_found_result_ui import (
Ui_Dialog as Ui_NewEditionCheckFoundResult, 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_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): class NewEditionCheckSelector(QtWidgets.QDialog, Ui_NewEditionCheckSelector):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.setWindowTitle("Neuauflagen prüfen") self.setWindowTitle("Neuauflagen prüfen")
self.setWindowIcon(Icon("confirm_type").icon)
self.btn_apparat.clicked.connect(self.select_apparat) self.btn_apparat.clicked.connect(self.select_apparat)
self.btn_prof.clicked.connect(self.select_professor) self.btn_prof.clicked.connect(self.select_professor)
self.selection = None self.selection = None
@@ -31,6 +39,7 @@ class NewEditionCheckSelector(QtWidgets.QDialog, Ui_NewEditionCheckSelector):
self.selection = "professor" self.selection = "professor"
self.accept() self.accept()
class NewEditionCheckFoundResult(QtWidgets.QDialog, Ui_NewEditionCheckFoundResult): class NewEditionCheckFoundResult(QtWidgets.QDialog, Ui_NewEditionCheckFoundResult):
def __init__(self, book: BookData, parent=None): def __init__(self, book: BookData, parent=None):
assert isinstance(book, BookData) 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_publisher.setText(self.book.publisher if self.book.publisher else "")
self.line_year.setText(self.book.year if self.book.year 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_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 "" link = self.book.link if self.book.link else ""
if self.book.link != "SWB": if self.book.link != "SWB":
link = f"<a href='{link}'>Lehmanns</a>" link = f"<a href='{link}'>Lehmanns</a>"
@@ -66,7 +76,17 @@ class NewEditionCheckFoundResult(QtWidgets.QDialog, Ui_NewEditionCheckFoundResul
self.in_library.setText( self.in_library.setText(
"Diese Neuauflage ist bereits in der Bibliothek vorhanden." "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 pass
@@ -78,7 +98,10 @@ class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook):
self.book = book self.book = book
self.accepted_books = [] self.accepted_books = []
self.responses = responses 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_title.setText(self.book.title)
self.line_ppn.setText(self.book.ppn if self.book.ppn else "") self.line_ppn.setText(self.book.ppn if self.book.ppn else "")
self.line_signature.setText(self.book.signature if self.book.signature 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.label_book_index.setText(f"1 / {self.stackedWidget.count()}")
self.btn_next.clicked.connect(self.next) self.btn_next.clicked.connect(self.next)
self.btn_prev.clicked.connect(self.previous) self.btn_prev.clicked.connect(self.previous)
if self.stackedWidget.count() <= 1:
self.btn_next.hide()
self.btn_prev.hide()
def next(self): def next(self):
index = self.stackedWidget.currentIndex() index = self.stackedWidget.currentIndex()
@@ -110,6 +136,7 @@ class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook):
index += 1 index += 1
self.stackedWidget.setCurrentIndex(index) self.stackedWidget.setCurrentIndex(index)
self.label_book_index.setText(f"{index + 1} / {self.stackedWidget.count()}") self.label_book_index.setText(f"{index + 1} / {self.stackedWidget.count()}")
self.btn_prev.show()
if index == self.stackedWidget.count() - 1: if index == self.stackedWidget.count() - 1:
self.btn_next.hide() self.btn_next.hide()
@@ -119,6 +146,9 @@ class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook):
index -= 1 index -= 1
self.stackedWidget.setCurrentIndex(index) self.stackedWidget.setCurrentIndex(index)
self.label_book_index.setText(f"{index + 1} / {self.stackedWidget.count()}") 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: if index < self.stackedWidget.count() - 1:
self.btn_next.show() self.btn_next.show()
@@ -130,7 +160,8 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.results = results 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 # remove pages from stacked widget
for _ in range(self.stackedWidget.count()): for _ in range(self.stackedWidget.count()):
widget = self.stackedWidget.widget(0) widget = self.stackedWidget.widget(0)
@@ -138,6 +169,7 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck):
widget.deleteLater() widget.deleteLater()
for resultset in self.results: for resultset in self.results:
book, responses = resultset book, responses = resultset
# print(book, responses)
self.stackedWidget.addWidget( self.stackedWidget.addWidget(
NewEditionCheckBook(parent=self, book=book, responses=responses) NewEditionCheckBook(parent=self, book=book, responses=responses)
) )
@@ -170,7 +202,7 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck):
self.progressBar.setValue(index + 1) self.progressBar.setValue(index + 1)
def accept(self) -> None: def accept(self) -> None:
print("finished checking for new editions") # print("finished checking for new editions")
accepted_books = [] accepted_books = []
for i in range(self.stackedWidget.count()): for i in range(self.stackedWidget.count()):
book_widget = self.stackedWidget.widget(i) book_widget = self.stackedWidget.widget(i)
@@ -179,7 +211,8 @@ class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck):
found_widget = book_widget.stackedWidget.widget(j) found_widget = book_widget.stackedWidget.widget(j)
if isinstance(found_widget, NewEditionCheckFoundResult): if isinstance(found_widget, NewEditionCheckFoundResult):
if found_widget.checkBox.isChecked(): if found_widget.checkBox.isChecked():
found_widget.book.old_book = book_widget.book
accepted_books.append(found_widget.book) accepted_books.append(found_widget.book)
super().accept() super().accept()
print("accepted", len(accepted_books), "new editions") # print("accepted", len(accepted_books), "new editions")
self.accepted_books = accepted_books self.accepted_books = accepted_books

View File

@@ -194,7 +194,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
appnr = self.tableWidget.item(tableposition, 2).text() appnr = self.tableWidget.item(tableposition, 2).text()
if reminder.result() == QtWidgets.QDialog.DialogCode.Accepted: if reminder.result() == QtWidgets.QDialog.DialogCode.Accepted:
data = reminder.return_message() data = reminder.return_message()
# #print(data) # ##print(data)
self.db.addMessage( self.db.addMessage(
data, data,
"admin", "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} params = {key: value for key, value in params.items() if value is not None}
log.info(params) log.info(params)
retdata = self.db.searchBook(params) retdata = self.db.searchBook(params)
log.info(retdata)
if retdata == [] or retdata is None: if retdata == [] or retdata is None:
self.no_result.setText("Keine Ergebnisse gefunden") self.no_result.setText("Keine Ergebnisse gefunden")
return return
@@ -235,7 +234,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
self.book_search_result.setItem( self.book_search_result.setItem(
0, 1, QtWidgets.QTableWidgetItem(book[0].signature) 0, 1, QtWidgets.QTableWidgetItem(book[0].signature)
) )
# #print(book[1]) # ##print(book[1])
self.book_search_result.setItem( self.book_search_result.setItem(
0, 0,
2, 2,
@@ -281,7 +280,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
self.btn_notify_for_deletion.setEnabled(False) self.btn_notify_for_deletion.setEnabled(False)
def setStatisticTableSize(self): 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() size = self.statistics_table.size()
h = size.height() h = size.height()
w = size.width() w = size.width()
@@ -402,7 +401,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
selected_apparats.append(self.tableWidget.item(i, 2).text()) selected_apparats.append(self.tableWidget.item(i, 2).text())
selected_apparat_rows.append(i) selected_apparat_rows.append(i)
# delete all selected apparats # delete all selected apparats
# # #print(selected_apparats) # # ##print(selected_apparats)
log.info(f"Deleting apparats: {selected_apparats}") log.info(f"Deleting apparats: {selected_apparats}")
for apparat in selected_apparats: for apparat in selected_apparats:
self.db.deleteApparat(apparat, self.semester) 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" "Dieser Semesterapparat kann nicht gelöscht werden, da er bereits gelöscht wurde"
) )
elif parent_depth == 2: elif parent_depth == 2:
# #print("depth", parent_depth) # ##print("depth", parent_depth)
# apparat selected case - open the apparat in the frame # apparat selected case - open the apparat in the frame
self.apparat_open.emit(apparat) self.apparat_open.emit(apparat)
return return
def emit_signal(self, *args): def emit_signal(self, *args):
# #print("emit_signal", *args) # ##print("emit_signal", *args)
self.apparat_open.emit(args[1]) self.apparat_open.emit(args[1])

View File

@@ -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.catalogue import Catalogue
from src.backend.database import Database from src.backend.database import Database
from src.logic.swb import SWB
from .widget_sources.admin_update_signatures_ui import Ui_Dialog 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): class UpdaterThread(QtCore.QThread):
progress = QtCore.Signal(int) progress = QtCore.Signal(int)
@@ -19,17 +37,66 @@ class UpdaterThread(QtCore.QThread):
def run(self): def run(self):
total_books = len(self.books) total_books = len(self.books)
for index, book in enumerate(self.books): for index, book in enumerate(self.books):
try:
id = book["id"] id = book["id"]
bookdata = book["bookdata"] bookdata = book["bookdata"]
ppn = bookdata.link.split("kid=")[-1] ppn = bookdata.ppn
result = self.catalogue.get_book(ppn) result = self.catalogue.get_book(ppn)
if result: if result:
log.debug(f"Updating book {id} with ppn {ppn}")
bookdata.signature = result.signature bookdata.signature = result.signature
print(bookdata) # #print(bookdata)
self.db.updateBookdata(id, bookdata) self.db.updateBookdata(id, bookdata)
else: else:
print(f"No result for {ppn}") log.debug(f"No result for {ppn}")
self.db.deleteBook(id) # #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.progress.emit(index + 1)
self.currtot.emit(index + 1, total_books) self.currtot.emit(index + 1, total_books)
@@ -40,20 +107,49 @@ class UpdateSignatures(QtWidgets.QDialog, Ui_Dialog):
self.setupUi(self) self.setupUi(self)
self.setWindowTitle("Updating signatures...") self.setWindowTitle("Updating signatures...")
self.progressBar.setValue(0) 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.db = Database()
self.catalogue = Catalogue() 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() books = self.db.getAllBooks()
total_books = len(books) total_books = len(books)
self.progressBar.setMaximum(total_books) self.progressBar.setMaximum(total_books)
self.updater = UpdaterThread(books) self.updater = UpdaterThread(books)
self.updater.progress.connect(self.update_progress) self.updater.progress.connect(self.update_progress)
self.updater.finished.connect(self.updater.deleteLater)
self.updater.start() 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): def update_progress(self, value):
self.progressBar.setValue(value) self.progressBar.setValue(value)
if value >= self.progressBar.maximum(): if value <= self.progressBar.maximum():
self.pushButton.setText("Done") self.btn_update_signatures.setEnabled(False)
self.pushButton.setEnabled(False) if value == self.progressBar.maximum():
self.btn_update_signatures.setEnabled(True)
self.play_sound("ding.mp3")

View File

@@ -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 import sys
from appdirs import AppDirs
from pathlib import Path from pathlib import Path
from typing import Any
import loguru 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") appdirs = AppDirs("SemesterApparatsManager", "SAM")
@@ -193,7 +196,7 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard):
settings.set_openai_attr("api_key", openai_api_key) settings.set_openai_attr("api_key", openai_api_key)
settings.set_openai_attr("model", openai_model) settings.set_openai_attr("model", openai_model)
# save settings to file # save settings to file
print("Saving settings...") # print("Saving settings...")
settings.save() settings.save()
def open_database_settings(self): def open_database_settings(self):
@@ -201,7 +204,9 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard):
file_dialog = QtWidgets.QFileDialog(self, "Select Database File") file_dialog = QtWidgets.QFileDialog(self, "Select Database File")
file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory) file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory)
file_dialog.setViewMode(QtWidgets.QFileDialog.ViewMode.List) 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 # set start dir to appdir.user_data_dir
file_dialog.setDirectory(str(appdirs.user_data_dir)) file_dialog.setDirectory(str(appdirs.user_data_dir))
if file_dialog.exec(): if file_dialog.exec():
@@ -224,6 +229,7 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard):
temp_path = selected_dirs[0] temp_path = selected_dirs[0]
self.settings_temp.setText(temp_path) self.settings_temp.setText(temp_path)
def launch_wizard(): def launch_wizard():
"""Launch the welcome wizard.""" """Launch the welcome wizard."""
app = QtWidgets.QApplication.instance() app = QtWidgets.QApplication.instance()