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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(",")
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user