From 271f9a33092c88fba4d8acaf8efca8c5a24b2234 Mon Sep 17 00:00:00 2001 From: WorldTeacher <41587052+WorldTeacher@users.noreply.github.com> Date: Fri, 24 May 2024 16:30:31 +0200 Subject: [PATCH] elsa functions, graphs, fixes #9, #7 --- src/logic/userInterface.py | 4587 +++++++++++++++++++----------------- 1 file changed, 2414 insertions(+), 2173 deletions(-) diff --git a/src/logic/userInterface.py b/src/logic/userInterface.py index b15549a..0410d8b 100644 --- a/src/logic/userInterface.py +++ b/src/logic/userInterface.py @@ -1,2173 +1,2414 @@ -# 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 QDate, QThread -from PyQt6.QtGui import QColor, QRegularExpressionValidator - -from src.backend.admin_console import AdminCommands -from src.backend.create_file import recreateFile -from src.backend.database import Database -from src.backend.delete_temp_contents import delete_temp_contents -from src.backend.semester import generateSemesterByDate -from src.logic import AvailChecker, BookGrabber, c_sort -from src.logic.constants import APP_NRS, PROF_TITLES -from src.logic.csvparser import csv_to_list -from src.logic.dataclass import ApparatData, BookData -from src.logic.log import MyLogger -from src.logic.wordparser import word_docx_to_csv -from src.ui import ( - App_Ext_Dialog, - FilePicker, - GraphWidget, - Mail_Dialog, - StatusWidget, - Ui_Semesterapparat, - edit_bookdata_ui, - login_ui, - medienadder_ui, - parsed_titles_ui, - popus_confirm, - reminder_ui, - settings_ui, -) - -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 MessageCalendar(QtWidgets.QCalendarWidget): - # Widget for MessageCalendar - def __init__(self, parent=None): - super().__init__(parent) - self.messages = {} # Dictionary to store dates with messages - - def setMessages(self, messages): - for message in messages: - print(message) - # Convert the date string to a QDate object - date = QDate.fromString(message["remind_at"], "yyyy-MM-dd") - # Store the message for the date - self.messages[date] = message["message"] - self.updateCells() - - def updateCells(self): - self.repaint() - - def paintCell(self, painter, rect, date): - super().paintCell(painter, rect, date) - - # Check if there is a message for the current date - if date in self.messages: - # If there is a message, color the cell background - painter.fillRect(rect, QColor("#a7e681")) - - def change_stylesheet_cell(self, date: QDate, color: str): - # change the stylesheet of a cell - self.setStyleSheet( - f"QCalendarWidget QTableView QTableCornerButton::section {{background-color: {color};}}" - ) - - -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") - icon = QtGui.QIcon() - icon.addPixmap( - QtGui.QPixmap("icons/logo.ico"), - QtGui.QIcon.Mode.Normal, - QtGui.QIcon.State.Off, - ) - MainWindow.setWindowIcon(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.setGeometry(QtCore.QRect(0, 0, 291, 191)) - 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.tableWidget_apparat_media.horizontalHeader().setSectionResizeMode( - QtWidgets.QHeaderView.ResizeMode.Stretch - ) - self.tableWidget_apparate.horizontalHeader().setSectionResizeMode( - QtWidgets.QHeaderView.ResizeMode.Stretch - ) - self.tableWidget_apparate.setSortingEnabled(True) - # self.tableWidget_apparate.text - self.actionEinstellungen.triggered.connect(self.open_settings) - # if help>documentation is clicked, open the documentation or shortcut is pressed - self.actionDokumentation.triggered.connect(self.open_documentation) - - # 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.frame.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.db.getAllAparats(deleted=0) - self.apparats = natsorted(self.apparats, key=lambda x: x[4], reverse=True) - - for apparat in self.apparats: - self.insert_apparat_into_table(apparat) - - 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) - # self.thread_check() - - ### 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) - # 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 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 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: - # self.tabWidget.setCurrentIndex(1) - self.tabWidget_2.setCurrentIndex(1) - self.tabWidget_2.setCurrentIndex(0) - - 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.user_create_frame.hide() - self.user_edit_frame.hide() - self.user_delete_frame.hide() - self.edit_faculty_member.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.user_create_frame.show() - self.user_frame_userrole.addItems(roles) - elif action == "Nutzer aktualisieren": - self.hide_all() - self.user_edit_frame.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.user_delete_frame.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.edit_faculty_member.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) - created_status.person_double_clicked.connect(self.open_apparat) - deleted_status.person_double_clicked.connect(self.open_apparat) - - 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 = c_sort.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() - # create graph - - graph_data = { - "x": [i[0] for i in data], - "y": [i[1] for i in data], - "y2": [i[2] for i in data], - } - graph = GraphWidget(data=graph_data, legend_labels=["Erstellt", "Gelöscht"]) - - # place the graph into tabWidget_3 - self.tabWidget_3.addTab(graph, "Erstellte und gelöschte Semesterapparate") - 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() - elif curr_tab == 2: # admin tab - self.populate_admin_tab() - - def populate_admin_tab(self): - pass - - 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.frame.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() - - def confirm_popup(self, message: str): - dial = QtWidgets.QDialog() - popup = popus_confirm() - popup.setupUi(dial) - popup.textEdit.setReadOnly(True) - popup.textEdit.setText(message) - dial.exec() - return dial.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.frame.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: - self.drpdwn_prof_name.setStyleSheet("border: 1px solid black;") - - def validate_prof_mail(self): - if self.frame.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.frame.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.frame.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.frame.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.frame.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) - else: - self._mand.setText("*") - self.change_state(5, 0) - - 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.frame.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): - - 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.frame.setDisabled(True) - # for child in self.frame.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.frame.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.frame.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.frame.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.frame.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() - for child in self.frame.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.frame.setEnabled(False) - self.groupBox_2.hide() - self.groupBox.hide() - self.check_send_mail.setChecked(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(): - message = messages[self.spin_select_message.value() - 1] - self.db.deleteMessage(message["id"]) - # remove message from list - messages.remove(message) - self.spin_select_message.setMaximum(len(messages)) - self.spin_select_message.setValue(1) - self.label_total_day_messages.setText("/ " + str(len(messages))) - # - - 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) - - def open_settings(self): - dialog = QtWidgets.QDialog() - settings = settings_ui() - settings.setupUi(dialog) - dialog.exec() - if dialog.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=""): - if self.active_apparat == "": - if apparat is None: - self.confirm_popup("Bitte erst einen Apparat auswählen!") - return - active_apparat_id = ( - self.tableWidget_apparate.item( - self.tableWidget_apparate.currentRow(), 0 - ).text() - if apparat is None - else apparat - ) - 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(delete_temp_contents) - 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(delete_temp_contents) - - -if __name__ == "__main__": - print("This is the main window") - # app = QtWidgets.QApplication(sys.argv) - # window = MainWindow() - # app.exec() - # open login screen - launch_gui() +# 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 QDate, QThread +from PyQt6.QtGui import QColor, QRegularExpressionValidator + +from src.backend.admin_console import AdminCommands +from src.backend.create_file import recreateElsaFile, recreateFile +from src.backend.database import Database +from src.backend.delete_temp_contents import delete_temp_contents +from src.backend.semester import generateSemesterByDate +from src.logic import AvailChecker, BookGrabber, c_sort +from src.logic.constants import APP_NRS, PROF_TITLES +from src.logic.csvparser import csv_to_list +from src.logic.dataclass import ApparatData, BookData +from src.logic.log import MyLogger +from src.logic.wordparser import elsa_word_to_csv, word_docx_to_csv +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_ui, +) + +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 MessageCalendar(QtWidgets.QCalendarWidget): + # Widget for MessageCalendar + def __init__(self, parent=None): + super().__init__(parent) + self.messages = {} # Dictionary to store dates with messages + + def setMessages(self, messages): + for message in messages: + print(message) + # Convert the date string to a QDate object + date = QDate.fromString(message["remind_at"], "yyyy-MM-dd") + # Store the message for the date + self.messages[date] = message["message"] + self.updateCells() + + def updateCells(self): + self.repaint() + + def paintCell(self, painter, rect, date): + super().paintCell(painter, rect, date) + + # Check if there is a message for the current date + if date in self.messages: + # If there is a message, color the cell background + painter.fillRect(rect, QColor("#a7e681")) + + def change_stylesheet_cell(self, date: QDate, color: str): + # change the stylesheet of a cell + self.setStyleSheet( + f"QCalendarWidget QTableView QTableCornerButton::section {{background-color: {color};}}" + ) + + +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") + icon = QtGui.QIcon() + icon.addPixmap( + QtGui.QPixmap("icons/logo.ico"), + QtGui.QIcon.Mode.Normal, + QtGui.QIcon.State.Off, + ) + MainWindow.setWindowIcon(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.setGeometry(QtCore.QRect(0, 0, 291, 191)) + 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.tableWidget_apparat_media.horizontalHeader().setSectionResizeMode( + QtWidgets.QHeaderView.ResizeMode.Stretch + ) + self.tableWidget_apparate.horizontalHeader().setSectionResizeMode( + QtWidgets.QHeaderView.ResizeMode.Stretch + ) + self.tableWidget_apparate.setSortingEnabled(True) + # self.tableWidget_apparate.text + self.actionEinstellungen.triggered.connect(self.open_settings) + # if help>documentation is clicked, open the documentation or shortcut is pressed + self.actionDokumentation.triggered.connect(self.open_documentation) + + # 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.frame.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) + # self.thread_check() + + ### 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) + 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 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.MainWindow.setStatusTip("ELSA-Apparat hat noch keine Daten") + self.create_frame_elsa.setEnabled(True) + else: + self.MainWindow.setStatusTip( + "ELSA-Apparat hat bereits alle benötigten Daten" + ) + 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 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) + + 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) + 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 = {} + + for apparat in elsa_apparats: + print(apparat) + data = self.insert_elsa_into_table(apparat) + if data[0] in graph_data.keys(): + graph_data["x"].append(data[0]) + graph_data["y"].append(data[1]) + else: + graph_data["x"] = [data[0]] + graph_data["y"] = [data[1]] + generateMissing = True if len(graph_data["x"]) > 1 else False + graph = DataGraph( + "ELSA Apparate pro Semester", + graph_data, + generateMissing, + "Anzahl der Apparate", + ) + + 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.user_create_frame.hide() + self.user_edit_frame.hide() + self.user_delete_frame.hide() + self.edit_faculty_member.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.user_create_frame.show() + self.user_frame_userrole.addItems(roles) + elif action == "Nutzer aktualisieren": + self.hide_all() + self.user_edit_frame.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.user_delete_frame.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.edit_faculty_member.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) + created_status.person_double_clicked.connect(self.open_apparat) + deleted_status.person_double_clicked.connect(self.open_apparat) + + 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 = c_sort.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() + # 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.frame.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): + dial = QtWidgets.QDialog() + popup = popus_confirm() + popup.setupUi(dial) + popup.textEdit.setReadOnly(True) + popup.textEdit.setText(message) + dial.exec() + return dial.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.frame.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: + self.drpdwn_prof_name.setStyleSheet("border: 1px solid black;") + + def validate_prof_mail(self): + if self.frame.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.frame.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.frame.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.frame.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.frame.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) + else: + self._mand.setText("*") + self.change_state(5, 0) + + 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.frame.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.frame.setDisabled(True) + # for child in self.frame.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.frame.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.frame.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.frame.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.frame.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() + for child in self.frame.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.frame.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(): + message = messages[self.spin_select_message.value() - 1] + self.db.deleteMessage(message["id"]) + # remove message from list + messages.remove(message) + self.spin_select_message.setMaximum(len(messages)) + self.spin_select_message.setValue(1) + self.label_total_day_messages.setText("/ " + str(len(messages))) + # + + 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) + + def open_settings(self): + dialog = QtWidgets.QDialog() + settings = settings_ui() + settings.setupUi(dialog) + dialog.exec() + if dialog.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=""): + if self.active_apparat == "": + if apparat is None: + self.confirm_popup("Bitte erst einen Apparat auswählen!") + return + active_apparat_id = ( + self.tableWidget_apparate.item( + self.tableWidget_apparate.currentRow(), 0 + ).text() + if apparat is None + else apparat + ) + 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(delete_temp_contents) + 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(delete_temp_contents) + + +if __name__ == "__main__": + print("This is the main window") + # app = QtWidgets.QApplication(sys.argv) + # window = MainWindow() + # app.exec() + # open login screen + launch_gui()