From c6f356fda420716444efa89b89d240328ecc6e87 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 3 Sep 2025 10:40:57 +0200 Subject: [PATCH] Refactor user interface: enhance sound playback functionality and integrate signature update process, fix broken database calls by using the app_id instead of the previously used appnr --- src/ui/userInterface.py | 124 +++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 26 deletions(-) diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index f490d3d..c7847f3 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -7,14 +7,14 @@ import time import webbrowser from datetime import datetime from pathlib import Path -from typing import Any, Union +from typing import Any, List, Optional, Tuple, Union import loguru from natsort import natsorted from PySide6 import QtCore, QtGui, QtWidgets -from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer from PySide6.QtCore import QThread from PySide6.QtGui import QRegularExpressionValidator +from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer from src import LOG_DIR, Icon from src.backend import ( @@ -59,7 +59,10 @@ from src.ui.widgets import ( FilePicker, MessageCalendar, NewEditionChecker, + NewEditionCheckSelector, SearchStatisticPage, + UpdaterThread, + UpdateSignatures, UserCreate, ) @@ -76,13 +79,6 @@ log.add( log.success("UI started") valid_input = (0, 0, 0, 0, 0, 0) -def play_sound(sound_file:str): - player = QMediaPlayer() - audio_output = QAudioOutput() - player.setAudioOutput(audio_output) - player.setSource(f"sounds/{sound_file}") - player.play() - class Ui(Ui_Semesterapparat): # use the Ui_MainWindow class from mainwindow.py @@ -256,6 +252,7 @@ class Ui(Ui_Semesterapparat): self.availChecker = None self.mail_thread = None self.autoGrabber = None + self.newEditionChecker = NewEditionCheckerThread() self.elsatab.setLayout(QtWidgets.QVBoxLayout()) self.search_statistics.setLayout(QtWidgets.QVBoxLayout()) @@ -270,6 +267,9 @@ class Ui(Ui_Semesterapparat): self.steps.hide() + self.player = QMediaPlayer() + self.audio_output = QAudioOutput() + self.valid_check_semester.clicked.connect(self.display_valid_semester) # type:ignore def create_doc(self): @@ -313,6 +313,9 @@ class Ui(Ui_Semesterapparat): elif self.select_action_box.currentText() == "Lehrperson bearbeiten": self.setWidget(EditProf()) self.admin_action.setTitle("Lehrperson bearbeiten") + elif self.select_action_box.currentText() == "Signaturen aktualisieren": + self.setWidget(UpdateSignatures()) + self.admin_action.setTitle("Signaturen aktualisieren") else: self.hideWidget() self.admin_action.setTitle("") @@ -330,6 +333,12 @@ class Ui(Ui_Semesterapparat): tempdelete() sys.exit() + 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 get_apparats(self): alist = self.db.getAllAparats(deleted=0) alist = natsorted(alist, key=lambda x: x.appnr, reverse=True) @@ -417,7 +426,7 @@ class Ui(Ui_Semesterapparat): else: return f"WiSe {currentYear}/{currentYear + 1}" - def open_apparat(self, apparat): + def open_apparat(self, apparat: Union[int, str]): if self.load_app_data(apparat): # change tab focus to tab 0 self.tabWidget.setCurrentIndex(0) @@ -626,17 +635,17 @@ class Ui(Ui_Semesterapparat): self.prof_mail.setText(data.mail) self.app_name.setFocus() - def get_index_of_value(self, table_widget, value): + def get_index_of_value(self, table_widget: QtWidgets.QTableWidget, value: str): for i in range(table_widget.rowCount()): for j in range(table_widget.columnCount()): if ( table_widget.item(i, j) is not None - and table_widget.item(i, j).text() == value + and table_widget.item(i, j).text() == value # type: ignore ): return i, j return (None, None) - def load_app_data(self, app_id=None): + def load_app_data(self, app_id: Optional[Union[int, str]] = None): self.cancel_active_selection.setEnabled(True) self.add_medium.setEnabled(True) for child in self.app_group_box.findChildren(QtWidgets.QToolButton): @@ -866,8 +875,9 @@ class Ui(Ui_Semesterapparat): def update_app_media_list(self): deleted = 0 if not self.chkbx_show_del_media.isChecked() else 1 - app_id = self.active_apparat + app_id = self.db.getId(self.app_name.text()) prof_id = self.db.getProfId(self.profdata) + print(app_id, prof_id) books: list[dict[int, BookData, int]] = self.db.getBooks( app_id, prof_id, deleted ) @@ -1201,6 +1211,7 @@ class Ui(Ui_Semesterapparat): for runner in self.bookGrabber: if not runner.isRunning(): runner.deleteLater() + self.bookGrabber.remove(runner) # #log.debug("Checking file") # get active app_id and prof_id self.tableWidget_apparate.setEnabled(False) @@ -1497,22 +1508,34 @@ class Ui(Ui_Semesterapparat): self.avail_status.setText("0/0") def check_new_editions(self): + # create a dialog that asks "Prof oder Apparat" with a button for each. based on that either search through the books of the apparat, or all books associated with the prof + selector = NewEditionCheckSelector() + selector.exec() + pick = selector.selection + app_id = self.tableWidget_apparate.item( self.tableWidget_apparate.currentRow(), 0 ).text() + prof_id: int = self.db.getProfIDByApparat(app_id) app_name = self.tableWidget_apparate.item( self.tableWidget_apparate.currentRow(), 1 ).text() subject = self.tableWidget_apparate.item( self.tableWidget_apparate.currentRow(), 4 ).text() - - prof_id: int = self.db.getProfIDByApparat(app_id) - books = self.db.getBooks(app_id, prof_id, deleted=0) + if pick == "professor": + books = self.db.getBooksByProfId(prof_id) + app_name = "Sammelmail" + app_id = ", ".join( + [str(app.appnr) for app in self.db.getApparatsByProf(prof_id)] + ) + else: + books = self.db.getBooks(app_id, prof_id, deleted=0) books = [book["bookdata"] for book in books] log.info(f"Checking {len(books)} for new editions") - self.newEditionChecker = NewEditionCheckerThread(books) - self.newEditionChecker.finished.connect(self.newEditionChecker.deleteLater) + + self.newEditionChecker.entries = books + self.newEditionChecker.finished.connect(self.newEditionChecker.reset) self.progressBar.setMaximum(len(books)) self.newEditionChecker.updateSignal.connect(self.update_status) @@ -1520,13 +1543,18 @@ class Ui(Ui_Semesterapparat): self.newEditionChecker.start() while self.newEditionChecker.isRunning(): QtWidgets.QApplication.processEvents() - play_sound("ding.mp3") + self.play_sound("ding.mp3") results = self.newEditionChecker.results + if results == []: + return + log.info(f"Found {len(results)} possible new editions - opening dialog") newEditionChecker = NewEditionChecker(results=results) newEditionChecker.exec() accepted_books = newEditionChecker.accepted_books # print(accepted_books) + if accepted_books == []: + return self.mail_thread = Mail_Dialog( prof_name=self.db.getSpecificProfData(prof_id, ["fullname"]), @@ -1535,7 +1563,7 @@ class Ui(Ui_Semesterapparat): app_name=app_name, app_subject=subject, accepted_books=accepted_books, - default_mail="Neuauflagen für Semesterapparat" + default_mail="Neuauflagen für Semesterapparat", ) self.mail_thread.show() @@ -1625,8 +1653,45 @@ class Ui(Ui_Semesterapparat): menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) # type: ignore def update_data(self): - # TODO: use link in table, parse data and if needed, update location / signature - pass + signatures = [ + self.tableWidget_apparat_media.item(row, 1).text() + for row in range(self.tableWidget_apparat_media.rowCount()) + ] # type: ignore + prof_id = self.db.getProfId(self.profdata) # type: ignore + app_id = self.active_apparat + books: List[Tuple[int, BookData]] = [] + for signature in signatures: + book = self.db.getBookBasedOnSignature( + app_id=app_id, + signature=signature, + prof_id=prof_id, + ) + book_id = self.db.getBookIdBasedOnSignature( + self.active_apparat, + prof_id, + signature, + ) + books.append((book_id, book)) + # self.autoUpdater.entries = books + # self.autoUpdater.finished.connect(self.autoUpdater.reset) + self.updater = UpdaterThread() + u_books = [] + for book_id, book in books: + u_books.append({"id": book_id, "bookdata": book}) + self.updater.books = u_books + self.progressBar.setMaximum(len(books)) + self.updater.finished.connect(self.updater.deleteLater) + self.updater.finished.connect(self.update_app_media_list) + self.updater.currtot.connect(self.update_status) + self.updater.start() + # ppn = book.link.split("kid=")[-1] + # result = cat.get_book(ppn) + # if result: + # book.signature = result.signature + # book.in_apparat = True + # print(book) + # self.db.updateBookdata(book_id, book) + # self.update_app_media_list() def copy_to_apparat(self): selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() # type: ignore @@ -1724,12 +1789,12 @@ class Ui(Ui_Semesterapparat): ).text() prof_id = self.db.getProfId(self.profdata) data = self.db.getBookBasedOnSignature( - app_id=self.active_apparat, + app_id=self.db.getId(self.app_name.text()), signature=book, prof_id=prof_id, ) book_id = self.db.getBookIdBasedOnSignature( - self.active_apparat, + self.db.getId(self.app_name.text()), prof_id, book, ) @@ -1891,7 +1956,13 @@ class Ui(Ui_Semesterapparat): if state == 1: log.debug("Deleting apparat {}", selected_apparat_id) pid = self.db.getProfIDByApparat(selected_apparat_id) - self.db.deleteApparat(selected_apparat_id, Semester().value) + apparat = Apparat( + appnr=int(selected_apparat_id), + name=self.tableWidget_apparate.item( + self.tableWidget_apparate.currentRow(), 1 + ).text(), + ) + self.db.deleteApparat(apparat=apparat, semester=Semester().value) # delete the corresponding entry from self.apparats for apparat in self.apparats: if apparat.appnr == int(selected_apparat_id): @@ -1935,6 +2006,7 @@ def launch_gui(): ) # if that thread uses an event loop app.aboutToQuit.connect(aui.docu.terminate) # our new slot app.aboutToQuit.connect(aui.docu.wait) + app.aboutToQuit.connect(aui.newEditionChecker.terminate) atexit.register(tempdelete) # atexit.register(aui.validate_thread.quit) # atexit.register(aui.docu.quit)