From fd692b8c99157546459538f5844dd2e6e49248eb Mon Sep 17 00:00:00 2001 From: WorldTeacher <41587052+WorldTeacher@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:28:23 +0200 Subject: [PATCH] updates --- icons/dark/notification.svg | 1 + icons/icons.yaml | 3 +- icons/light/notification.svg | 1 + src/backend/database.py | 27 +- src/ui/Ui_semesterapparat_ui.py | 8 +- src/ui/dialogs/confirm_extend.py | 14 + .../dialog_sources/Ui_confirm_extend.py | 9 +- src/ui/dialogs/reminder.py | 3 +- src/ui/semesterapparat_ui.ui | 14 +- src/ui/userInterface.py | 2444 +++++++++++++++++ src/ui/widgets/calendar_entry.py | 23 +- 11 files changed, 2520 insertions(+), 27 deletions(-) create mode 100644 icons/dark/notification.svg create mode 100644 icons/light/notification.svg create mode 100644 src/ui/dialogs/confirm_extend.py create mode 100644 src/ui/userInterface.py diff --git a/icons/dark/notification.svg b/icons/dark/notification.svg new file mode 100644 index 0000000..16527e4 --- /dev/null +++ b/icons/dark/notification.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/icons.yaml b/icons/icons.yaml index 9cec28c..cdd2019 100644 --- a/icons/icons.yaml +++ b/icons/icons.yaml @@ -16,4 +16,5 @@ template_fail: test_fail.svg offAction: shutdown.svg info: info.svg help: help.svg -close: close.svg \ No newline at end of file +close: close.svg +notification: notification.svg \ No newline at end of file diff --git a/icons/light/notification.svg b/icons/light/notification.svg new file mode 100644 index 0000000..4a3fcea --- /dev/null +++ b/icons/light/notification.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/backend/database.py b/src/backend/database.py index 512d889..88c69b6 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -76,7 +76,6 @@ class Database: Returns: sql.Connection: The active connection to the database """ - print(self.db_path) return sql.connect(self.db_path) def close_connection(self, conn: sql.Connection): @@ -501,6 +500,31 @@ class Database: (message["message"], user_id, message["remind_at"], app_id), ) + def getAllMessages(self) -> list[dict[str, str, str, str]]: + """Get all the messages in the database + + Returns: + list[dict[str, str, str, str]]: a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message + """ + + def __get_user_name(user_id): + return self.query_db( + "SELECT username FROM user WHERE id=?", (user_id,), one=True + )[0] + + messages = self.query_db("SELECT * FROM messages") + ret = [ + { + "message": i[2], + "user": __get_user_name(i[4]), + "appnr": i[5], + "id": i[0], + "remind_at": i[3], + } + for i in messages + ] + return ret + def getMessages(self, date: str) -> list[dict[str, str, str, str]]: """Get all the messages for a specific date @@ -529,6 +553,7 @@ class Database: Args: message_id (str): the id of the message """ + logger.log_info(f"Deleting message with id {message_id}") self.query_db("DELETE FROM messages WHERE id=?", (message_id,)) # Prof data diff --git a/src/ui/Ui_semesterapparat_ui.py b/src/ui/Ui_semesterapparat_ui.py index d34a1bf..b989c33 100644 --- a/src/ui/Ui_semesterapparat_ui.py +++ b/src/ui/Ui_semesterapparat_ui.py @@ -891,7 +891,7 @@ class Ui_MainWindow(object): self.elsa_semester.setObjectName("elsa_semester") self.gridLayout_7.addWidget(self.elsa_semester, 2, 1, 1, 1) self.table_elsa_list = QtWidgets.QTableWidget(parent=self.tab_8) - self.table_elsa_list.setGeometry(QtCore.QRect(20, 410, 771, 271)) + self.table_elsa_list.setGeometry(QtCore.QRect(20, 410, 771, 321)) self.table_elsa_list.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) self.table_elsa_list.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.table_elsa_list.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) @@ -908,15 +908,15 @@ class Ui_MainWindow(object): self.table_elsa_list.setHorizontalHeaderItem(3, item) self.table_elsa_list.horizontalHeader().setStretchLastSection(True) self.elsa_statistic_frame = QtWidgets.QFrame(parent=self.tab_8) - self.elsa_statistic_frame.setGeometry(QtCore.QRect(810, 410, 431, 271)) + self.elsa_statistic_frame.setGeometry(QtCore.QRect(800, 410, 451, 321)) self.elsa_statistic_frame.setObjectName("elsa_statistic_frame") self.elsa_statistics = QtWidgets.QTabWidget(parent=self.elsa_statistic_frame) - self.elsa_statistics.setGeometry(QtCore.QRect(0, 0, 431, 271)) + self.elsa_statistics.setGeometry(QtCore.QRect(10, 0, 431, 321)) self.elsa_statistics.setObjectName("elsa_statistics") self.tab_9 = QtWidgets.QWidget() self.tab_9.setObjectName("tab_9") self.elsa_statistics_table = QtWidgets.QTableWidget(parent=self.tab_9) - self.elsa_statistics_table.setGeometry(QtCore.QRect(0, 0, 421, 241)) + self.elsa_statistics_table.setGeometry(QtCore.QRect(0, 0, 421, 291)) self.elsa_statistics_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.elsa_statistics_table.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) self.elsa_statistics_table.setTextElideMode(QtCore.Qt.TextElideMode.ElideRight) diff --git a/src/ui/dialogs/confirm_extend.py b/src/ui/dialogs/confirm_extend.py new file mode 100644 index 0000000..7fdd74e --- /dev/null +++ b/src/ui/dialogs/confirm_extend.py @@ -0,0 +1,14 @@ +from .dialog_sources.Ui_confirm_extend import Ui_extend_confirm +from PyQt6 import QtWidgets + +class ConfirmExtend(QtWidgets.QDialog, Ui_extend_confirm): + def __init__(self, parent=None): + super().__init__(parent) + self.setupUi(self) + + +def launch(): + app = QtWidgets.QApplication([]) + window = ConfirmExtend() + window.show() + app.exec() \ No newline at end of file diff --git a/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py b/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py index 0d4c620..bcb7531 100644 --- a/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py +++ b/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py @@ -16,18 +16,15 @@ class Ui_extend_confirm(object): self.buttonBox = QtWidgets.QDialogButtonBox(parent=extend_confirm) self.buttonBox.setGeometry(QtCore.QRect(290, 20, 81, 241)) self.buttonBox.setOrientation(QtCore.Qt.Orientation.Vertical) - self.buttonBox.setStandardButtons( - QtWidgets.QDialogButtonBox.StandardButton.Cancel - | QtWidgets.QDialogButtonBox.StandardButton.Ok - ) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) self.buttonBox.setObjectName("buttonBox") self.textEdit = QtWidgets.QTextEdit(parent=extend_confirm) self.textEdit.setGeometry(QtCore.QRect(10, 10, 271, 81)) self.textEdit.setObjectName("textEdit") self.retranslateUi(extend_confirm) - self.buttonBox.accepted.connect(extend_confirm.accept) # type: ignore - self.buttonBox.rejected.connect(extend_confirm.reject) # type: ignore + self.buttonBox.accepted.connect(extend_confirm.accept) # type: ignore + self.buttonBox.rejected.connect(extend_confirm.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(extend_confirm) def retranslateUi(self, extend_confirm): diff --git a/src/ui/dialogs/reminder.py b/src/ui/dialogs/reminder.py index e5afe11..26fe9cf 100644 --- a/src/ui/dialogs/reminder.py +++ b/src/ui/dialogs/reminder.py @@ -1,12 +1,13 @@ from PyQt6 import QtWidgets from .dialog_sources.Ui_reminder import Ui_Dialog - +from src import Icon class ReminderDialog(QtWidgets.QDialog, Ui_Dialog): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) + self.windowIcon(Icon("notification").icon) def return_message(self) -> dict: return { diff --git a/src/ui/semesterapparat_ui.ui b/src/ui/semesterapparat_ui.ui index 70b3395..906048f 100644 --- a/src/ui/semesterapparat_ui.ui +++ b/src/ui/semesterapparat_ui.ui @@ -2169,7 +2169,7 @@ 20 410 771 - 271 + 321 @@ -2208,19 +2208,19 @@ - 810 + 800 410 - 431 - 271 + 451 + 321 - 0 + 10 0 431 - 271 + 321 @@ -2233,7 +2233,7 @@ 0 0 421 - 241 + 291 diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py new file mode 100644 index 0000000..f5965c4 --- /dev/null +++ b/src/ui/userInterface.py @@ -0,0 +1,2444 @@ +# encoding: utf-8 +import atexit +import os + +# import re +import sys +import tempfile +import time +import webbrowser +from pathlib import Path + +from icecream import ic +from natsort import natsorted +from omegaconf import OmegaConf +from PyQt6 import QtCore, QtGui, QtWidgets +from PyQt6.QtCore import QThread +from PyQt6.QtGui import QRegularExpressionValidator + +from src.backend import ( + AdminCommands, + recreateElsaFile, + recreateFile, + Database, + tempdelete, + generateSemesterByDate, +) +from src.logic import ( + AvailChecker, + BookGrabber, + custom_sort, + APP_NRS, + PROF_TITLES, + ApparatData, + BookData, + csv_to_list, + elsa_word_to_csv, + word_docx_to_csv, + MyLogger, +) + +from src.ui import ( + App_Ext_Dialog, + DataGraph, + FilePicker, + Mail_Dialog, + StatusWidget, + Ui_Semesterapparat, + edit_bookdata_ui, + login_ui, + medienadder_ui, + parsed_titles_ui, + popus_confirm, + reminder_ui, + Settings, + About, + CalendarEntry, + MessageCalendar, +) +from src.utils import Icon + +config = OmegaConf.load("config.yaml") + + +class Medien(medienadder_ui): + def __init__(self) -> None: + self.logger = MyLogger("Medien") + super().__init__() + self.mode = "" + self.data = [] + + def get_list_data(self) -> list: + signatures = self.listWidget.findItems("*", QtCore.Qt.MatchFlag.MatchWildcard) + return [signature.text() for signature in signatures] + + def get_mode(self) -> str: + return self.comboBox.currentText() + + +class MyComboBox(QtWidgets.QComboBox): + + def __init__(self, parent=None): + super().__init__(parent) + + +valid_input = (0, 0, 0, 0, 0, 0) + + +class Ui(Ui_Semesterapparat): + # use the Ui_MainWindow class from mainwindow.py + def __init__(self, MainWindow, username: str) -> None: + self.logger = MyLogger("Ui") + self.logger.log_info("Starting Semesterapparatsmanagement") + super().__init__() + self.active_user = username + self.setupUi(MainWindow) + self.MainWindow = MainWindow + # set the window title + MainWindow.setWindowTitle("Semesterapparatsmanagement") + MainWindow.setWindowIcon(Icon("logo").icon) + + self.db = Database() + # self.show() + # self.setWindowTitle("Semesterapparatsmanagement") + # self.setWindowIcon(QIcon("ui\icon.png")) + # self.sem_sommer.clicked.connect(self.buttonClicked) + self.btn_add_document.clicked.connect(self.add_document) + self.check_file.clicked.connect( + self.btn_check_file_threaded + ) # default: self.add_media_from_file) + self.create_new_app.clicked.connect(self.btn_create_new_apparat) + # self.load_app.clicked.connect(self.btn_load_apparat) + self.btn_apparat_save.clicked.connect(self.btn_save_apparat) + self.btn_apparat_apply.clicked.connect(self.update_apparat) + self.btn_open_document.clicked.connect(self.open_document) + self.add_medium.clicked.connect(self.btn_add_medium) + self.btn_copy_adis_command.clicked.connect(self.text_to_clipboard) + self.btn_reserve.clicked.connect(self.check_availability) + self.calendarWidget = MessageCalendar(self.frame_2) + self.calendarWidget.setGridVisible(True) + self.calendarWidget.setVerticalHeaderFormat( + QtWidgets.QCalendarWidget.VerticalHeaderFormat.NoVerticalHeader + ) + self.calendarWidget.setObjectName("MessageCalendar") + self.calendarWidget.clicked.connect(self.open_reminder) + # assign a context menu to the calendar + self.calendarlayout.addWidget(self.calendarWidget) + self.tableWidget_apparat_media.horizontalHeader().setSectionResizeMode( + QtWidgets.QHeaderView.ResizeMode.Stretch + ) + self.tableWidget_apparate.horizontalHeader().setSectionResizeMode( + QtWidgets.QHeaderView.ResizeMode.Stretch + ) + self.tableWidget_apparate.setSortingEnabled(True) + + # Actions + self.actionEinstellungen.triggered.connect(self.open_settings) + self.actionDokumentation.triggered.connect(self.open_documentation) + Icon("offAction", self.actionBeenden) + self.actionBeenden.triggered.connect(self.quit) + self.actionAbout.triggered.connect(self.open_about) + + # set validators + self.sem_year.setText(str(QtCore.QDate.currentDate().year())) + self.prof_mail.setValidator( + QRegularExpressionValidator( + QtCore.QRegularExpression( + r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}" + ) + ) + ) + self.prof_tel_nr.setValidator(QtGui.QIntValidator()) + # set the validator for the app name, allow all letters and umlauts + self.app_fach.setValidator( + QtGui.QRegularExpressionValidator( + QtCore.QRegularExpression(r"[a-zA-Z\s\W]+") + ) + ) + + # allow only letters, numbers, whitespaces, symbols for the apparat name + self.app_name.setValidator( + QtGui.QRegularExpressionValidator( + QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+") + ) + ) + self.tableWidget_apparate.addScrollBarWidget( + QtWidgets.QScrollBar(), QtCore.Qt.AlignmentFlag.AlignRight + ) + # connect contextmenuevent to tablewidget + + # enable automatic resizing of columns for book_search_result + self.book_search_result.horizontalHeader().setSectionResizeMode( + QtWidgets.QHeaderView.ResizeMode.Stretch + ) + self.tableWidget_apparate.doubleClicked.connect(self.load_app_data) + self.load_app.hide() + print(f"user:{self.active_user}") + userrole = self.db.getRole(self.active_user) + # hide admin interface when non-admin is logged in + if userrole == "admin": + self.tabWidget.setTabVisible(3, True) + else: + self.tabWidget.setTabVisible(3, False) + # self.update_app_media_list() + self.populate_prof_dropdown() + self.populate_appfach_dropdown() + # if the focus is changed from the prof name dropdown, set the prof data if the prof exists in the database, otherwise show a message + self.drpdwn_prof_name.currentIndexChanged.connect(self.set_prof_data) + self.cancel_active_selection.clicked.connect(self.btn_cancel_active_selection) + self.check_eternal_app.stateChanged.connect(self.set_state) + # validate inputs + self.prof_mail.textChanged.connect(self.validate_prof_mail) + self.drpdwn_prof_name.editTextChanged.connect(self.validate_prof_name) + self.prof_tel_nr.textChanged.connect(self.validate_prof_tel) + self.app_name.textChanged.connect(self.validate_app_name) + self.app_fach.currentTextChanged.connect(self.validate_app_fach) + self.sem_year.textChanged.connect(self.validate_semester) + self.check_eternal_app.stateChanged.connect(self.validate_semester) + self.chkbx_show_del_media.stateChanged.connect(self.update_app_media_list) + + self.progress_label.setText("Bitte warten...") + + # Set visibility/enabled state of certain entries + self.chkbx_show_del_media.setEnabled(False) + self.label_info.hide() + self.app_group_box.setEnabled(False) + self.line_2.hide() + self.progress_label.hide() + # self.message_frame.hide() + self.btn_reserve.hide() + self.label_20.hide() + self.line_3.hide() + self.avail_status.hide() + self.chkbx_show_del_media.hide() + self.groupBox_2.hide() + self.groupBox.hide() + self.btn_del_select_apparats.setEnabled(False) + + self.check_deletable.stateChanged.connect(self.gridchange) + self.tableWidget.horizontalHeader().setSectionResizeMode( + QtWidgets.QHeaderView.ResizeMode.Stretch + ) + self.btn_del_select_apparats.clicked.connect(self.delete_selected_apparats) + self.statistics_table.doubleClicked.connect(self.display_detailed_data) + self.tabWidget_2.currentChanged.connect(self.tabW2_changed) + self.tabWidget.currentChanged.connect(self.tabW1_changed) + self.tableWidget.resizeColumnsToContents() + self.tableWidget.resizeRowsToContents() + + # create a thread, that continually checks the validity of the inputs + self.validate_thread = QThread() + self.validate_thread.started.connect(self.thread_check) + self.validate_thread.start() + + # get all current apparats and cache them in a list + self.apparats = self.get_apparats() + + self.old_apparats = self.apparats # create a clone to compare against later + # if length of apparats changes, update box_apparats + # if tab is changed, gather data needed + self.tabWidget.currentChanged.connect(self.tab_changed) + self.btn_search.clicked.connect(self.statistics) + + ### Admin interface ### + self.select_action_box.addItem("") + self.select_action_box.setCurrentText("") + self.hide_all() + self.select_action_box.currentTextChanged.connect(self.admin_action_changed) + self.edit_faculty_member_select_member.currentTextChanged.connect( + self.edit_faculty_member_set_data + ) + self.book_search.clicked.connect(self.search_book) + + # Context Menus + self.tableWidget_apparate.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.CustomContextMenu + ) + self.tableWidget_apparat_media.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.CustomContextMenu + ) + self.tableWidget_apparate.customContextMenuRequested.connect( + self.open_context_menu + ) + self.tableWidget_apparat_media.customContextMenuRequested.connect( + self.media_context_menu + ) + self.tableWidget.customContextMenuRequested.connect( + self.statistics_table_context_menu + ) + # statistic side buttons + self.btn_notify_for_deletion.clicked.connect(self.notify_for_deletion) + self.btn_notify_for_deletion.setEnabled(False) + # elsa buttons + self.elsa_add_new.clicked.connect(self.add_new_elsa) + self.elsa_cancel_create.clicked.connect(self.cancel_elsa_creation) + self.elsa_save.clicked.connect(self.save_elsa) + self.elsa_date_today.clicked.connect(self.generateTodayDateElsa) + self.active_semester.clicked.connect(self.addSemester) + Icon("semester", self.active_semester) + Icon("today", self.elsa_date_today) + self.elsa_table.doubleClicked.connect(self.open_elsa) + self.btn_add_document_elsa.clicked.connect(self.addDokumentElsa) + self.check_file_elsa.clicked.connect(self.parseDokumentElsa) + self.btn_open_document_elsa.clicked.connect(self.openDocumentElsa) + + # admin buttons + self.user_frame_addUser.clicked.connect(self.add_user) + self.pushButton.clicked.connect(self.delete_user) + self.update_user.clicked.connect(self.update_user_data) + self.update_faculty_member.clicked.connect(self.edit_faculty_member_action) + + # Create instances to be used by the threads in the application + self.bookGrabber = None + self.availChecker = None + self.mail_thread = None + self.autoGrabber = None + + def open_about(self): + about = About() + about.exec() + + def quit(self): + # delete all temporary files + delete_temp_contents() + sys.exit() + + def add_new_elsa(self): + self.create_frame_elsa.setEnabled(True) + self.elsa_cancel_create.setEnabled(True) + self.active_semester.setEnabled(True) + profs = self.db.getProfs() + for prof in profs: + self.elsa_prof.addItem(f"{prof[3]}, {prof[2]}") + self.elsa_prof.setCurrentText("") + self.elsa_date.setText("") + self.elsa_semester.setText("") + + def cancel_elsa_creation(self): + self.create_frame_elsa.setEnabled(False) + self.elsa_cancel_create.setEnabled(False) + self.elsa_prof.setCurrentText("") + self.elsa_date.setText("") + self.elsa_semester.setText("") + self.dokument_list_elsa.setRowCount(0) + self.table_elsa_list.setRowCount(0) + + def generateTodayDateElsa(self): + self.elsa_date.setText(QDate.currentDate().toString("dd.MM.yyyy")) + + def addSemester(self): + self.elsa_semester.setText(self.generateSemester(today=True)) + + def save_elsa(self): + if ( + self.elsa_prof.currentText() == "" + or self.elsa_semester.text() == "" + or self.elsa_date.text() == "" + ): + # warning message + self.confirm_popup("Bitte füllen Sie alle Felder aus") + + return + prof = self.elsa_prof.currentText() + semester = self.elsa_semester.text() + date = self.elsa_date.text() + + self.db.createElsaApparat(date, prof, semester) + self.cancel_elsa_creation() + + def insert_elsa_into_table(self, apparat): + self.elsa_table.insertRow(0) + date = apparat[1] + semester = apparat[2] + prof = apparat[3] + self.elsa_table.setItem(0, 0, QtWidgets.QTableWidgetItem(prof)) + self.elsa_table.setItem(0, 1, QtWidgets.QTableWidgetItem(date)) + self.elsa_table.setItem(0, 2, QtWidgets.QTableWidgetItem(semester)) + return (semester, 1) + + def open_elsa(self): + prof = self.elsa_table.item(self.elsa_table.currentRow(), 0).text() + date = self.elsa_table.item(self.elsa_table.currentRow(), 1).text() + semester = self.elsa_table.item(self.elsa_table.currentRow(), 2).text() + if self.elsa_prof.currentText() == prof: + self.logger.log_info("Same prof, stopping") + return + + self.dokument_list_elsa.setRowCount(0) + self.table_elsa_list.setRowCount(0) + self.elsa_cancel_create.setEnabled(True) + # get elsa apparats, iterate over them and find the one where all matches + elsa_apparats = self.db.getElsaApparats() + elsa_id = None + for apparat in elsa_apparats: + if apparat[1] == date and apparat[2] == semester and apparat[3] == prof: + elsa_id = apparat[0] + print(elsa_id) + break + if elsa_id is None: + return + self.elsa_date.setText(date) + self.elsa_semester.setText(semester) + self.elsa_prof.setCurrentText(prof) + documents = self.db.getElsaFiles(elsa_id) + for document in documents: + print(document) + self.dokument_list_elsa.insertRow(0) + self.dokument_list_elsa.setItem( + 0, 0, QtWidgets.QTableWidgetItem(document[0]) + ) + self.dokument_list_elsa.setItem( + 0, 1, QtWidgets.QTableWidgetItem(document[1]) + ) + self.dokument_list_elsa.setItem(0, 2, QtWidgets.QTableWidgetItem("❌")) + self.dokument_list_elsa.setItem( + 0, 3, QtWidgets.QTableWidgetItem("Database") + ) + scans = self.db.getElsaMedia(elsa_id) + if scans == []: + self.create_frame_elsa.setEnabled(True) + else: + for scan in scans: + self.table_elsa_list.insertRow(0) + self.table_elsa_list.setItem(0, 0, QtWidgets.QTableWidgetItem(scan[1])) + self.table_elsa_list.setItem(0, 1, QtWidgets.QTableWidgetItem(scan[2])) + self.table_elsa_list.setItem(0, 2, QtWidgets.QTableWidgetItem(scan[3])) + self.table_elsa_list.setItem(0, 3, QtWidgets.QTableWidgetItem(scan[4])) + + def addDokumentElsa(self): + print("Add document") + picker = FilePicker() + files = picker.pick_files() + datalist = [] + for file in files: + data = {} + print(file) + filename = file.split("/")[-1] + filetype = filename.split(".")[-1] + self.dokument_list_elsa.insertRow(0) + self.dokument_list_elsa.setItem(0, 0, QtWidgets.QTableWidgetItem(filename)) + self.dokument_list_elsa.setItem(0, 1, QtWidgets.QTableWidgetItem(filetype)) + self.dokument_list_elsa.setItem(0, 2, QtWidgets.QTableWidgetItem("*")) + self.dokument_list_elsa.setItem(0, 3, QtWidgets.QTableWidgetItem(file)) + # set tooltip of row 3 to the file path for each row + self.dokument_list_elsa.item(0, 3).setToolTip(file) + data["name"] = filename + data["path"] = file + data["type"] = filetype + datalist.append(data) + elsa_id = self.db.getElsaId( + self.elsa_prof.currentText(), + self.elsa_semester.text(), + self.elsa_date.text(), + ) + print(elsa_id) + if elsa_id is None: + # get length of elsa table + rows = self.elsa_table.rowCount() + elsa_id = rows + 1 + self.db.insertElsaFile(datalist, elsa_id) + self.elsa_save.click() + + def parseDokumentElsa(self): + if self.dokument_list_elsa.rowCount() == 0: + return + else: + # get the file path of the selected file based on it's row + row = self.dokument_list_elsa.currentRow() + file = self.dokument_list_elsa.item(row, 3).text() + print(file) + if file == "Database": + filename = self.dokument_list_elsa.item(row, 0).text() + filetype = self.dokument_list_elsa.item(row, 1).text() + + file = recreateElsaFile( + filename=filename, filetype=filetype, open=False + ) + print(file) + data = elsa_word_to_csv(file) + for row in data: + self.table_elsa_list.insertRow(0) + ic(row) + chapter_title = row[2] + book_title = row[4] + signature = row[7] + pages = row[6] + data = { + "chapter": chapter_title, + "title": book_title, + "signature": signature, + "pages": pages, + } + self.table_elsa_list.setItem( + 0, 0, QtWidgets.QTableWidgetItem(chapter_title) + ) + self.table_elsa_list.setItem( + 0, 1, QtWidgets.QTableWidgetItem(book_title) + ) + self.table_elsa_list.setItem( + 0, 2, QtWidgets.QTableWidgetItem(signature) + ) + self.table_elsa_list.setItem(0, 3, QtWidgets.QTableWidgetItem(pages)) + self.db.addElsaMedia( + data, + self.db.getElsaId( + self.elsa_prof.currentText(), + self.elsa_semester.text(), + self.elsa_date.text(), + ), + ) + + def openDocumentElsa(self): + # open the selected document + row = self.dokument_list_elsa.currentRow() + location = self.dokument_list_elsa.item(row, 3).text() + filetype = self.dokument_list_elsa.item(row, 1).text() + filename = self.dokument_list_elsa.item(row, 0).text() + if location == "Database": + recreateElsaFile(filename, filetype, open=True) + else: + os.system(f"{filename}") + + def notify_for_deletion(self): + # get all selected apparats + selected_apparats: list[dict] = [] + for i in range(self.tableWidget.rowCount()): + data = {} + if self.tableWidget.cellWidget(i, 0).isChecked(): + data["app_id"] = self.tableWidget.item(i, 2).text() + data["app_name"] = self.tableWidget.item(i, 1).text() + data["prof_name"] = self.tableWidget.item(i, 3).text() + + selected_apparats.append(data) + # delete all selected apparats + ic(selected_apparats) + dialogs = [] + for i in selected_apparats: + + app_id = i["app_id"] + app_name = i["app_name"] + prof_name = i["prof_name"] + prof_mail = self.db.getProfData(prof_name)[0] + self.mail_thread = Mail_Dialog( + app_id=app_id, + app_name=app_name, + prof_name=prof_name, + prof_mail=prof_mail, + app_subject="", + default_mail="Information bezüglich der Auflösung des Semesterapparates", + ) + dialogs.append(self.mail_thread) + for dialog in dialogs: + dialog.exec() + + self.btn_notify_for_deletion.setEnabled(False) + + def setStatisticTableSize(self): + print(self.statistics_table.size(), self.statistics_table.rowCount()) + size = self.statistics_table.size() + h = size.height() + w = size.width() + rows = self.statistics_table.rowCount() + rowheight = round(h / rows) - 5 + header_width = round(w / 3) - 5 + for i in range(3): + self.statistics_table.setColumnWidth(i, header_width) + for i in range(rows): + self.statistics_table.setRowHeight(i, rowheight) + + def get_apparats(self): + alist = self.db.getAllAparats(deleted=0) + alist = natsorted(alist, key=lambda x: x[4], reverse=True) + for apparat in alist: + self.insert_apparat_into_table(apparat) + return alist + + def populate_appfach_dropdown(self): + self.app_fach.clear() + self.app_fach.addItem("") + self.app_fach.setCurrentText("") + self.app_fach.addItems([subject[1] for subject in self.db.getSubjects()]) + + def open_documentation(self): + # open the documentation in the default browser + webbrowser.open("file:///" + os.path.abspath("docs/index.html")) + # documentation = documentationview.DocumentationViewer() + # documentation.show() + + def tabW1_changed(self): + if self.tabWidget.currentIndex() == 1: # Statistic + # self.tabWidget.setCurrentIndex(1) + self.tabWidget_2.setCurrentIndex(1) + self.tabWidget_2.setCurrentIndex(0) + self.populate_tab() + if self.tabWidget.currentIndex() == 0: # Apparate + # clear all entries from the table + self.tableWidget_apparate.setRowCount(0) + self.get_apparats() + if self.tabWidget.currentIndex() == 2: # Elsa + self.elsa_cancel_create.click() + try: + self.elsa_statistics.removeTab(1) + except: + pass + self.elsa_table.setRowCount(0) + elsa_apparats = self.db.getElsaApparats() + elsa_apparats = natsorted(elsa_apparats, key=lambda x: x[2], reverse=True) + graph_data = {"x": [], "y": []} # x = semester, y = number of apparats + + for apparat in elsa_apparats: + print(apparat) + data = self.insert_elsa_into_table(apparat) + semester = data[0] + number = data[1] + if semester not in graph_data["x"]: + graph_data["x"].append(semester) + graph_data["y"].append(number) + else: + index = graph_data["x"].index(semester) + graph_data["y"][index] += number + + generateMissing = True if len(graph_data["x"]) > 1 else False + graph = DataGraph( + "ELSA Apparate pro Semester", + graph_data, + generateMissing, + "Anzahl der Apparate", + ) + ic(graph_data) + self.elsa_statistics_table.setRowCount(0) + for i in range(len(graph_data["x"])): + self.elsa_statistics_table.insertRow(0) + self.elsa_statistics_table.setItem( + 0, 0, QtWidgets.QTableWidgetItem(graph_data["x"][i]) + ) + self.elsa_statistics_table.setItem( + 0, 1, QtWidgets.QTableWidgetItem(str(graph_data["y"][i])) + ) + self.elsa_statistics.addTab(graph, "Graph") + + def search_book(self): + self.book_search_result.setRowCount(0) + signature = self.seach_by_signature.text() + title = self.search_by_title.text() + params = { + "signature": signature if signature != "" else None, + "title": title if title != "" else None, + } + params = {key: value for key, value in params.items() if value is not None} + # ic(params) + retdata = self.db.searchBook(params) + if retdata is None: + return + for book in retdata: + + self.book_search_result.insertRow(0) + self.book_search_result.setItem( + 0, 0, QtWidgets.QTableWidgetItem(book[0].title) + ) + self.book_search_result.setItem( + 0, 1, QtWidgets.QTableWidgetItem(book[0].signature) + ) + print(book[1]) + self.book_search_result.setItem( + 0, + 2, + QtWidgets.QTableWidgetItem(self.db.getApparatName(book[1], book[2])), + ) + + def edit_faculty_member_set_data(self): + # get the selected member + name = self.edit_faculty_member_select_member.currentText() + fullname = name.replace(",", "") + print(fullname, name) + # get the data for the selected member + data = self.db.getProfByName(fullname) + # set the data + print(data) + if data is None: + self.edit_faculty_member_title.setText("") + self.faculty_member_old_telnr.setText("") + self.faculty_member_oldmail.setText("") + self.edit_faculty_member_title.setText("") + else: + self.edit_faculty_member_title.setText(data[1]) + self.faculty_member_old_telnr.setText(data[6]) + self.faculty_member_oldmail.setText(data[5]) + ( + self.edit_faculty_member_title.setText(data[1]) + if data[1] is not None + else self.edit_faculty_member_title.setText("") + ) + + # self.edit_faculty_member_name.setText(f"{data[3]} {data[2]}") + # self.edit_faculty_member_title.setCurrentText(data[1]) + # self.edit_faculty_member_mail.setText(data[4]) + # self.edit_faculty_member_tel.setText(data[5]) + # self.edit_faculty_member_adis_id.setText(str(data[0])) + # self.edit_faculty_member_id.setText(str(data[6])) + + def add_user(self): + username = self.user_create_frame_username.text() + password = self.user_create_frame_password.text() + role = self.user_frame_userrole.currentText() + if self.db.checkUsername(username): + # ic("username already exists") + # self.user_create_frame_error.setText("Der Nutzername ist bereits vergeben")#TODO: implement error message + return + userdata = AdminCommands().create_password(password) + self.db.createUser( + user=username, + password=f"{userdata[1]}{userdata[0]}", + salt=userdata[1], + role=role, + ) + self.user_create_frame_username.clear() + self.user_create_frame_password.clear() + self.user_frame_userrole.setCurrentText("") + self.admin_action_changed() + + def delete_user(self): + if self.user_delete_confirm.isChecked(): + username = self.user_delete_frame_user_select.currentText() + self.db.deleteUser(username) + self.user_delete_frame_user_select.removeItem( + self.user_delete_frame_user_select.currentIndex() + ) + self.user_delete_confirm.setChecked(False) + else: + self.user_delete_err_message.setText( + "Bitte bestätigen Sie die Löschung des Nutzers" + ) # TODO: implement error message + # ic("please confirm the deletion of the user") + + def update_user_data(self): + username = self.user_edit_frame_user_select.currentText() + password = ( + self.user_edit_frame_new_password.text() + if self.user_edit_frame_new_password.text() != "" + else None + ) + role = ( + self.user_edit_frame_role_select.currentText() + if self.user_edit_frame_role_select.currentText() != "" + else None + ) + + userdata = AdminCommands().create_password(password) + data = { + "password": f"{userdata[1]}{userdata[0]}", + "salt": userdata[1], + "role": role, + } + data = {key: value for key, value in data.items() if value is not None} + print(data) + self.db.updateUser(username=username, data=data) + self.user_edit_frame_user_select.setCurrentText("") + self.user_edit_frame_new_password.clear() + self.user_edit_frame_role_select.setCurrentText("") + + def edit_faculty_member_action(self): + def __gen_fullname(fname, lname, data): + if fname == "" and lname == "": + return data[3] + if fname == "" and lname != "": + return f"{lname} {data[1]}" + if fname != "" and lname == "": + return f"{data[2]} {fname}" + if fname != "" and lname != "": + return f"{lname} {fname}" + + # get the data and use new value if it is not none and does not mach the old value + if self.edit_faculty_member_select_member.currentText() == "": + return + olddata = self.db.getFacultyMember( + self.edit_faculty_member_select_member.currentText() + ) + ic(olddata) + data = olddata + oldlname = data[2] + oldfname = data[1] + # take data except first and last entry + + titel = ( + self.edit_faculty_member_new_title.currentText() + if self.edit_faculty_member_new_title.currentText() != "Kein Titel" + else None + ) + fname = ( + self.edit_faculty_member_new_surname.text() + if self.edit_faculty_member_new_surname.text() != "" + else self.edit_faculty_member_select_member.currentText() + .split(" ")[1] + .strip() + ) + lname = ( + self.user_faculty_member_new_name.text() + if self.user_faculty_member_new_name.text() != "" + else self.edit_faculty_member_select_member.currentText() + .split(" ")[0] + .strip() + ) + fullname = __gen_fullname(fname, lname, data) + telnr = self.user_faculty_member_new_telnr.text() + mail = self.user_faculty_member_new_mail.text() + new_data = { + "titel": titel, + "fname": fname, + "lname": lname, + "fullname": fullname, + "mail": mail, + "telnr": telnr, + } + new_data = {key: value for key, value in new_data.items() if value != ""} + self.db.updateFacultyMember(data=new_data, oldlname=oldlname, oldfname=oldfname) + self.add_faculty_member_data() + self.edit_faculty_member_new_title.setCurrentText("") + self.edit_faculty_member_new_surname.clear() + self.user_faculty_member_new_name.clear() + self.user_faculty_member_new_telnr.clear() + self.user_faculty_member_new_mail.clear() + + def hide_all(self): + self.userCreateBox.hide() + self.userChangeDataBox.hide() + self.deleteUserBox.hide() + self.profChangeDataBox.hide() + + def admin_action_changed(self): + action = self.select_action_box.currentText() + roles = self.db.getRoles() + roles = [role[0] for role in roles] + # remove duplicates + roles = list(dict.fromkeys(roles)) + users = self.db.getUsers() + users = [user[2] for user in users] + users.remove(self.active_user) + if "admin" in users: + users.remove("admin") + if action == "Nutzer anlegen": + self.hide_all() + self.userCreateBox.show() + self.user_frame_userrole.clear() + self.user_frame_userrole.addItems(roles) + elif action == "Nutzer aktualisieren": + self.hide_all() + self.userChangeDataBox.show() + self.user_edit_frame_role_select.addItems(roles) + self.user_edit_frame_user_select.addItems(users) + elif action == "Nutzer löschen": + self.hide_all() + self.deleteUserBox.show() + self.user_delete_frame_user_select.addItems(users) + self.user_delete_frame_user_select.setCurrentText("") + self.user_delete_frame_user_select.addItems(users) + + elif action == "Lehrperson bearbeiten": + self.hide_all() + self.profChangeDataBox.show() + self.add_faculty_member_data() + self.edit_faculty_member_new_title.addItems(PROF_TITLES) + + else: + self.hide_all() + return + + def add_faculty_member_data(self): + faculty_members = self.db.getFacultyMembers() + names = [f"{member[5]}" for member in faculty_members] + self.edit_faculty_member_select_member.clear() + self.edit_faculty_member_select_member.addItems(names) + self.edit_faculty_member_select_member.addItem("") + self.edit_faculty_member_select_member.setCurrentText("") + + def tabW2_changed(self): + + if self.tabWidget_2.currentIndex() == 0: + self.stackedWidget_4.setCurrentIndex(0) + else: + self.stackedWidget_4.setCurrentIndex(1) + + def generateSemester(self, today=False): + """Generates the current semester. + + Args: + ----- + today (bool, optional): If True, the current semester is generated. Defaults to False. + Returns: + -------- + str: The current semester + """ + if today: + return generateSemesterByDate() + currentYear = self.sem_year.text() + currentYear = int(currentYear[-2:]) + + semester = ( + self.sem_sommer.text() + if self.sem_sommer.isChecked() + else self.sem_winter.text() + ) + if semester == "Sommer": + return "SoSe " + str(currentYear) + else: + return f"WiSe {currentYear}/{currentYear+1}" + + def display_detailed_data(self): + selected_semester = self.statistics_table.item( + self.statistics_table.currentRow(), 0 + ).text() + # get all apparats from the selected semester + data = self.db.getApparatsBySemester(selected_semester) + # replace keys for german names + # split to two lists + created = {"Erstellt": data["created"]} + deleted = {"Gelöscht": data["deleted"]} + created_status = StatusWidget(created, selected_semester) + deleted_status = StatusWidget(deleted, selected_semester) + created_status.setSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + deleted_status.setSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + + # add them to the gridLayout_4 + self.gridLayout_4.addWidget(created_status, 1, 0) + self.gridLayout_4.addWidget(deleted_status, 1, 1) + # self.setStatisticTableSize() + created_status.person_double_clicked.connect(self.open_apparat) + created_status.setToolTip("Doppelklick um den Semesterapparat zu öffnen") + deleted_status.setToolTip("Nur zur Übersicht") + # set deleted_status background to slightly gray + + def open_apparat(self, header: str, apparat: str, parent_depth: int): + print(header) + if header == "deleted" and parent_depth == 2: + # TODO: warn message here + print("warning") + if parent_depth == 1: + print(apparat) + # person selected case - open all apparats from this person in the tableWidget + self.tableWidget.setRowCount(0) + prof_id = self.db.getProfId(apparat.split("(")[0].strip()) + apparats = self.db.getApparatsByProf(prof_id) + for app in apparats: + print(app) + # set the items 0 = clickable checkbox, 1 = appname, 2 = profname, 3 = fach + # insert new row + self.tableWidget.insertRow(0) + self.tableWidget.setItem(0, 0, QtWidgets.QTableWidgetItem("")) + self.tableWidget.setItem(0, 1, QtWidgets.QTableWidgetItem(app[1])) + self.tableWidget.setItem(0, 2, QtWidgets.QTableWidgetItem(str(app[4]))) + self.tableWidget.setItem(0, 3, QtWidgets.QTableWidgetItem(app[2])) + self.tableWidget.setItem(0, 4, QtWidgets.QTableWidgetItem(app[3])) + # replace the 0 with a checkbox + checkbox = QtWidgets.QCheckBox() + checkbox.setChecked(False) + self.tableWidget.setCellWidget(0, 0, checkbox) + # if i[9] is 1, set the background of the row to red + if int(app[9]) == 1: + for j in range(5): + self.tableWidget.item(0, j).setBackground( + QtGui.QColor(235, 74, 71) + ) + # disable the checkbox + self.tableWidget.cellWidget(0, 0).setEnabled(False) + # set the tooltip + self.tableWidget.cellWidget(0, 0).setToolTip( + "Dieser Semesterapparat kann nicht gelöscht werden, da er bereits gelöscht wurde" + ) + elif parent_depth == 2: + # apparat selected case - open the apparat in the frame + self.load_app_data(apparat) + # change tab focus to tab 0 + self.tabWidget.setCurrentIndex(0) + return + + def gridchange(self): + if self.check_deletable.isChecked(): + self.box_semester.setEnabled(False) + self.box_semester.clear() + self.box_appnrs.setEnabled(False) + self.box_appnrs.clear() + self.box_person.setEnabled(False) + self.box_person.clear() + self.box_fach.setEnabled(False) + self.box_fach.clear() + self.box_erstellsemester.setEnabled(False) + self.box_erstellsemester.clear() + self.box_dauerapp.setEnabled(False) + self.box_dauerapp.clear() + else: + self.box_semester.setEnabled(True) + self.box_appnrs.setEnabled(True) + self.box_person.setEnabled(True) + self.box_fach.setEnabled(True) + self.box_erstellsemester.setEnabled(True) + self.box_dauerapp.setEnabled(True) + self.tab_changed() + + def populate_tab(self): + # add default values to the dropdowns + self.box_appnrs.clear() + self.box_appnrs.addItem("") + self.box_appnrs.setCurrentText("") + self.box_person.clear() + self.box_person.addItem("") + self.box_person.setCurrentText("") + self.box_fach.clear() + self.box_fach.addItem("") + self.box_fach.setCurrentText("") + self.box_erstellsemester.clear() + self.box_erstellsemester.addItem("") + self.box_erstellsemester.setCurrentText("") + self.box_semester.clear() + self.box_semester.addItem("") + self.box_semester.setCurrentText("") + self.box_dauerapp.clear() + self.box_dauerapp.addItems(["Ja", "Nein", ""]) + self.box_dauerapp.setCurrentText("") + # add custom vaules + appnrs = self.db.getUnavailableApparatNumbers() + apparats = natsorted(appnrs) + apparats = [str(apparat) for apparat in apparats] + self.box_appnrs.addItems(apparats) + persons = self.db.getProfs() + self.box_person.addItems([f"{person[3]}, {person[2]}" for person in persons]) + self.box_fach.addItems(subject[1] for subject in self.db.getSubjects()) + semester = self.db.getSemersters() + self.box_erstellsemester.addItems(semester) + self.statistics_table.setRowCount(0) + + # set data for table and graph in tab 2 tableWidget_3 + data = self.db.getApparatCountBySemester() + data = custom_sort(data) + # self.tabWidget_3.clear() + self.tabWidget_3.removeTab(1) + self.statistics_table.setRowCount(len(data)) + for i in range(len(data)): + self.statistics_table.setItem(i, 0, QtWidgets.QTableWidgetItem(data[i][0])) + self.statistics_table.setItem( + i, 1, QtWidgets.QTableWidgetItem(str(data[i][1])) + ) + self.statistics_table.setItem( + i, 2, QtWidgets.QTableWidgetItem(str(data[i][2])) + ) + self.statistics_table.resizeColumnsToContents() + self.statistics_table.resizeRowsToContents() + # self.setStatisticTableSize() + + # get height of the table + # create graph + + graph_data = { + "x": [i[0] for i in data], + "y": {"Erstellt": [i[1] for i in data], "Gelöscht": [i[2] for i in data]}, + } + graph = DataGraph(title="Erstellte und gelöschte Apparate", data=graph_data) + + # place the graph into tabWidget_3 + self.tabWidget_3.addTab(graph, "Graph") + self.tabWidget_3.setCurrentIndex(0) + + def tab_changed(self): + curr_tab = self.tabWidget.currentIndex() + if curr_tab == 0: # create tab + return + elif curr_tab == 1: # statistics tab + self.populate_tab() + + def populate_dropdown(self, box, data): + box.clear() + box.addItem("") + box.setCurrentText("") + box.addItems(data) + + def delete_selected_apparats(self): + # get all selected apparats + selected_apparats = [] + 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_apparat_rows.append(i) + # delete all selected apparats + print(selected_apparats) + for apparat in selected_apparats: + self.db.deleteApparat(apparat, self.generateSemester(today=True)) + for row in selected_apparat_rows: + # set the background of the row to red + for j in range(5): + self.tableWidget.item(row, j).setBackground(QtGui.QColor(235, 74, 71)) + # refresh the table + self.populate_tab() + self.btn_del_select_apparats.setEnabled(False) + + def statistics(self): + """Generate the statistics based on the selected filters.""" + self.db_err_message.setText("") + self.btn_del_select_apparats.setEnabled(True) + self.btn_notify_for_deletion.setEnabled(True) + params = { + "appnr": ( + self.box_appnrs.currentText() + if self.box_appnrs.currentText() != "" + else None + ), + "prof_id": ( + self.db.getProfId(self.box_person.currentText()) + if self.box_person.currentText() != "" + else None + ), + "fach": ( + self.box_fach.currentText() + if self.box_fach.currentText() != "" + else None + ), + "erstellsemester": ( + self.box_erstellsemester.currentText() + if self.box_erstellsemester.currentText() != "" + else None + ), + "dauer": ( + "1" + if self.box_dauerapp.currentText() == "Ja" + else "0" if self.box_dauerapp.currentText() == "Nein" else None + ), + "endsemester": ( + self.box_semester.currentText() + if self.box_semester.currentText() != "" + else None + ), + "deletable": "True" if self.check_deletable.isChecked() else None, + "deletesemester": self.generateSemester(today=True), + } + params = { + key: value for key, value in params.items() if value is not None + } # remove empty lines to prevent errors + print(params) + params = { + key: value for key, value in params.items() if value != "Alle" + } # remove empty lines to prevent errors + print(params) + result = self.db.statistic_request(**params) + # add QTableWidgetItems to the table + self.tableWidget.setRowCount(len(result)) + if len(result) == 0: + self.db_err_message.setText("Keine Ergebnisse gefunden") + return + for i in range(len(result)): + print(result[i]) + # set the items 0 = clickable checkbox, 1 = appname, 2 = profname, 3 = fach + self.tableWidget.setItem(i, 0, QtWidgets.QTableWidgetItem("")) + self.tableWidget.setItem(i, 1, QtWidgets.QTableWidgetItem(result[i][1])) + self.tableWidget.setItem( + i, 2, QtWidgets.QTableWidgetItem(str(result[i][4])) + ) + self.tableWidget.setItem(i, 3, QtWidgets.QTableWidgetItem(result[i][2])) + self.tableWidget.setItem(i, 4, QtWidgets.QTableWidgetItem(result[i][3])) + # replace the 0 with a checkbox + checkbox = QtWidgets.QCheckBox() + checkbox.setChecked(False) + self.tableWidget.setCellWidget(i, 0, checkbox) + # if i[9] is 1, set the background of the row to red + if int(result[i][9]) == 1: + for j in range(5): + self.tableWidget.item(i, j).setBackground(QtGui.QColor(235, 74, 71)) + # disable the checkbox + self.tableWidget.cellWidget(i, 0).setEnabled(False) + # set the tooltip + self.tableWidget.cellWidget(i, 0).setToolTip( + "Dieser Semesterapparat kann nicht gelöscht werden, da er bereits gelöscht wurde" + ) + + def populate_frame(self, appdata: ApparatData): + # populate the frame with the data from the database + # ic(appdata) + self.drpdwn_app_nr.setCurrentText(str(appdata.appnr)) + self.prof_title.setText(appdata.prof_title) + prof_name = appdata.profname.split(" ") + if len(prof_name) > 2: + fname = " ".join(prof_name[1:]) + lname = prof_name[0] + prof_name = f"{lname}, {fname}" + else: + prof_name = ", ".join(prof_name) + self.drpdwn_prof_name.setCurrentText(prof_name) + self.prof_mail.setText(appdata.prof_mail) + self.prof_tel_nr.setText(appdata.prof_tel) + self.app_name.setText(appdata.appname) + print("changing dropdown app_fach from '' to ", appdata.app_fach) + self.app_fach.setCurrentText(appdata.app_fach) + print("changed dropdown app_fach to ", self.app_fach.currentText()) + if appdata.semester is not None: + self.sem_sommer.setChecked( + True if appdata.semester.split(" ")[0] == "SoSe" else False + ) + self.sem_winter.setChecked( + True if appdata.semester.split(" ")[0] == "WiSe" else False + ) + self.sem_year.setText(appdata.semester.split(" ")[1]) + else: + self.sem_sommer.setChecked( + True if appdata.erstellsemester.split(" ")[0] == "SoSe" else False + ) + self.sem_winter.setChecked( + True if appdata.erstellsemester.split(" ")[0] == "WiSe" else False + ) + self.sem_year.setText(appdata.erstellsemester.split(" ")[1]) + self.check_eternal_app.setChecked(appdata.dauerapp) + self.prof_id_adis.setText(str(appdata.prof_adis_id)) + self.apparat_id_adis.setText(str(appdata.apparat_adis_id)) + self.app_group_box.setEnabled(True) + self.groupBox_2.hide() + self.groupBox.hide() + self.validateLoadedData() + + def validateLoadedData(self): + self.validate_prof_mail() + self.validate_prof_name() + self.validate_prof_tel() + self.validate_app_name() + self.validate_app_fach() + self.validate_semester() + + def update_apparat(self): + appdata = ApparatData() + appdata.app_fach = self.app_fach.currentText() + appdata.appname = self.app_name.text() + appdata.appnr = self.active_apparat + appdata.dauerapp = self.check_eternal_app.isChecked() + appdata.prof_mail = self.prof_mail.text() + appdata.prof_tel = self.prof_tel_nr.text() + appdata.prof_title = self.prof_title.text() + appdata.profname = self.drpdwn_prof_name.currentText() + appdata.semester = ( + self.sem_sommer.text() + " " + self.sem_year.text() + if self.sem_sommer.isChecked() + else self.sem_winter.text() + " " + self.sem_year.text() + ) + appdata.prof_adis_id = self.prof_id_adis.text() + self.add_files() + appdata.apparat_adis_id = self.apparat_id_adis.text() + + self.db.updateApparat(appdata) + + self.update_app_media_list() + self.cancel_active_selection.click() + self.check_send_mail.show() + self.chkbx_show_del_media.show() + self.cancel_active_selection.setEnabled(False) + + def confirm_popup(self, message: str): + popup = popus_confirm() + popup.setupUi() + popup.textEdit.setReadOnly(True) + popup.textEdit.setText(message) + + popup.exec() + return popup.result() + + def threads(self): + while True: + self.validate_prof_mail() + self.validate_prof_name() + self.validate_prof_tel() + self.validate_app_name() + self.validate_app_fach() + self.validate_semester() + + def thread_check(self): + self.prof_mail.textChanged.connect(self.validate_prof_mail) + self.drpdwn_prof_name.editTextChanged.connect(self.validate_prof_name) + self.prof_tel_nr.textChanged.connect(self.validate_prof_tel) + self.app_name.textChanged.connect(self.validate_app_name) + self.app_fach.currentTextChanged.connect(self.validate_app_fach) + self.sem_year.textChanged.connect(self.validate_semester) + self.check_eternal_app.stateChanged.connect(self.validate_semester) + + def validate_prof_name(self): + if self.app_group_box.isEnabled(): + if "," in self.drpdwn_prof_name.currentText(): + self.drpdwn_prof_name.setStyleSheet("border: 1px solid green;") + self.profname_mand.setText("") + self.change_state(0, 1) + else: + self.drpdwn_prof_name.setStyleSheet("border: 1px solid red;") + self.profname_mand.setText("*") + self.change_state(0, 0) + else: + pass + # self.drpdwn_prof_name.setStyleSheet("border: 1px solid black;") + + def validate_prof_mail(self): + if self.app_group_box.isEnabled(): + if self.prof_mail.hasAcceptableInput(): + self.prof_mail.setStyleSheet("border: 1px solid green;") + self.mail_mand.setText("") + self.change_state(1, 1) + else: + self.prof_mail.setStyleSheet("border: 1px solid red;") + self.mail_mand.setText("*") + self.change_state(1, 0) + else: + self.prof_mail.setStyleSheet("border: 1px solid black;") + + def validate_prof_tel(self): + if self.app_group_box.isEnabled(): + if self.prof_tel_nr.text() != "": + self.prof_tel_nr.setStyleSheet("border: 1px solid green;") + self.telnr_mand.setText("") + self.change_state(2, 1) + else: + self.prof_tel_nr.setStyleSheet("border: 1px solid red;") + self.telnr_mand.setText("*") + self.change_state(2, 0) + + def validate_app_name(self): + if self.app_group_box.isEnabled(): + if self.app_name.hasAcceptableInput(): + self.app_name.setStyleSheet("border: 1px solid green;") + self.appname_mand.setText("") + self.change_state(3, 1) + else: + self.app_name.setStyleSheet("border: 1px solid red;") + self.appname_mand.setText("*") + self.change_state(3, 0) + + def validate_app_fach(self): + if self.app_group_box.isEnabled(): + if self.app_fach.currentText() != "": + self.app_fach.setStyleSheet("border: 1px solid green;") + self.fach_mand.setText("") + self.change_state(4, 1) + else: + self.app_fach.setStyleSheet("border: 1px solid red;") + self.fach_mand.setText("*") + self.change_state(4, 0) + + def validate_semester(self): + if self.app_group_box.isEnabled(): + if ( + (self.sem_sommer.isChecked() or self.sem_winter.isChecked()) + and self.sem_year.hasAcceptableInput() + ) or self.check_eternal_app.isChecked(): + self._mand.setText("") + self.change_state(5, 1) + self.check_eternal_app.setEnabled(True) + else: + self._mand.setText("*") + self.change_state(5, 0) + self.check_eternal_app.setEnabled(False) + + def change_state(self, index, state): + global valid_input + valid_input = list(valid_input) + valid_input[index] = state + valid_input = tuple(valid_input) + + def set_state(self): + # set state of semester and year + if self.check_eternal_app.isChecked(): + self.sem_winter.setEnabled(False) + self.sem_sommer.setEnabled(False) + self.sem_year.setEnabled(False) + else: + self.sem_winter.setEnabled(True) + self.sem_sommer.setEnabled(True) + self.sem_year.setEnabled(True) + + def validate_fields(self): + return all(valid_input) + + # def req_fields_filled(self): + # # check if all required fields are filled + # values = [] + # for item in self.app_group_box.findChildren(QtWidgets.QLabel): + # # if label name contains "req" and the text is empty, return false + # if "mand" in item.objectName() and item.text() == "": + # values.append(True) + # elif "mand" in item.objectName() and item.text() != "": + # values.append(False) + # return all(values) + # + def buttonClicked(self): + print("Button clicked") + + def set_prof_data(self): + if "," not in self.drpdwn_prof_name.currentText(): + self.prof_mail.clear() + self.prof_tel_nr.clear() + return + selected_prof = self.drpdwn_prof_name.currentText() + data = self.db.getProfData(selected_prof) + # ic(data) + prof_title = data[2] + if prof_title == "None": + prof_title = "Kein Titel" + self.prof_title.setText(prof_title) + self.prof_tel_nr.setText(data[1]) + self.prof_mail.setText(data[0]) + + def get_index_of_value(self, table_widget, value): + 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 + ): + return i, j + return None + + def load_app_data(self, app_id=None): + self.cancel_active_selection.setEnabled(True) + if isinstance(app_id, str): + # double click the tableWidget_apparate row with the given app_id + row, column = self.get_index_of_value(self.tableWidget_apparate, app_id) + # set the current index to the row + self.tableWidget_apparate.setCurrentCell(row, 0) + self.check_send_mail.hide() + app_pos = self.tableWidget_apparate.currentIndex() + appnr = self.tableWidget_apparate.item(app_pos.row(), 0).text() + appname = self.tableWidget_apparate.item(app_pos.row(), 1).text() + self.sem_sommer.setEnabled(False) + self.sem_winter.setEnabled(False) + self.sem_year.setEnabled(False) + self.dokument_list.setRowCount(0) + self.chkbx_show_del_media.setEnabled(True) + appdata = self.db.getApparatData(appnr, appname) + self.populate_frame(appdata) + self.btn_apparat_save.hide() + self.btn_reserve.show() + self.chkbx_show_del_media.show() + self.groupBox_2.show() + self.groupBox.show() + + self.drpdwn_app_nr.setDisabled(True) + self.update_app_media_list() + self.update_documemt_list() + + def update_documemt_list(self): + app_id = self.active_apparat + prof_id = self.db.getProfId( + self.drpdwn_prof_name.currentText().replace(", ", " ") + ) + files = self.db.getFiles(app_id, prof_id) + for file in files: + self.dokument_list.insertRow(0) + self.dokument_list.setItem(0, 0, QtWidgets.QTableWidgetItem(file[0])) + self.dokument_list.setItem(0, 1, QtWidgets.QTableWidgetItem(file[1])) + self.dokument_list.setItem(0, 2, QtWidgets.QTableWidgetItem("")) + self.dokument_list.setItem(0, 3, QtWidgets.QTableWidgetItem("Database")) + + # def btn_load_apparat(self): + # # remove all rows from table + # #get all + # self.tableWidget_apparate.sortItems(0, QtCore.Qt.SortOrder.AscendingOrder) + # self.app_group_box.setDisabled(True) + # for child in self.app_group_box.findChildren(QtWidgets.QLineEdit): + # child.clear() + + def btn_create_new_apparat(self): + self.groupBox.show() + self.groupBox_2.show() + global valid_input + # *create a new apparat + self.btn_apparat_save.show() if self.btn_apparat_save.isHidden() else None + # clear dokumemt_list + self.dokument_list.setRowCount(0) + self.cancel_active_selection.setEnabled(True) + self.app_group_box.setEnabled(True) + + self.sem_year.setEnabled(True) + self.sem_sommer.setEnabled(True) + self.sem_winter.setEnabled(True) + self.chkbx_show_del_media.setEnabled(True) + self.drpdwn_app_nr.setEnabled(True) + self.app_fach.setEnabled(True) + + if self.tableWidget_apparat_media.rowCount() > 0: + self.tableWidget_apparat_media.setRowCount(0) + # clear all fields + for item in self.app_group_box.findChildren(QtWidgets.QLineEdit): + item.clear() + self.drpdwn_app_nr.clear() + self.prof_title.clear() + self.drpdwn_prof_name.clear() + # set drop down menu for apparat numbers to only available numbers + self.drpdwn_app_nr.addItems( + [str(i) for i in APP_NRS if i not in self.db.getUnavailableApparatNumbers()] + ) + + valid_input = (0, 0, 0, 0, 0, 0) + self.populate_prof_dropdown() + # self.horizontalLayout_6.show() + # increase size by 300px + + def update_progress_label(self, curr, total): + text = f"Medium {curr}/{total}" + self.logger.log_info(text) + self.progress_label.setText(text) + # update tableWidget_apparat_media + self.update_app_media_list() + + def hide_progress_label(self): + self.logger.log_info("Finished adding media, hiding progress label") + self.progress_label.hide() + self.progress_label.setText("Bitte warten...") + self.line_2.hide() + self.label_info.hide() + + def btn_add_medium(self): + if not self.app_group_box.isEnabled(): + self.confirm_popup("Bitte erst einen Apparat auswählen!") + return + + def __new_ui(): + dialog = QtWidgets.QDialog() + frame = Medien() + frame.setupUi(dialog) + dialog.exec() + mode = frame.get_mode() + data = frame.get_list_data() + return mode, data, dialog.result() + + self.progress_label.show() + self.line_2.show() + self.label_info.show() + self.progress_label.setText("Bitte warten...") + mode, data, result = __new_ui() + if result == 1: + if data == []: + self.confirm_popup("Bitte mindestens ein Medium hinzufügen!") + + app_id = self.active_apparat + prof_id = self.db.getProfId(self.drpdwn_prof_name.currentText()) + # check if app_id is in database + if not self.db.checkApparatExists(app_id): + # create apparat + self.btn_save_apparat() + # create a thread that updates the progress label after each medium + self.bookGrabber = BookGrabber( + mode=mode, app_id=app_id, prof_id=prof_id, data=data + ) + # grabber.mode = mode + # grabber.data = data + # grabber.app_id = app_id + # grabber.prof_id = prof_id + self.bookGrabber.finished.connect(self.bookGrabber.deleteLater) + self.bookGrabber.finished.connect(self.hide_progress_label) + self.bookGrabber.finished.connect(self.update_app_media_list) + self.bookGrabber.updateSignal.connect(self.update_progress_label) + + self.bookGrabber.start() + # run grabber.deletelater + + # for book in data: + # # self.progress_label.setText(f"Medium {ct}/{len(data)}") + # # update the progress label + # self.logger.log_info(f"trying to add BookData for Signature {book}") + # webdata = WebRequest().get_ppn(book).get_data() + # bd: BookData = BibTextTransformer(mode).get_data(webdata).return_data() + # bd.signature = book + # self.db.addBookToDatabase(bookdata=bd, app_id=app_id, prof_id=prof_id) + + # get all media list books + + else: + return + + def check_availability(self): + # self.threadeds.clear() + + def _update_progress(current, all_titles): + self.avail_status.setText("{}/{}".format(current, all_titles)) + + def _hide_progress_label(): + self.label_20.hide() + self.avail_status.hide() + self.avail_status.setText("0/0") + + # get all links from the table + # if no index in tableWidget_apparat_media is selected, check all + if self.tableWidget_apparat_media.currentRow() == -1: + links = [ + self.tableWidget_apparat_media.item(i, 1).text() + for i in range(self.tableWidget_apparat_media.rowCount()) + if self.tableWidget_apparat_media.item(i, 4).text() == "❌" + or self.tableWidget_apparat_media.item(i, 4).text() == "" + ] + else: + links = [ + self.tableWidget_apparat_media.item( + self.tableWidget_apparat_media.currentRow(), 1 + ).text() + ] + items = len(links) + self.label_20.setText("Verfügbarkeit wird geprüft, bitte warten...") + self.label_20.show() + self.avail_status.setText(f"0/{items}") + self.avail_status.show() + books = self.db.getBooks( + self.active_apparat, + self.db.getProfId(self.drpdwn_prof_name.currentText()), + deleted=0, + ) + + # thread = QThread() + appnumber = self.active_apparat + print(links) + self.availChecker = AvailChecker(links, appnumber, books=books) + # availcheck.moveToThread(thread) + # availcheck.finished.connect(thread.quit) + self.availChecker.finished.connect(self.availChecker.deleteLater) + self.availChecker.finished.connect(self.update_app_media_list) + self.availChecker.updateProgress.connect(_update_progress) + self.availChecker.finished.connect(_hide_progress_label) + self.availChecker.start() + # kill availcheck after completion + + # self.threadeds.append(thread) + # self.grabbers.append(availcheck) + + def btn_cancel_active_selection(self): + # clear the rows of the table + self.tableWidget_apparat_media.setRowCount(0) + self.dokument_list.setRowCount(0) + self.app_group_box.setEnabled(False) + self.app_fach.setCurrentText("") + self.chkbx_show_del_media.hide() + self.check_send_mail.hide() + self.btn_reserve.hide() + self.groupBox_2.hide() + self.groupBox.hide() + self.check_eternal_app.setEnabled(False) + # set all radio buttons to unchecked + self.sem_sommer.setChecked(False) + self.sem_winter.setChecked(False) + + for child in self.app_group_box.findChildren(QtWidgets.QLineEdit): + child.clear() + + def update_app_media_list(self): + deleted = 0 if not self.chkbx_show_del_media.isChecked() else 1 + app_id = self.active_apparat + prof_id = self.db.getProfId(self.drpdwn_prof_name.currentText()) + books: list[dict[int, BookData, int]] = self.db.getBooks( + app_id, prof_id, deleted + ) + + # print(books) + # take the dataclass from the tuple + # booklist:list[BookData]=[book[0] for book in books] + self.tableWidget_apparat_media.setRowCount(0) + for book in books: + book["id"] + book_data = book["bookdata"] + availability = book["available"] + # bd = BookData().from_string(book) + # print(bd, type(bd)) + # create a new row below the last one + self.tableWidget_apparat_media.insertRow( + self.tableWidget_apparat_media.rowCount() + ) + # #set the data + self.tableWidget_apparat_media.setItem( + self.tableWidget_apparat_media.rowCount() - 1, + 0, + QtWidgets.QTableWidgetItem(book_data.title), + ) + self.tableWidget_apparat_media.setItem( + self.tableWidget_apparat_media.rowCount() - 1, + 1, + QtWidgets.QTableWidgetItem(book_data.signature), + ) + self.tableWidget_apparat_media.setItem( + self.tableWidget_apparat_media.rowCount() - 1, + 2, + QtWidgets.QTableWidgetItem(book_data.edition), + ) + self.tableWidget_apparat_media.setItem( + self.tableWidget_apparat_media.rowCount() - 1, + 3, + QtWidgets.QTableWidgetItem(book_data.author), + ) + self.tableWidget_apparat_media.setItem( + self.tableWidget_apparat_media.rowCount() - 1, + 6, + QtWidgets.QTableWidgetItem(book_data.link), + ) + if availability == 1: + # display green checkmark at column 4 in the row + self.tableWidget_apparat_media.setItem( + self.tableWidget_apparat_media.rowCount() - 1, + 4, + QtWidgets.QTableWidgetItem("✅"), + ) + # set tooltip + self.tableWidget_apparat_media.item( + self.tableWidget_apparat_media.rowCount() - 1, 4 + ).setToolTip("Das Medium wurde im Apparat gefunden") + else: + self.tableWidget_apparat_media.setItem( + self.tableWidget_apparat_media.rowCount() - 1, + 4, + QtWidgets.QTableWidgetItem("❌"), + ) + self.tableWidget_apparat_media.item( + self.tableWidget_apparat_media.rowCount() - 1, 4 + ).setToolTip("Das Medium wurde nicht im Apparat gefunden") + + # make table link clickable + self.tableWidget_apparat_media.itemClicked.connect(self.open_link) + + def open_link(self, item): + def __openLink(link): + if link == "": + return + if "http" not in link: + link = "https://" + link + + webbrowser.open(link) + + # get the name of the column + columnname = self.tableWidget_apparat_media.horizontalHeaderItem( + item.column() + ).text() + if columnname == "Link": + __openLink(item.text()) + else: + pass + + def text_to_clipboard(self): + app_id = self.active_apparat + text = f"SQ=select distinct akkey from aupr01 where aufst='{app_id}' union select pr_isn from aks4pd where akruf ='{app_id}'" + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(text) + + def populate_prof_dropdown(self): + profs = self.db.getProfs() + # add empty entry to dropdown and set it as current + self.drpdwn_prof_name.addItem("Kein Name") + for prof in profs: + self.drpdwn_prof_name.addItem(f"{prof[3]}, {prof[2]}") + + def add_document(self): + print("Add document") + picker = FilePicker() + files = picker.pick_files() + + for file in files: + print(file) + filename = file.split("/")[-1] + filetype = filename.split(".")[-1] + self.dokument_list.insertRow(0) + self.dokument_list.setItem(0, 0, QtWidgets.QTableWidgetItem(filename)) + self.dokument_list.setItem(0, 1, QtWidgets.QTableWidgetItem(filetype)) + self.dokument_list.setItem(0, 2, QtWidgets.QTableWidgetItem("*")) + self.dokument_list.setItem(0, 3, QtWidgets.QTableWidgetItem(file)) + # set tooltip of row 3 to the file path for each row + self.dokument_list.item(0, 3).setToolTip(file) + + # self.db.insert_file(files, self.active_apparat, self.db.get_prof_id(self.drpdwn_prof_name.currentText())) + + def open_document(self): + _selected_doc_name = "" + _selected_doc_filetype = "" + try: + _selected_doc_name = self.dokument_list.item( + self.dokument_list.currentRow(), 0 + ).text() + _selected_doc_location = self.dokument_list.item( + self.dokument_list.currentRow(), 3 + ).text() + _selected_doc_filetype = self.dokument_list.item( + self.dokument_list.currentRow(), 1 + ).text() + except AttributeError: + self.confirm_popup("Bitte erst ein Dokument auswählen!") + return + if not _selected_doc_location == "Database": + path = Path(_selected_doc_location) + path: Path = path + "/" + _selected_doc_name + if os.getenv("OS") == "Windows_NT": + path = path.resolve() + os.startfile(path) + else: + path = path.resolve() + os.system(f"open {path}") + else: + recreateFile( + _selected_doc_name, self.active_apparat, filetype=_selected_doc_filetype + ) + # # if ~ in path, replace it with the home directory + # if "~" in path: + # path = path.replace("~", str(Path.home())) + # path = Path(path) + + def add_media_from_file(self): + def __open_dialog(signatures): + dialog = QtWidgets.QDialog() + frame = parsed_titles_ui() + frame.setupUi(dialog) + dialog.show() + frame.signatures = signatures + frame.populate_table() + frame.progressBar.setMaximum(len(signatures)) + frame.progressBar.setValue(0) + frame.progressBar.show() + frame.count.setText(str(len(signatures))) + frame.toolButton.click() + data = frame.return_data() + print(data) + # if no data was returned, return + + return data + # get + + # if files are in the table, and are selected, check for books in the file + if self.dokument_list.rowCount() == 0: + return + else: + # if file is selected, check for books in the file + if self.dokument_list.currentRow() != -1: + print("File selected") + file = self.dokument_list.item( + self.dokument_list.currentRow(), 3 + ).text() + + file_type = self.dokument_list.item( + self.dokument_list.currentRow(), 1 + ).text() + file_location = self.dokument_list.item( + self.dokument_list.currentRow(), 3 + ).text() + file_name = self.dokument_list.item( + self.dokument_list.currentRow(), 0 + ).text() + if file_location == "Database": + # create a temporaty file to use, delete it after use + temp_file = tempfile.NamedTemporaryFile( + delete=False, suffix="." + file_type + ) + temp_file.write( + self.db.getBlob(file_name, int(self.active_apparat)) + ) + temp_file.close() + file = temp_file.name + print(file) + if file_type == "pdf": + # Todo: implement parser here + self.confirm_popup("PDF Dateien werden nochnicht unterstützt!") + return + if file_type == "csv": + signatures = csv_to_list(file) + data = __open_dialog(signatures) + # get the app_id and prof_id + app_id = self.active_apparat + prof_id = self.db.getProfId(self.drpdwn_prof_name.currentText()) + # add the data to the database + for book in data: + if type(book) != BookData: + continue + self.db.addBookToDatabase( + bookdata=book, app_id=app_id, prof_id=prof_id + ) + if file_type == "docx": + data = word_docx_to_csv(file) + signatures = [ + i + for i in data["Standnummer"].values + if i != "\u2002\u2002\u2002\u2002\u2002" + ] + data = __open_dialog(signatures) + # if no data was returned, return + if data == []: + return + # get the app_id and prof_id + app_id = self.active_apparat + prof_id = self.db.getProfId(self.drpdwn_prof_name.currentText()) + # add the data to the database + for book in data: + if type(book) != BookData: + continue + self.db.addBookToDatabase( + bookdata=book, app_id=app_id, prof_id=prof_id + ) + self.update_app_media_list() + print(len(signatures)) + + def btn_check_file_threaded(self): + print("Checking file") + # get active app_id and prof_id + self.tableWidget_apparate.setEnabled(False) + self.tableWidget_apparate.setToolTip( + "Bitte warten, bis alle Medien hinzugefügt wurden" + ) + app_id = self.active_apparat + prof_id = self.db.getProfId(self.drpdwn_prof_name.currentText()) + # check if apparat in database + + # if app_id not in database, create apparat + if not self.db.checkApparatExistsById(app_id): + # create apparat + print("Creating apparat") + self.btn_save_apparat() + if self.dokument_list.rowCount() == 0: + print("No file selected") + self.tableWidget_apparate.setEnabled(True) + self.tableWidget_apparate.setToolTip("") + return + else: + # if file is selected, check for books in the file + print("File selected") + file = self.dokument_list.item(self.dokument_list.currentRow(), 3).text() + + file_type = self.dokument_list.item( + self.dokument_list.currentRow(), 1 + ).text() + file_location = self.dokument_list.item( + self.dokument_list.currentRow(), 3 + ).text() + file_name = self.dokument_list.item( + self.dokument_list.currentRow(), 0 + ).text() + if file_location == "Database": + file = recreateFile(file_name, app_id, file_type, open=False) + else: + self.add_files() + if file_type == "pdf": + # Todo: implement parser here + self.confirm_popup("PDF Dateien werden noch nicht unterstützt!") + return + if file_type == "csv": + signatures = csv_to_list(file) + # add the data to the database + if file_type == "docx": + data = word_docx_to_csv(file) + signatures = [ + i + for i in data["Standnummer"].values + if i != "\u2002\u2002\u2002\u2002\u2002" + ] + + signatures = [i for i in signatures if i != ""] + # ic(signatures) + print("starting thread") + self.autoGrabber = BookGrabber( + mode="ARRAY", app_id=app_id, prof_id=prof_id, data=signatures + ) + # grabber.mode = "ARRAY" + # grabber.data = signatures + # grabber.app_id = app_id + # grabber.prof_id = prof_id + # grabber.moveToThread(thread) + self.label_info.show() + self.progress_label.show() + self.line_2.show() + # grabber.finished.connect(thread.quit) + self.autoGrabber.finished.connect(self.autoGrabber.deleteLater) + self.autoGrabber.finished.connect(self.hide_progress_label) + self.autoGrabber.finished.connect(self.update_app_media_list) + self.autoGrabber.finished.connect(self.unlock_apparate) + self.autoGrabber.updateSignal.connect(self.update_progress_label) + # worker.finished.connect(worker.deleteLater) + + self.autoGrabber.start() + # self.thread = thread + # kill grabber after completion + # self.grabbers.append(grabber) + + def unlock_apparate(self): + self.tableWidget_apparate.setEnabled(True) + self.tableWidget_apparate.setToolTip("") + + def btn_save_apparat(self): + def __clear_fields(): + self.drpdwn_app_nr.clear() + self.prof_title.clear() + self.drpdwn_prof_name.clearMask() + self.app_name.clear() + self.prof_mail.clear() + self.prof_tel_nr.clear() + self.app_fach.setCurrentText("") + self.app_name.clear() + self.sem_year.clear() + self.dokument_list.setRowCount(0) + self.sem_winter.setChecked(False) + self.sem_sommer.setChecked(False) + self.check_eternal_app.setChecked(False) + self.prof_id_adis.clear() + self.prof_id_adis.clear() + self.apparat_id_adis.clear() + self.drpdwn_prof_name.clear() + self.tableWidget_apparat_media.setRowCount(0) + self.app_group_box.setEnabled(False) + self.groupBox_2.hide() + self.groupBox.hide() + self.check_send_mail.setChecked(False) + self.cancel_active_selection.setEnabled(False) + + if not self.validate_fields(): + self.confirm_popup("Bitte alle Pflichtfelder ausfüllen!") + return + appd = ApparatData() + appd.appnr = self.active_apparat + appd.prof_title = ( + None if self.prof_title.text() == "" else self.prof_title.text() + ) + appd.profname = self.drpdwn_prof_name.currentText() + appd.appname = self.app_name.text() + appd.semester = self.generateSemester() + appd.dauerapp = 1 if self.check_eternal_app.isChecked() else 0 + appd.prof_tel = self.prof_tel_nr.text() + appd.prof_mail = self.prof_mail.text() + appd.app_fach = self.app_fach.currentText() + appd.erstellsemester = self.generateSemester() + appd.deleted = 0 + appd.prof_adis_id = self.prof_id_adis.text() + appd.apparat_adis_id = self.apparat_id_adis.text() + if self.dokument_list.rowCount() > 0: + self.add_files() + if not self.validate_fields(): + pass + self.db.createApparat(appd) + # if self.dokument_list.rowCount() > 0: + self.add_files() + appdata = self.db.getAllAparats() + # merge self.appdata and appdata, remove duplicates + self.apparats = list(set(self.apparats + appdata)) + self.apparats = natsorted(self.apparats, key=lambda x: x[4], reverse=True) + self.update_apparat_list() + + # self.btn_load_apparat() + + if self.check_send_mail.isChecked(): + self.contact_prof( + apparat=appd.appnr, mail="Information zum Semesterapparat" + ) + __clear_fields() + + def send_mail_preview(self): + pass + + @property + def active_apparat(self): + return self.drpdwn_app_nr.currentText() + + def add_files(self): + files = [] + for i in range(self.dokument_list.rowCount()): + files.append( + { + "name": self.dokument_list.item(i, 0).text(), + "type": self.dokument_list.item(i, 1).text(), + "date": self.dokument_list.item(i, 2).text(), + "path": self.dokument_list.item(i, 3).text(), + } + ) + self.dokument_list.item(i, 2).setText("") + self.db.insertFile( + files, + self.active_apparat, + self.db.getProfId(self.drpdwn_prof_name.currentText()), + ) + + def update_apparat_list(self): + # get a list of new apparats based on self.apparats and self.old_apparats + new_apparats = [ + apparat for apparat in self.apparats if apparat not in self.old_apparats + ] + print(new_apparats) + # insert the new apparats into the table + for apparat in new_apparats: + self.insert_apparat_into_table(apparat) + # sort the table by apparat number using natural sorting + self.tableWidget_apparate.sortItems(0, QtCore.Qt.SortOrder.AscendingOrder) + self.old_apparats = self.apparats + + def insert_apparat_into_table(self, apparat): + # ic(apparat) + def __dauer_check(apparat): + return "Ja" if apparat[7] == 1 else "Nein" + + self.tableWidget_apparate.insertRow(0) + self.tableWidget_apparate.setItem( + 0, 0, QtWidgets.QTableWidgetItem(str(apparat[4])) + ) + self.tableWidget_apparate.setItem( + 0, 1, QtWidgets.QTableWidgetItem(str(apparat[1])) + ) + self.tableWidget_apparate.setItem( + 0, + 2, + QtWidgets.QTableWidgetItem( + self.db.getProfNameById(apparat[2], add_title=False) + ), + ) + self.tableWidget_apparate.setItem( + 0, + 3, + QtWidgets.QTableWidgetItem( + str(apparat[8]) if apparat[8] is not None else apparat[5] + ), + ) + self.tableWidget_apparate.setItem( + 0, 4, QtWidgets.QTableWidgetItem(__dauer_check(apparat)) + ) + self.tableWidget_apparate.setItem( + 0, 5, QtWidgets.QTableWidgetItem(str(apparat[13])) + ) + self.logger.log_info(f"Inserted apparat {apparat[4]}") + + def open_context_menu(self, position): + menu = QtWidgets.QMenu() + extend_action = menu.addAction("Verlängern") + contact_action = menu.addAction("Kontaktieren") + delete_action = menu.addAction("Löschen") + remind_action = menu.addAction("Erinnerung") + menu.addAction(extend_action) + menu.addActions([contact_action, delete_action, remind_action]) + extend_action.triggered.connect(self.extend_apparat) + delete_action.triggered.connect(self.delete_apparat) + contact_action.triggered.connect(self.contact_prof) + remind_action.triggered.connect(self.reminder) + menu.exec(self.tableWidget_apparate.mapToGlobal(position)) + + def statistics_table_context_menu(self, position): + menu = QtWidgets.QMenu() + restore_action = menu.addAction("Wiederherstellen") + menu.addAction(restore_action) + restore_action.triggered.connect(self.restore_apparat) + menu.exec(self.tableWidget.mapToGlobal(position)) + + def restore_apparat(self): + row = self.tableWidget.currentRow() + apparat = self.tableWidget.item(row, 1).text() + ic(apparat) + apparat_id = self.db.getApparatId(apparat) + # restore the apparat + self.db.restoreApparat(apparat_id) + # update the table + self.reload() + + def reminder(self): + self.logger.log_info("Opening reminder dialog") + dialog = QtWidgets.QDialog() + reminder = reminder_ui() + reminder.setupUi(dialog) + dialog.exec() + if dialog.result() == QtWidgets.QDialog.DialogCode.Accepted: + data = reminder.return_message() + print(data) + self.db.addMessage( + data, + self.active_user, + self.active_apparat if self.active_apparat != "" else None, + ) + self.calendarWidget.setMessages([data]) + self.calendarWidget.updateCells() + # self.db.update_bookdata(data, book_id) + # self.db.update_bookdata(data) + self.logger.log_info("Commited message to database") + # self.update_app_media_list() + + def get_reminders(self): + messages = self.db.getMessages() + self.logger.log_info(f"Got {len(messages)} messages from database") + self.calendarWidget.setMessages(messages) + self.calendarWidget.updateCells() + + def open_reminder(self): + # def __update_message(): + # message_select = self.spin_select_message.value() + # try: + # message = messages[message_select - 1] + # except IndexError: + # self.message_frame.hide() + # return + # self.message_box.setText(message["message"]) + # appnr = message["appnr"] + # appnr = "/" if appnr is None else str(appnr) + # self.line_app_info.setText(appnr) + + def __delete_message(id): + print("trying to delete message", id) + self.db.deleteMessage(id) + # + + selected_date = self.calendarWidget.selectedDate().toString("yyyy-MM-dd") + # print(selected_date) + messages = self.db.getMessages(selected_date) + if messages == []: + # self.message_frame.hide() if self.message_frame.isVisible() else None + return + # print(messages) + # message_count = len(messages) + # self.spin_select_message.setMaximum(message_count) + # self.message_frame.show() + # self.label_total_day_messages.setText("/ " + str(message_count)) + # # if there is only one message, disable the spinbox + # ( + # self.spin_select_message.setEnabled(False) + # if message_count == 1 + # else self.spin_select_message.setEnabled(True) + # ) + # self.spin_select_message.setValue(1) + # # load the first message + # __update_message() + # # on valuechanged, update the message + # self.spin_select_message.valueChanged.connect(__update_message) + # self.btn_delete_message.clicked.connect(__delete_message) + dialog = CalendarEntry(messages=messages) + # append dialog to self.frame_2 + self.calendarlayout.addWidget(dialog) + # dialog.deleteSignal.connect(__delete_message) + # if selected_date is not None: + # dialog.repaintSignal.connect( + # lambda: self.calendarWidget.redraw(selected_date) + # ) + + def open_settings(self): + settings = Settings(self.active_user) + settings.exec() + if settings.result() == QtWidgets.QDialog.DialogCode.Accepted: + data = settings.return_data() + print(data) + OmegaConf.save(data, "config.yaml") + # re-load the config + OmegaConf.load("config.yaml") + self.logger.log_info("Saved settings to config.yaml") + self.reload() + + def reload(self): + # create a new connection to the database, refresh table data and replace the old connection + self.db = Database() + self.apparats = self.db.getAllAparats(deleted=0) + self.apparats = natsorted(self.apparats, key=lambda x: x[4], reverse=True) + self.tableWidget_apparate.setRowCount(0) + for apparat in self.apparats: + self.insert_apparat_into_table(apparat) + + def media_context_menu(self, position): + menu = QtWidgets.QMenu() + delete_action = menu.addAction("Löschen") + edit_action = menu.addAction("Bearbeiten") + menu.addAction(delete_action) + menu.addAction(edit_action) + delete_action.triggered.connect(self.delete_medium) + edit_action.triggered.connect(self.edit_medium) + menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) + + def edit_medium(self): + book = self.tableWidget_apparat_media.item( + self.tableWidget_apparat_media.currentRow(), 1 + ).text() + data = self.db.getBookBasedOnSignature( + app_id=self.active_apparat, + signature=book, + prof_id=self.db.getProfId(self.drpdwn_prof_name.currentText()), + ) + book_id = self.db.getBookIdBasedOnSignature( + self.active_apparat, + self.db.getProfId(self.drpdwn_prof_name.currentText()), + book, + ) + widget = QtWidgets.QDialog() + bookedit = edit_bookdata_ui() + bookedit.setupUi(widget) + # change title of dialog + widget.setWindowTitle("Metadaten") + bookedit.populate_fields(data) + widget.exec() + if widget.result() == QtWidgets.QDialog.DialogCode.Accepted: + data = bookedit.get_data() + print(data) + self.db.updateBookdata(data, book_id) + # self.db.update_bookdata(data) + print("accepted") + self.update_app_media_list() + else: + return + pass + + def delete_medium(self): + selected_apparat_id = self.tableWidget_apparate.item( + self.tableWidget_apparate.currentRow(), 0 + ).text() + # check how many rows are selected + selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() + if len(selected_rows) == 1: + signature = self.tableWidget_apparat_media.item( + self.tableWidget_apparat_media.currentRow(), 1 + ).text() + book_id = self.db.getBookIdBasedOnSignature( + selected_apparat_id, + prof_id=self.db.getProfId(self.drpdwn_prof_name.currentText()), + signature=signature, + ) + message = f'Soll das Medium "{self.tableWidget_apparat_media.item(self.tableWidget_apparat_media.currentRow(),0).text()}" wirklich gelöscht werden?' + state = self.confirm_popup(message) + print(state) + if state == 1: + self.db.deleteBook(book_id) + self.update_app_media_list() + self.contact_prof(mail="deleted") + pass + else: + # get all selected rows + ranges = self.tableWidget_apparat_media.selectedRanges() + rows = [] + for r in ranges: + for row in range(r.topRow(), r.bottomRow() + 1): + rows.append(row) + print(rows) + message = f"Sollen die {len(rows)} Medien wirklich gelöscht werden?" + state = self.confirm_popup(message) + if state == 1: + for _ in rows: + signature = self.tableWidget_apparat_media.item(_, 1).text() + book_id = self.db.getBookIdBasedOnSignature( + selected_apparat_id, + prof_id=self.db.getProfId(self.drpdwn_prof_name.currentText()), + signature=signature, + ) + self.db.deleteBook(book_id) + self.update_app_media_list() + + def extend_apparat(self): + framework = QtWidgets.QDialog() + frame = App_Ext_Dialog() + frame.setupUi(framework) + frame.sem_year.setText(str(QtCore.QDate.currentDate().year())) + framework.exec() + # return data from dialog if ok is pressed + if framework.result() == QtWidgets.QDialog.DialogCode.Accepted: + data = frame.get_data() + print(data) + # return data + selected_apparat_id = self.tableWidget_apparate.item( + self.tableWidget_apparate.currentRow(), 0 + ).text() + print(selected_apparat_id) + # update self.apparats with the new data + # find matching apparat + # for apparat in self.apparats: + # if apparat[4] == int(selected_apparat_id): + # apparat[5]=data["semester"] + # apparat[7]=data["dauerapp"] + # break + # self.old_apparats = self.apparats + self.db.setNewSemesterDate( + selected_apparat_id, data["semester"], dauerapp=data["dauerapp"] + ) + else: + return + + def contact_prof(self, apparat=None, mail=""): + print(apparat) + if self.active_apparat == "": + if apparat is False: + self.confirm_popup("Bitte erst einen Apparat auswählen!") + return + else: + # TODO: stuff + pass + else: + if apparat: + active_apparat_id = apparat + else: + active_apparat_id = self.drpdwn_app_nr.currentText() + print(active_apparat_id) + profname = self.drpdwn_prof_name.currentText().replace(",", "").split(" ") + profname = f"{profname[1]} {profname[0]}" + prof_id = self.db.getProfId(self.drpdwn_prof_name.currentText()) + pmail = self.db.getSpecificProfData(prof_id, ["mail"]) + # create a new thread to show the mail interface and send the mail + self.mail_thread = Mail_Dialog( + app_id=active_apparat_id, + prof_name=profname, + prof_mail=pmail, + app_name=self.app_name.text(), + app_subject=self.app_fach.currentText(), + ) + self.mail_thread.show() + + def delete_apparat(self): + selected_apparat_id = self.tableWidget_apparate.item( + self.tableWidget_apparate.currentRow(), 0 + ).text() + message = f"Soll der Apparat {selected_apparat_id} wirklich gelöscht werden?" + state = self.confirm_popup(message) + print(state) + if state == 1: + self.db.deleteApparat(selected_apparat_id, generateSemesterByDate()) + # delete the corresponding entry from self.apparats + for apparat in self.apparats: + if apparat[4] == int(selected_apparat_id): + self.apparats.remove(apparat) + break + self.old_apparats = self.apparats + print(self.apparats) + # remove the row from the table + self.tableWidget_apparate.removeRow(self.tableWidget_apparate.currentRow()) + # send mail to prof + self.contact_prof(mail="deleted", apparat=selected_apparat_id) + # if state==QtWidgets.QDialog.DialogCode.Accepted: + # self.db.delete_apparat(selected_apparat_id) + # pass + + +def launch_gui(): + print("trying to login") + print("checking if database available") + # database = config.database.path + config.database.name + # print(database) + # if not os.path.exists(database): + # print("Database not found, creating new database") + # db = Database() + # db.create_database() + app = QtWidgets.QApplication(sys.argv) + login_dialog = QtWidgets.QDialog() + ui = login_ui() + ui.setupUi(login_dialog) + login_dialog.exec() # This will block until the dialog is closed + + if ui.lresult == 1: + # if login is successful, open main window + # show login dialog + print(ui.lusername) + MainWindow = QtWidgets.QMainWindow() + aui = Ui(MainWindow, username=ui.lusername) + + print(aui.active_user) + MainWindow.show() + # atexit.register() + atexit.register(tempdelete) + atexit.register(aui.validate_thread.quit) + sys.exit(app.exec()) + + elif ui.lresult == 0: + warning_dialog = QtWidgets.QMessageBox() + warning_dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) + warning_dialog.setText("Invalid username or password. Please try again.") + warning_dialog.setWindowTitle("Login Failed") + warning_dialog.exec() + elif ui.lresult == 2: + # TODO: implement admin functionality here + """change passwords for apparats, change passwords for users, list users, create and delete users etc""" + # open a console window + # console = "" + print("admin") + atexit.register(tempdelete) + + +if __name__ == "__main__": + print("This is the main window") + # app = QtWidgets.QApplication(sys.argv) + # window = MainWindow() + # app.exec() + # open login screen + launch_gui() diff --git a/src/ui/widgets/calendar_entry.py b/src/ui/widgets/calendar_entry.py index fb9bde2..6a3e514 100644 --- a/src/ui/widgets/calendar_entry.py +++ b/src/ui/widgets/calendar_entry.py @@ -30,22 +30,31 @@ class CalendarEntry(QtWidgets.QDialog, Ui_Dialog): if self.messages[i] == message: return self.messages[i]["id"] + def __message_index(self, id): + for i in range(len(self.messages)): + if self.messages[i]["id"] == id: + return i + def delete_message(self): + print(self.spin_select_message.value()) value = self.spin_select_message.value() + print(value) if value > 0: value = value - 1 message = self.messages[value] - + print(message) id = self.__get_id(message) print("id", id) - del self.messages[value - 1] + # del self.messages[value - 1] self.spin_select_message.setMaximum(len(self.messages)) self.message_box.clear() - if value > 0: - self.set_message() - self.deleteSignal.emit(id) - else: - return + self.set_message() + + self.deleteSignal.emit(id) + self.messages.pop(self.__message_index(id)) + if len(self.messages) == 0: + self.repaintSignal.emit() + self.close() else: # self.repaintSignal.emit()