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 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 = """<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
"""
mail_base = mail.split("<html>")[0]
mail_body = mail.split("</head>")[1]
mail = mail_base + html_head + mail_body
mail = (
mail.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&quot;", '"')
.replace("&amp;", "&")
)
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": """<pre class="moz-signature" cols="72">--
{signature}
</pre>""",
"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

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()