# encoding: utf-8 import atexit import os import sys import tempfile 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 QRegularExpressionValidator from src.transformers.transformers import DictToTable from src import Icon from src.backend import ( AdminCommands, Database, generateSemesterByDate, recreateElsaFile, recreateFile, tempdelete, ) from src.logic import ( APP_NRS, PROF_TITLES, ApparatData, AvailChecker, BookData, BookGrabber, MyLogger, csv_to_list, elsa_word_to_csv, word_docx_to_csv, ZoteroController, ) from src.ui import ( About, App_Ext_Dialog, CalendarEntry, DataGraph, FilePicker, Mail_Dialog, MessageCalendar, Settings, Ui_Semesterapparat, edit_bookdata_ui, login_ui, medienadder_ui, parsed_titles_ui, popus_confirm, reminder_ui, SearchStatisticPage, ) from src.ui.dialogs.elsa_add_entry import ElsaAddEntry config = OmegaConf.load("config.yaml") valid_input = (0, 0, 0, 0, 0, 0) class Ui(Ui_Semesterapparat): # use the Ui_MainWindow class from mainwindow.py def __init__(self, MainWindow, username: str) -> None: self.logger = MyLogger("Ui") self.logger.log_info("Starting Semesterapparatsmanagement") super().__init__() self.active_user = username self.setupUi(MainWindow) self.MainWindow = MainWindow # set the window title MainWindow.setWindowTitle("Semesterapparatsmanagement") MainWindow.setWindowIcon(Icon("logo").icon) self.db = Database() self.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.btn_apparat_save.clicked.connect(lambda: self.btn_save_apparat(True)) self.btn_apparat_apply.clicked.connect(self.update_apparat) self.btn_open_document.clicked.connect(self.open_document) self.add_medium.clicked.connect(self.btn_add_medium) self.btn_copy_adis_command.clicked.connect(self.text_to_clipboard) self.btn_reserve.clicked.connect(self.check_availability) self.calendarWidget = MessageCalendar(self.frame_2) self.calendarWidget.setGridVisible(True) self.calendarWidget.setVerticalHeaderFormat( QtWidgets.QCalendarWidget.VerticalHeaderFormat.NoVerticalHeader ) self.calendarWidget.setObjectName("MessageCalendar") self.calendarWidget.clicked.connect(self.open_reminder) # assign a context menu to the calendar self.calendarlayout.addWidget(self.calendarWidget) self.tableWidget_apparat_media.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.ResizeMode.Stretch ) self.tableWidget_apparate.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.ResizeMode.Stretch ) self.tableWidget_apparate.setSortingEnabled(True) # Actions self.actionEinstellungen.triggered.connect(self.open_settings) Icon("settings", self.actionEinstellungen) self.actionDokumentation.triggered.connect(self.open_documentation) Icon("offAction", self.actionBeenden) self.actionBeenden.triggered.connect(self.quit) self.actionAbout.triggered.connect(self.open_about) # set validators self.sem_sommer.clicked.connect(lambda: self.toggleButton(self.sem_winter)) self.sem_winter.clicked.connect(lambda: self.toggleButton(self.sem_sommer)) 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 app_fach validator to allow only entries from the present items in self.app_fach and allow manual text entry # self.app_fach.setValidator( # QtGui.QRegularExpressionValidator( # QtCore.QRegularExpression( # "|".join([f"{subject[1]}" for subject in self.db.getSubjects()]) # ) # ) # ) self.app_fach.setValidator( # validator to allow typing in the app_fach field QtGui.QRegularExpressionValidator( QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+") ) ) self.prof_tel_nr.setValidator( QtGui.QRegularExpressionValidator( QtCore.QRegularExpression(r"^\d{3}|^\d{4}-\d{2}|^\+\d{8,}") ) ) # 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 ) self.tableWidget_apparate.doubleClicked.connect(self.load_app_data) self.load_app.hide() print(f"user:{self.active_user}") userrole = self.db.getRole(self.active_user) # hide admin interface when non-admin is logged in if userrole == "admin": self.tabWidget.setTabVisible(3, True) else: self.tabWidget.setTabVisible(3, False) # self.update_app_media_list() self.populate_prof_dropdown() self.populate_appfach_dropdown() # if the focus is changed from the prof name dropdown, set the prof data if the prof exists in the database, otherwise show a message self.drpdwn_prof_name.currentIndexChanged.connect(self.set_prof_data) self.cancel_active_selection.clicked.connect(self.btn_cancel_active_selection) self.check_eternal_app.stateChanged.connect(self.set_state) # validate inputs self.prof_mail.textChanged.connect(self.validate_prof_mail) self.drpdwn_prof_name.editTextChanged.connect(self.validate_prof_name) self.prof_tel_nr.textChanged.connect(self.validate_prof_tel) self.app_name.textChanged.connect(self.validate_app_name) self.app_fach.currentTextChanged.connect(self.validate_app_fach) self.sem_year.textChanged.connect(self.validate_semester) self.check_eternal_app.stateChanged.connect(self.validate_semester) self.chkbx_show_del_media.stateChanged.connect(self.update_app_media_list) self.progress_label.setText("Bitte warten...") # Set visibility/enabled state of certain entries self.chkbx_show_del_media.setEnabled(False) self.label_info.hide() self.app_group_box.setEnabled(False) self.line_2.hide() self.progress_label.hide() # self.message_frame.hide() self.btn_reserve.hide() self.label_20.hide() self.line_3.hide() self.avail_status.hide() self.chkbx_show_del_media.hide() self.groupBox_2.hide() self.groupBox.hide() # self.btn_del_select_apparats.setEnabled(False) self.tabWidget.currentChanged.connect(self.tabW1_changed) self.statistics = SearchStatisticPage() self.statistics.apparat_open.connect(self.open_apparat) self.statistics.reloadSignal.connect(self.reload) # 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 ### 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 ) # 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.table_elsa_list.setContextMenuPolicy( QtCore.Qt.ContextMenuPolicy.CustomContextMenu ) self.table_elsa_list.customContextMenuRequested.connect(self.elsa_context_menu) # elsa buttons Icon("semester", self.active_semester) Icon("today", self.elsa_date_today) self.create_frame_elsa.setEnabled(False) 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_update.clicked.connect(self.update_elsa) 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) self.quote_entry.clicked.connect(self.elsa_table_entry) # admin buttons self.user_frame_addUser.clicked.connect(self.add_user) self.btn_delete_user.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 = [] self.availChecker = None self.mail_thread = None self.autoGrabber = None def elsa_context_menu(self, position): menu = QtWidgets.QMenu() # TODO: add functions pass def toggleButton(self, button): if button.isChecked(): button.setChecked(False) def open_about(self): About().exec() def quit(self): # delete all temporary files tempdelete() sys.exit() def elsa_table_entry(self): dtt = DictToTable() zot = ZoteroController() data = ElsaAddEntry() selected_row = self.elsa_table.currentRow() # print all rows in the table for i in range(self.elsa_table.rowCount()): print(self.elsa_table.item(i, 0).text()) mediatype = self.table_elsa_list.item(selected_row, 11).text() signature = self.table_elsa_list.item(selected_row, 10).text() data.searchIdent.setText(signature) if mediatype == "Zeitschriftenaufsätze": data.btn_zs.click() elif mediatype == "Herausgeberwerke": data.btn_hg.click() elif mediatype == "Monographie": data.btn_mono.click() data.exec() def add_new_elsa(self): self.create_frame_elsa.setEnabled(True) self.elsa_cancel_create.setEnabled(True) self.dokument_list_elsa.setRowCount(0) 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 update_elsa(self): prof = self.elsa_prof.currentText() date = self.elsa_date.text() semester = self.elsa_semester.text() elsa_id = self.db.getElsaId(prof, semester, date) if elsa_id is None: return self.db.updateElsaApparat(elsa_id, prof, semester, date) self.elsa_update.setEnabled(False) 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", title="Fehler") 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() self.refresh_elsa_table() self.elsa_prof.clear() self.elsa_prof.addItem(prof) def refresh_elsa_table(self): self.elsa_table.setRowCount(0) elsa_apparats = self.db.getElsaApparats() for apparat in elsa_apparats: self.insert_elsa_into_table(apparat) 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() self.elsa_update.setEnabled(True) self.elsa_save.setEnabled(False) 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) ic(scans) if scans == []: self.create_frame_elsa.setEnabled(True) else: pass for scan in scans: self.table_elsa_list.insertRow(0) self.table_elsa_list.setItem( 0, 0, QtWidgets.QTableWidgetItem(scan["work_author"]) ) self.table_elsa_list.setItem( 0, 1, QtWidgets.QTableWidgetItem(scan["section_author"]) ) self.table_elsa_list.setItem( 0, 2, QtWidgets.QTableWidgetItem(scan["year"]) ) self.table_elsa_list.setItem( 0, 3, QtWidgets.QTableWidgetItem(scan["issue"]) ) self.table_elsa_list.setItem( 0, 4, QtWidgets.QTableWidgetItem(scan["edition"]) ) self.table_elsa_list.setItem( 0, 5, QtWidgets.QTableWidgetItem(scan["work_title"]) ) self.table_elsa_list.setItem( 0, 6, QtWidgets.QTableWidgetItem(scan["chapter_title"]) ) self.table_elsa_list.setItem( 0, 7, QtWidgets.QTableWidgetItem(scan["pages"]) ) self.table_elsa_list.setItem( 0, 8, QtWidgets.QTableWidgetItem(scan["location"]) ) self.table_elsa_list.setItem( 0, 9, QtWidgets.QTableWidgetItem(scan["publisher"]) ) self.table_elsa_list.setItem( 0, 10, QtWidgets.QTableWidgetItem(scan["signature"]) ) self.table_elsa_list.setItem( 0, 11, QtWidgets.QTableWidgetItem(scan["type"]) ) self.create_frame_elsa.setEnabled(True) 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: # create elsa self.db.createElsaApparat( self.elsa_date.text(), self.elsa_prof.currentText(), self.elsa_semester.text(), ) elsa_id = self.db.getElsaId( self.elsa_prof.currentText(), self.elsa_semester.text(), self.elsa_date.text(), ) self.db.insertElsaFile(datalist, elsa_id) self.elsa_save.setEnabled(False) self.refresh_elsa_table() 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, doctype = elsa_word_to_csv(file) elsa_id = self.db.getElsaId( self.elsa_prof.currentText(), self.elsa_semester.text(), self.elsa_date.text(), ) for row in data: self.table_elsa_list.insertRow(0) ic(row) self.table_elsa_list.setItem( 0, 0, QtWidgets.QTableWidgetItem(row["work_author"]) ) self.table_elsa_list.setItem( 0, 1, QtWidgets.QTableWidgetItem(row["section_author"]) ) self.table_elsa_list.setItem( 0, 2, QtWidgets.QTableWidgetItem(row["year"]) ) self.table_elsa_list.setItem( 0, 3, QtWidgets.QTableWidgetItem(row["issue"]) ) self.table_elsa_list.setItem( 0, 4, QtWidgets.QTableWidgetItem(row["edition"]) ) self.table_elsa_list.setItem( 0, 5, QtWidgets.QTableWidgetItem(row["chapter_title"]) ) self.table_elsa_list.setItem( 0, 6, QtWidgets.QTableWidgetItem(row["work_title"]) ) self.table_elsa_list.setItem( 0, 7, QtWidgets.QTableWidgetItem(row["pages"]) ) self.table_elsa_list.setItem( 0, 8, QtWidgets.QTableWidgetItem(row["location"]) ) self.table_elsa_list.setItem( 0, 9, QtWidgets.QTableWidgetItem(row["publisher"]) ) self.table_elsa_list.setItem( 0, 10, QtWidgets.QTableWidgetItem(row["signature"]) ) self.table_elsa_list.setItem( 0, 11, QtWidgets.QTableWidgetItem(row["type"]) ) self.db.addElsaMedia(row, elsa_id) 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 get_apparats(self): alist = self.db.getAllAparats(deleted=0) alist = natsorted(alist, key=lambda x: x[4], reverse=True) for apparat in alist: self.insert_apparat_into_table(apparat) return alist def populate_appfach_dropdown(self): self.app_fach.clear() self.app_fach.addItem("") self.app_fach.setCurrentText("") self.app_fach.addItems([subject[1] for subject in self.db.getSubjects()]) def open_documentation(self): # open the documentation in the default browser webbrowser.open("file:///" + os.path.abspath("docs/index.html")) # documentation = documentationview.DocumentationViewer() # documentation.show() def tabW1_changed(self): if self.tabWidget.currentIndex() == 1: # Statistics stats_layout = self.search_statistics.layout() if stats_layout is not None: # delete tabpage, re-add with same name at same position return layout = QtWidgets.QVBoxLayout(self.search_statistics) layout.addWidget(self.statistics) print("searchpage") 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: self.logger.log_debug("No tab to remove") self.elsa_table.setRowCount(0) elsa_apparats = self.db.getElsaApparats() elsa_apparats = natsorted(elsa_apparats, key=lambda x: x[2], reverse=True) graph_data = {"x": [], "y": []} # x = semester, y = number of apparats for apparat in elsa_apparats: print(apparat) data = self.insert_elsa_into_table(apparat) semester = data[0] number = data[1] if semester not in graph_data["x"]: graph_data["x"].append(semester) graph_data["y"].append(number) else: index = graph_data["x"].index(semester) graph_data["y"][index] += number generateMissing = True if len(graph_data["x"]) > 1 else False graph = DataGraph( "ELSA Apparate pro Semester", graph_data, generateMissing, "Anzahl der Apparate", ) ic(graph_data) self.elsa_statistics_table.setRowCount(0) for i in range(len(graph_data["x"])): self.elsa_statistics_table.insertRow(0) self.elsa_statistics_table.setItem( 0, 0, QtWidgets.QTableWidgetItem(graph_data["x"][i]) ) self.elsa_statistics_table.setItem( 0, 1, QtWidgets.QTableWidgetItem(str(graph_data["y"][i])) ) self.elsa_statistics.addTab(graph, "Graph") def 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("") ) 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): 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 def update_user_data(self): username = self.user_edit_frame_user_select.currentText() password = ( self.user_edit_frame_new_password.text() if self.user_edit_frame_new_password.text() != "" else None ) role = ( self.user_edit_frame_role_select.currentText() if self.user_edit_frame_role_select.currentText() != "" else None ) userdata = AdminCommands().create_password(password) data = { "password": f"{userdata[1]}{userdata[0]}", "salt": userdata[1], "role": role, } data = {key: value for key, value in data.items() if value is not None} print(data) self.db.updateUser(username=username, data=data) self.user_edit_frame_user_select.setCurrentText("") self.user_edit_frame_new_password.clear() self.user_edit_frame_role_select.setCurrentText("") def edit_faculty_member_action(self): def __gen_fullname(fname, lname, data): if fname == "" and lname == "": return data[3] if fname == "" and lname != "": return f"{lname} {data[1]}" if fname != "" and lname == "": return f"{data[2]} {fname}" if fname != "" and lname != "": return f"{lname} {fname}" # get the data and use new value if it is not none and does not mach the old value if self.edit_faculty_member_select_member.currentText() == "": return olddata = self.db.getFacultyMember( self.edit_faculty_member_select_member.currentText() ) ic(olddata) data = olddata oldlname = data[2] oldfname = data[1] # take data except first and last entry titel = ( self.edit_faculty_member_new_title.currentText() if self.edit_faculty_member_new_title.currentText() != "Kein Titel" else None ) fname = ( self.edit_faculty_member_new_surname.text() if self.edit_faculty_member_new_surname.text() != "" else self.edit_faculty_member_select_member.currentText() .split(" ")[1] .strip() ) lname = ( self.user_faculty_member_new_name.text() if self.user_faculty_member_new_name.text() != "" else self.edit_faculty_member_select_member.currentText() .split(" ")[0] .strip() ) fullname = __gen_fullname(fname, lname, data) telnr = self.user_faculty_member_new_telnr.text() mail = self.user_faculty_member_new_mail.text() new_data = { "titel": titel, "fname": fname, "lname": lname, "fullname": fullname, "mail": mail, "telnr": telnr, } new_data = {key: value for key, value in new_data.items() if value != ""} self.db.updateFacultyMember(data=new_data, oldlname=oldlname, oldfname=oldfname) self.add_faculty_member_data() self.edit_faculty_member_new_title.setCurrentText("") self.edit_faculty_member_new_surname.clear() self.user_faculty_member_new_name.clear() self.user_faculty_member_new_telnr.clear() self.user_faculty_member_new_mail.clear() def hide_all(self): self.userCreateBox.hide() self.userChangeDataBox.hide() self.deleteUserBox.hide() self.profChangeDataBox.hide() def admin_action_changed(self): action = self.select_action_box.currentText() roles = self.db.getRoles() roles = [role[0] for role in roles] # remove duplicates roles = list(dict.fromkeys(roles)) users = self.db.getUsers() users = [user[2] for user in users] users.remove(self.active_user) if "admin" in users: users.remove("admin") if action == "Nutzer anlegen": self.hide_all() self.userCreateBox.show() self.user_frame_userrole.clear() self.user_frame_userrole.addItems(roles) elif action == "Nutzer aktualisieren": self.hide_all() self.userChangeDataBox.show() self.user_edit_frame_role_select.addItems(roles) self.user_edit_frame_user_select.addItems(users) elif action == "Nutzer löschen": self.hide_all() self.deleteUserBox.show() self.user_delete_frame_user_select.addItems(users) self.user_delete_frame_user_select.setCurrentText("") self.user_delete_frame_user_select.addItems(users) elif action == "Lehrperson bearbeiten": self.hide_all() self.profChangeDataBox.show() self.add_faculty_member_data() self.edit_faculty_member_new_title.addItems(PROF_TITLES) else: self.hide_all() return def add_faculty_member_data(self): faculty_members = self.db.getFacultyMembers() names = [f"{member[5]}" for member in faculty_members] self.edit_faculty_member_select_member.clear() self.edit_faculty_member_select_member.addItems(names) self.edit_faculty_member_select_member.addItem("") self.edit_faculty_member_select_member.setCurrentText("") def 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 = "SoSe" if self.sem_sommer.isChecked() else "WiSe" if semester == "SoSe": return "SoSe " + str(currentYear) else: return f"WiSe {currentYear}/{currentYear+1}" def open_apparat(self, apparat): if self.load_app_data(apparat): # change tab focus to tab 0 self.tabWidget.setCurrentIndex(0) def populate_dropdown(self, box, data): box.clear() box.addItem("") box.setCurrentText("") box.addItems(data) def populate_frame(self, appdata: ApparatData): # populate the frame with the data from the database self.drpdwn_app_nr.setCurrentText(str(appdata.appnr)) self.prof_title.setText(appdata.prof_title) prof_name = appdata.profname.split(" ") if len(prof_name) > 2: fname = " ".join(prof_name[1:]) lname = prof_name[0] prof_name = f"{lname}, {fname}" else: prof_name = ", ".join(prof_name) self.drpdwn_prof_name.setCurrentText(prof_name) self.prof_mail.setText(appdata.prof_mail) self.prof_tel_nr.setText(appdata.prof_tel) self.app_name.setText(appdata.appname) print("changing dropdown app_fach from '' to ", appdata.app_fach) self.app_fach.setCurrentText(appdata.app_fach) print("changed dropdown app_fach to ", self.app_fach.currentText()) if appdata.semester is not None: self.sem_sommer.setChecked( True if appdata.semester.split(" ")[0] == "SoSe" else False ) self.sem_winter.setChecked( True if appdata.semester.split(" ")[0] == "WiSe" else False ) self.sem_year.setText(appdata.semester.split(" ")[1]) else: self.sem_sommer.setChecked( True if appdata.erstellsemester.split(" ")[0] == "SoSe" else False ) self.sem_winter.setChecked( True if appdata.erstellsemester.split(" ")[0] == "WiSe" else False ) self.sem_year.setText(appdata.erstellsemester.split(" ")[1]) self.check_eternal_app.setChecked(appdata.dauerapp) self.prof_id_adis.setText(str(appdata.prof_adis_id)) self.apparat_id_adis.setText(str(appdata.apparat_adis_id)) self.app_group_box.setEnabled(True) self.groupBox_2.hide() self.groupBox.hide() self.validateLoadedData() def validateLoadedData(self): self.validate_prof_mail() self.validate_prof_name() self.validate_prof_tel() self.validate_app_name() self.validate_app_fach() self.validate_semester() def update_apparat(self): appdata = ApparatData() appdata.app_fach = self.app_fach.currentText() appdata.appname = self.app_name.text() appdata.appnr = self.active_apparat appdata.dauerapp = self.check_eternal_app.isChecked() appdata.prof_mail = self.prof_mail.text() appdata.prof_tel = self.prof_tel_nr.text() appdata.prof_title = self.prof_title.text() appdata.profname = self.drpdwn_prof_name.currentText() appdata.semester = ( self.sem_sommer.text() + " " + self.sem_year.text() if self.sem_sommer.isChecked() else self.sem_winter.text() + " " + self.sem_year.text() ) appdata.prof_adis_id = self.prof_id_adis.text() self.add_files() appdata.apparat_adis_id = self.apparat_id_adis.text() self.db.updateApparat(appdata) self.update_app_media_list() self.cancel_active_selection.click() self.check_send_mail.show() self.chkbx_show_del_media.show() self.cancel_active_selection.setEnabled(False) def confirm_popup(self, message: str, title: str): popup = popus_confirm(title=title) popup.textEdit.setReadOnly(True) popup.textEdit.setText(message) popup.exec() return popup.result() def thread_check(self): print("Thread started") 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) # else: # pass # # self.drpdwn_prof_name.setStyleSheet("border: 1px solid black;") # Validators def __setValidState(self, widget, state, mand, index): if state: Icon("valid_true", widget) mand.setText("") self.change_state(index, 1) else: Icon("valid_false", widget) mand.setText("*") self.change_state(index, 0) def validate_prof_name(self): if ( self.app_group_box.isEnabled() and "," in self.drpdwn_prof_name.currentText() ): self.__setValidState(self.valid_check_profname, 1, self.profname_mand, 0) else: self.__setValidState(self.valid_check_profname, 0, self.profname_mand, 0) def validate_prof_mail(self): if self.app_group_box.isEnabled(): if self.prof_mail.hasAcceptableInput(): self.__setValidState(self.valid_check_mail, 1, self.mail_mand, 1) else: self.__setValidState(self.valid_check_mail, 0, self.mail_mand, 1) else: self.__setValidState(self.valid_check_mail, 0, self.mail_mand, 1) def validate_prof_tel(self): if self.app_group_box.isEnabled(): if self.prof_tel_nr.text() != "" and self.prof_tel_nr.hasAcceptableInput(): self.__setValidState(self.valid_check_telnr, 1, self.telnr_mand, 2) else: self.__setValidState(self.valid_check_telnr, 0, self.telnr_mand, 2) else: self.__setValidState(self.valid_check_telnr, 0, self.telnr_mand, 2) def validate_app_name(self): if self.app_group_box.isEnabled() and self.app_name.hasAcceptableInput(): self.__setValidState(self.valid_check_appname, 1, self.appname_mand, 3) else: self.__setValidState(self.valid_check_appname, 0, self.appname_mand, 3) def validate_app_fach(self): if self.app_group_box.isEnabled() and self.app_fach.currentText() != "": self.__setValidState(self.valid_check_app_fach, 1, self.fach_mand, 4) else: self.__setValidState(self.valid_check_app_fach, 0, self.fach_mand, 4) def validate_semester(self): if ( self.app_group_box.isEnabled() and ( (self.sem_sommer.isChecked() or self.sem_winter.isChecked()) and self.sem_year.text() != "" and len(self.sem_year.text()) == 4 ) or self.check_eternal_app.isChecked() ): self.__setValidState(self.valid_check_semester, 1, self._mand, 5) self.check_eternal_app.setEnabled(True) else: self.__setValidState(self.valid_check_semester, 0, self._mand, 5) self.check_eternal_app.setEnabled(False) def change_state(self, index, state): global valid_input valid_input = list(valid_input) valid_input[index] = state valid_input = tuple(valid_input) def set_state(self): # set state of semester and year if self.check_eternal_app.isChecked(): self.sem_winter.setEnabled(False) self.sem_sommer.setEnabled(False) self.sem_year.setEnabled(False) else: self.sem_winter.setEnabled(True) self.sem_sommer.setEnabled(True) self.sem_year.setEnabled(True) def validate_fields(self): return all(valid_input) def 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, 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) if row is None and column is None: return False # 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() return True 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_create_new_apparat(self): self.groupBox.show() self.groupBox_2.show() global valid_input # *create a new apparat self.btn_apparat_save.show() if self.btn_apparat_save.isHidden() else None # clear dokumemt_list self.dokument_list.setRowCount(0) self.cancel_active_selection.setEnabled(True) self.app_group_box.setEnabled(True) self.sem_year.setEnabled(True) self.sem_sommer.setEnabled(True) self.sem_winter.setEnabled(True) self.chkbx_show_del_media.setEnabled(True) self.drpdwn_app_nr.setEnabled(True) self.app_fach.setEnabled(True) if self.tableWidget_apparat_media.rowCount() > 0: self.tableWidget_apparat_media.setRowCount(0) # clear all fields for item in self.app_group_box.findChildren(QtWidgets.QLineEdit): item.clear() self.drpdwn_app_nr.clear() self.prof_title.clear() self.drpdwn_prof_name.clear() # set drop down menu for apparat numbers to only available numbers self.drpdwn_app_nr.addItems( [str(i) for i in APP_NRS if i not in self.db.getUnavailableApparatNumbers()] ) valid_input = (0, 0, 0, 0, 0, 0) self.populate_prof_dropdown() def update_progress_label(self, curr, total): text = f"Medium {curr}/{total}" self.logger.log_info(text) self.progress_label.setText(text) # update tableWidget_apparat_media self.update_app_media_list() def hide_progress_label(self): self.logger.log_info("Finished adding media, hiding progress label") self.progress_label.hide() self.progress_label.setText("Bitte warten...") self.line_2.hide() self.label_info.hide() def btn_add_medium(self): if not self.app_group_box.isEnabled(): self.confirm_popup("Bitte erst einen Apparat auswählen!", title="Fehler") return media = medienadder_ui() media.exec() mode = media.mode data = media.data result = media.result() self.progress_label.show() self.line_2.show() self.label_info.show() self.progress_label.setText("Bitte warten...") if result == 1: if data == []: self.confirm_popup( "Bitte mindestens ein Medium hinzufügen!", title="Fehler" ) 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(False) # create a thread that updates the progress label after each medium # self.bookGrabber = None bookGrabber = BookGrabber() bookGrabber.add_values(mode=mode, prof_id=prof_id, app_id=app_id, data=data) bookGrabber.finished.connect(self.hide_progress_label) bookGrabber.finished.connect(self.update_app_media_list) bookGrabber.updateSignal.connect(self.update_progress_label) bookGrabber.start() # while self.bookGrabber[-1].isRunning(): # print("waiting for thread to finish") # QtWidgets.QApplication.processEvents() self.__clear_fields() else: return def check_availability(self): # self.threadeds.clear() def _update_progress(current, all_titles): self.avail_status.setText("{}/{}".format(current, all_titles)) def _hide_progress_label(): self.label_20.hide() self.avail_status.hide() self.avail_status.setText("0/0") # get all links from the table # if no index in tableWidget_apparat_media is selected, check all if self.tableWidget_apparat_media.currentRow() == -1: links = [ self.tableWidget_apparat_media.item(i, 1).text() for i in range(self.tableWidget_apparat_media.rowCount()) if self.tableWidget_apparat_media.item(i, 4).text() == "❌" or self.tableWidget_apparat_media.item(i, 4).text() == "" ] else: links = [ self.tableWidget_apparat_media.item( self.tableWidget_apparat_media.currentRow(), 1 ).text() ] items = len(links) self.label_20.setText("Verfügbarkeit wird geprüft, bitte warten...") self.label_20.show() self.avail_status.setText(f"0/{items}") self.avail_status.show() books = self.db.getBooks( self.active_apparat, self.db.getProfId(self.drpdwn_prof_name.currentText()), deleted=0, ) # thread = QThread() appnumber = self.active_apparat print(links) self.availChecker = AvailChecker(links, appnumber, books=books) # availcheck.moveToThread(thread) # availcheck.finished.connect(thread.quit) self.availChecker.finished.connect(self.availChecker.deleteLater) self.availChecker.finished.connect(self.update_app_media_list) self.availChecker.updateProgress.connect(_update_progress) self.availChecker.finished.connect(_hide_progress_label) self.availChecker.start() # kill availcheck after completion # self.threadeds.append(thread) # self.grabbers.append(availcheck) def btn_cancel_active_selection(self): # clear the rows of the table self.tableWidget_apparat_media.setRowCount(0) self.dokument_list.setRowCount(0) self.app_group_box.setEnabled(False) self.app_fach.setCurrentText("") self.chkbx_show_del_media.hide() self.check_send_mail.hide() self.btn_reserve.hide() self.groupBox_2.hide() self.groupBox.hide() self.check_eternal_app.setEnabled(False) # set all radio buttons to unchecked self.sem_sommer.setChecked(False) self.sem_winter.setChecked(False) for child in self.app_group_box.findChildren(QtWidgets.QLineEdit): child.clear() self.validate_app_fach() self.validate_app_name() self.validate_prof_mail() self.validate_prof_name() self.validate_prof_tel() self.validate_semester() 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 return link # # get the name of the column columnname = self.tableWidget_apparat_media.horizontalHeaderItem( item.column() ).text() if columnname == "Link": link = __openLink(item.text()) if link is not None: webbrowser.open(link) return 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.add_files() 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!", title="Fehler") return if not _selected_doc_location == "Database": path = Path(_selected_doc_location) path: Path = path.resolve() 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!", title="Fehler" ) 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 created = False if not self.db.checkApparatExistsById(app_id): # create apparat print("Creating apparat") self.btn_save_apparat(False) created = True prof_id = self.db.getProfId(self.drpdwn_prof_name.currentText()) 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: if not created: self.add_files() if file_type == "pdf": # Todo: implement parser here self.confirm_popup( "PDF Dateien werden noch nicht unterstützt!", title="Fehler" ) 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") autoGrabber = BookGrabber() autoGrabber.add_values( mode="ARRAY", app_id=app_id, prof_id=prof_id, data=signatures ) self.label_info.show() self.progress_label.show() self.line_2.show() # grabber.finished.connect(thread.quit) # self.autoGrabber.finished.connect(self.autoGrabber.deleteLater) autoGrabber.finished.connect(self.hide_progress_label) autoGrabber.finished.connect(self.unlock_apparate) autoGrabber.updateSignal.connect(self.update_progress_label) # worker.finished.connect(worker.deleteLater) autoGrabber.start() while autoGrabber.isRunning(): QtWidgets.QApplication.processEvents() # end of thread # self.autoGrabber.exit() # self.__clear_fields() # self.btn_cancel_active_selection() def unlock_apparate(self): self.tableWidget_apparate.setEnabled(True) self.tableWidget_apparate.setToolTip("") def __clear_fields(self): self.drpdwn_app_nr.setCurrentText("") self.prof_title.clear() self.drpdwn_prof_name.clearMask() self.drpdwn_prof_name.setCurrentText("") self.app_name.clear() self.prof_mail.clear() self.prof_tel_nr.clear() self.app_fach.setCurrentText("") self.app_name.clear() self.sem_year.clear() self.dokument_list.setRowCount(0) self.sem_winter.setChecked(False) self.sem_sommer.setChecked(False) self.check_eternal_app.setChecked(False) self.prof_id_adis.clear() self.prof_id_adis.clear() self.apparat_id_adis.clear() self.drpdwn_prof_name.clear() self.tableWidget_apparat_media.setRowCount(0) self.app_group_box.setEnabled(False) self.groupBox_2.hide() self.groupBox.hide() self.check_send_mail.setChecked(False) self.cancel_active_selection.setEnabled(False) def btn_save_apparat(self, clear_fields=True): if not self.validate_fields(): self.confirm_popup("Bitte alle Pflichtfelder ausfüllen!", title="Fehler") 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", location="" ) if clear_fields: print("clearing fields") self.__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]) # convert point to row and column row = self.tableWidget_apparate.rowAt(position.y()) column = self.tableWidget_apparate.columnAt(position.x()) pos = (str(row), str(column)) pid = self.__get_table_data_field(self.tableWidget_apparate, pos[0], 2) ic(pos, pid) extend_action.triggered.connect(self.extend_apparat) delete_action.triggered.connect(lambda: self.delete_apparat(pos)) # pass pos to contact_prof contact_action.triggered.connect(lambda: self.contact_prof(pid=pid)) remind_action.triggered.connect(self.reminder) menu.exec(self.tableWidget_apparate.mapToGlobal(position)) 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.getAllMessages() self.logger.log_info(f"Got {len(messages)} messages from database") self.calendarWidget.setMessages(messages) self.calendarWidget.updateCells() def open_reminder(self): # def __update_message(): # message_select = self.spin_select_message.value() # try: # message = messages[message_select - 1] # except IndexError: # self.message_frame.hide() # return # self.message_box.setText(message["message"]) # appnr = message["appnr"] # appnr = "/" if appnr is None else str(appnr) # self.line_app_info.setText(appnr) def __delete_message(id): print("trying to delete message", id) self.db.deleteMessage(id) # selected_date = self.calendarWidget.selectedDate().toString("yyyy-MM-dd") # print(selected_date) messages = self.db.getMessages(selected_date) if messages == []: return dialog = CalendarEntry(messages=messages, date=selected_date) # append dialog to self.frame_2 self.calendarlayout.addWidget(dialog) dialog.repaintSignal.connect(lambda: self.calendarWidget.reload(selected_date)) def open_settings(self): settings = Settings(self.active_user) settings.exec() if settings.result() == QtWidgets.QDialog.DialogCode.Accepted: data = settings.return_data() print(data) OmegaConf.save(data, "config.yaml") # re-load the config OmegaConf.load("config.yaml") self.logger.log_info("Saved settings to config.yaml") self.reload() def reload(self): # create a new connection to the database, refresh table data and replace the old connection self.db = Database() self.apparats = self.db.getAllAparats(deleted=0) self.apparats = natsorted(self.apparats, key=lambda x: x[4], reverse=True) self.tableWidget_apparate.setRowCount(0) for apparat in self.apparats: self.insert_apparat_into_table(apparat) def media_context_menu(self, position): menu = QtWidgets.QMenu() delete_action = menu.addAction("Löschen") edit_action = menu.addAction("Bearbeiten") menu.addAction(delete_action) menu.addAction(edit_action) delete_action.triggered.connect(self.delete_medium) edit_action.triggered.connect(self.edit_medium) menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) def edit_medium(self): book = self.tableWidget_apparat_media.item( self.tableWidget_apparat_media.currentRow(), 1 ).text() data = self.db.getBookBasedOnSignature( app_id=self.active_apparat, signature=book, prof_id=self.db.getProfId(self.drpdwn_prof_name.currentText()), ) book_id = self.db.getBookIdBasedOnSignature( self.active_apparat, self.db.getProfId(self.drpdwn_prof_name.currentText()), book, ) widget = QtWidgets.QDialog() bookedit = edit_bookdata_ui() bookedit.setupUi(widget) # change title of dialog widget.setWindowTitle("Metadaten") bookedit.populate_fields(data) widget.exec() if widget.result() == QtWidgets.QDialog.DialogCode.Accepted: data = bookedit.get_data() print(data) self.db.updateBookdata(data, book_id) # self.db.update_bookdata(data) print("accepted") self.update_app_media_list() else: return pass def delete_medium(self): selected_apparat_id = self.tableWidget_apparate.item( self.tableWidget_apparate.currentRow(), 0 ).text() # check how many rows are selected selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() if len(selected_rows) == 1: signature = self.tableWidget_apparat_media.item( self.tableWidget_apparat_media.currentRow(), 1 ).text() book_id = self.db.getBookIdBasedOnSignature( selected_apparat_id, prof_id=self.db.getProfId(self.drpdwn_prof_name.currentText()), signature=signature, ) message = f'Soll das Medium "{self.tableWidget_apparat_media.item(self.tableWidget_apparat_media.currentRow(),0).text()}" wirklich gelöscht werden?' state = self.confirm_popup(message, title="Löschen?") print(state) if state == 1: self.db.deleteBook(book_id) self.update_app_media_list() self.contact_prof(mail="deleted", location="") 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, title="Löschen?") 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) self.db.setNewSemesterDate( selected_apparat_id, data["semester"], dauerapp=data["dauerapp"] ) else: return def __get_table_data_field(self, table, row, column): ic(row, column) row = int(row) return table.item(row, column).text() def __contact_dialog(self, apparat, location: tuple | str, mail=None, pid=""): ic(location, pid) active_apparat_id = ( self.drpdwn_app_nr.currentText() if apparat is None else apparat ) print(active_apparat_id) profname = self.drpdwn_prof_name.currentText().replace(",", "").split(" ") profname = f"{profname[1]} {profname[0]}" # get the row of the clicked cell prof_id = self.db.getProfId(pid) if profname == "Name Kein": profname = pid profname = f"{profname.split(" ")[1]} {profname.split(" ")[0]}" print(profname, pid) if prof_id: pmail = self.db.getSpecificProfData(prof_id, ["mail"]) else: pmail = self.prof_mail.text() # create a new thread to show the mail interface and send the mail print("showing mail dialog") self.mail_thread = Mail_Dialog( app_id=active_apparat_id, prof_name=pid, prof_mail=pmail, app_name=self.app_name.text(), app_subject=self.app_fach.currentText(), default_mail=mail if mail != "" else "Information zum Semesterapparat", ) self.mail_thread.show() def contact_prof(self, location="", apparat=None, mail="", pid=""): print(apparat) print("location", location) if self.active_apparat == "": if apparat is False: self.confirm_popup( "Bitte erst einen Apparat auswählen!", title="Apparat auswählen" ) return self.__contact_dialog(apparat, mail=mail, pid=pid, location=location) def delete_apparat(self, position): 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, title="Löschen?") print(state) pid = self.__get_table_data_field(self.tableWidget_apparate, position[0], 2) 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, pid=pid) def launch_gui(): print("trying to login") print("checking if database available") app = QtWidgets.QApplication(sys.argv) login_dialog = QtWidgets.QDialog() ui = login_ui() ui.setupUi(login_dialog) login_dialog.exec() # This will block until the dialog is closed if ui.lresult == 1: # if login is successful, open main window # show login dialog print(ui.lusername) MainWindow = QtWidgets.QMainWindow() aui = Ui(MainWindow, username=ui.lusername) print(aui.active_user) MainWindow.show() # atexit.register() atexit.register(tempdelete) atexit.register(aui.validate_thread.quit) sys.exit(app.exec()) elif ui.lresult == 0: warning_dialog = QtWidgets.QMessageBox() warning_dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) warning_dialog.setText("Invalid username or password. Please try again.") warning_dialog.setWindowTitle("Login Failed") warning_dialog.exec() elif ui.lresult == 2: # TODO: implement admin functionality here """change passwords for apparats, change passwords for users, list users, create and delete users etc""" # open a console window # console = "" print("admin") atexit.register(tempdelete) if __name__ == "__main__": print("This is the main window") # app = QtWidgets.QApplication(sys.argv) # window = MainWindow() # app.exec() # open login screen launch_gui()