From cd74214c17cb682a8c17264b5141e4e1270a4b1d Mon Sep 17 00:00:00 2001 From: WorldTeacher <41587052+WorldTeacher@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:45:42 +0200 Subject: [PATCH] rework prof data using dataclass, change database code, fix bugs --- src/backend/__init__.py | 2 +- src/backend/admin_console.py | 2 +- src/backend/database.py | 68 ++++++++++++++++++--------------- src/backend/semester.py | 29 +++++++++++++- src/logic/__init__.py | 8 ++-- src/logic/dataclass.py | 44 +++++++++++++++++---- src/logic/thread_bookgrabber.py | 1 + src/ui/extensions/__init__.py | 1 + src/ui/userInterface.py | 38 +++++++++--------- src/ui/widgets/searchPage.py | 22 +++++++++-- 10 files changed, 150 insertions(+), 65 deletions(-) diff --git a/src/backend/__init__.py b/src/backend/__init__.py index 06f8260..b29c94a 100644 --- a/src/backend/__init__.py +++ b/src/backend/__init__.py @@ -2,4 +2,4 @@ from .admin_console import AdminCommands from .create_file import recreateElsaFile, recreateFile from .database import Database from .delete_temp_contents import delete_temp_contents as tempdelete -from .semester import generateSemesterByDate +from .semester import generateSemesterByDate, generateSemesterByOffset diff --git a/src/backend/admin_console.py b/src/backend/admin_console.py index aa35c97..fee467b 100644 --- a/src/backend/admin_console.py +++ b/src/backend/admin_console.py @@ -1,7 +1,7 @@ import hashlib import random -from src.backend.database import Database +from .database import Database # change passwords for apparats, change passwords for users, list users, create and delete users etc diff --git a/src/backend/database.py b/src/backend/database.py index 5aa7561..29c0853 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -4,7 +4,6 @@ import sqlite3 as sql import tempfile from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union -import shutil # from icecream import ic from omegaconf import OmegaConf @@ -23,11 +22,10 @@ from src.backend.db import ( CREATE_TABLE_USER, ) from src.errors import AppPresentError, NoResultError +from src.logic import ApparatData, BookData, Prof, MyLogger from src.logic.constants import SEMAP_MEDIA_ACCOUNTS -from src.logic.dataclass import ApparatData, BookData -from src.logic.log import MyLogger from src.utils import create_blob, dump_pickle, load_pickle -from src.backend.semester import generateSemesterByDate +from .semester import generateSemesterByDate from icecream import ic config = OmegaConf.load("config.yaml") @@ -217,6 +215,8 @@ class Database: app_id (str): The apparat id where the book should be added to prof_id (str): The id of the professor where the book should be added to. """ + if app_id is None or prof_id is None: + raise ValueError("Apparate ID or Prof ID is None") conn = self.connect() cursor = conn.cursor() t_query = ( @@ -553,7 +553,7 @@ class Database: "Musik", "Philosophie", "Physik", - "Poitikwissenschaft", + "Politikwissenschaft", "Prorektorat Lehre und Studium", "Psychologie", "Soziologie", @@ -717,23 +717,25 @@ class Database: tuple: the mail, telephone number and title of the professor """ data = self.query_db( - "SELECT mail, telnr, titel FROM prof WHERE fullname=?", + "SELECT * FROM prof WHERE fullname=?", (profname.replace(",", ""),), one=True, ) - return data + print(data) + person = Prof() + return person.from_tuple(data) - def getProfs(self) -> list[tuple]: + def getProfs(self) -> list[Prof]: """Return all the professors in the database Returns: list[tuple]: a list containing all the professors in individual tuples tuple: (id, titel, fname, lname, fullname, mail, telnr) """ - return self.query_db("SELECT * FROM prof") - + profs = self.query_db("SELECT * FROM prof") + return [Prof().from_tuple(prof) for prof in profs] # Apparat def getAllAparats(self, deleted=0) -> list[tuple]: """Get all the apparats in the database @@ -774,9 +776,9 @@ class Database: apparat.dauerapp = True if result[7] == 1 else False prof_data = self.getProfData(self.getProfNameById(result[2])) apparat.profname = self.getProfNameById(result[2]) - apparat.prof_mail = prof_data[0] - apparat.prof_tel = prof_data[1] - apparat.prof_title = prof_data[2] + apparat.prof_mail = prof_data.mail + apparat.prof_tel = prof_data.telnr + apparat.prof_title = prof_data.title apparat.app_fach = result[3] apparat.erstellsemester = result[5] apparat.semester = result[8] @@ -848,15 +850,16 @@ class Database: Optional[int]: the id of the apparat """ ic(apparat) - prof_id = self.getProfByName(apparat.get_prof_details()["fullname"]) + prof = self.getProfByName(apparat.prof_details.fullname) + prof_id = prof.id ic(prof_id, apparat.profname) - if prof_id: - prof_id = prof_id[0] + app_id = self.getApparatId(apparat.appname) if app_id: return AppPresentError(app_id) if not prof_id: - prof_id = self.createProf(apparat.get_prof_details()) + print("prof id not present, creating prof with data", apparat.prof_details) + prof_id = self.createProf(apparat.prof_details) # self.getProfId(apparat.profname) ic(prof_id) query = f"INSERT OR IGNORE INTO semesterapparat (appnr, name, erstellsemester, dauer, prof_id, fach,deletion_status,konto) VALUES ('{apparat.appnr}', '{apparat.appname}', '{apparat.semester}', '{apparat.dauerapp}', {prof_id}, '{apparat.app_fach}', '{0}', '{SEMAP_MEDIA_ACCOUNTS[apparat.appnr]}')" @@ -1000,7 +1003,7 @@ class Database: apparat_data.appname, apparat_data.app_fach, apparat_data.dauerapp, - self.getProfId(apparat_data.profname), + self.getProfData(apparat_data.prof_details.fullname).id, apparat_data.prof_adis_id, apparat_data.apparat_adis_id, apparat_data.appnr, @@ -1458,15 +1461,17 @@ class Database: - def createProf(self, profdata): + def createProf(self, profdata:Prof): + print("createProf") + ic(profdata) conn = self.connect() cursor = conn.cursor() - fname = profdata["profname"].split(", ")[1].strip() - lname = profdata["profname"].split(", ")[0].strip() + fname = profdata.firstname#profdata["profname"].split(", ")[1].strip() + lname = profdata.lastname#profdata["profname"].split(", ")[0].strip() fullname = f"{lname} {fname}" - mail = profdata["prof_mail"] - telnr = profdata["prof_tel"] - title = profdata["title"] + mail = profdata.mail#profdata["prof_mail"] + telnr = profdata.telnr#profdata["prof_tel"] + title = profdata.title #profdata["title"] query = f"INSERT INTO prof (fname, lname, fullname, mail, telnr,titel) VALUES ('{fname}','{lname}','{fullname}','{mail}','{telnr}','{title}')" self.logger.log_info(query) @@ -1476,12 +1481,15 @@ class Database: conn.close() return self.getProfId(profdata) - def getProfId(self, profdata): + def getProfId(self, profdata: dict|Prof): conn = self.connect() cursor = conn.cursor() - fname = profdata["profname"].split(", ")[1].strip() - lname = profdata["profname"].split(", ")[0].strip() - fullname = f"{lname} {fname}" + if isinstance(profdata, Prof): + fullname = profdata.fullname + else: + fname = profdata["profname"].split(", ")[1].strip() + lname = profdata["profname"].split(", ")[0].strip() + fullname = f"{lname} {fname}" query = f"SELECT id FROM prof WHERE fullname = '{fullname}'" self.logger.log_info(query) @@ -1504,8 +1512,8 @@ class Database: result = cursor.execute(query).fetchone() if result: - return result - else: return None + return Prof().from_tuple(result) + else: return Prof() def getProfNameByApparat(self, apprarat_id): query = f"SELECT prof_id from semesterapparat WHERE appnr = '{apprarat_id}'" data = self.query_db(query) diff --git a/src/backend/semester.py b/src/backend/semester.py index a373bbe..f848087 100644 --- a/src/backend/semester.py +++ b/src/backend/semester.py @@ -1,10 +1,15 @@ import datetime -def generateSemesterByDate(): +def generateSemesterByDate(next:bool = False): currentYear = datetime.datetime.now().year currentYear = int(str(currentYear)[-2:]) month = datetime.datetime.now().month + if next: + month += 1 + if month > 12: + month = 1 + currentYear += 1 if month >= 4 and month <= 9: return "SoSe " + str(currentYear) else: @@ -12,3 +17,25 @@ def generateSemesterByDate(): return f"WiSe {currentYear}/{currentYear+1}" else: return f"WiSe {currentYear-1}/{currentYear}" + + +def generateSemesterByOffset(offset): + currentYear = datetime.datetime.now().year + currentYear = int(str(currentYear)[-2:]) + month = datetime.datetime.now().month + #offset represents a single semester + semester = generateSemesterByDate() + if offset == 1: + if semester.startswith("SoSe"): + return f"WiSe {currentYear}/{currentYear+1}" + else: + return f"SoSe {currentYear+1}" + else: + #if offset is even, increase the currentyear by offset + if offset % 2 == 0: + if semester.startswith("SoSe"): + return f"SoSe {currentYear+offset//2}" + else: + return f"WiSe {currentYear+1}/{currentYear+2}" + else: + return f"WiSe {currentYear+offset//2}/{currentYear+1+offset//2}" \ No newline at end of file diff --git a/src/logic/__init__.py b/src/logic/__init__.py index 5a6587c..bbe64a1 100644 --- a/src/logic/__init__.py +++ b/src/logic/__init__.py @@ -1,10 +1,12 @@ +from .log import MyLogger +from .dataclass import ApparatData, BookData, Prof from .thread_bookgrabber import BookGrabber from .threads_autoadder import AutoAdder from .threads_availchecker import AvailChecker from .c_sort import custom_sort -from .constants import APP_NRS, PROF_TITLES -from .dataclass import ApparatData, BookData +from .constants import APP_NRS, PROF_TITLES, SEMAP_MEDIA_ACCOUNTS from .csvparser import csv_to_list from .wordparser import elsa_word_to_csv, word_docx_to_csv -from .log import MyLogger from .zotero import ZoteroController + + diff --git a/src/logic/dataclass.py b/src/logic/dataclass.py index 0f4bfaa..0bd5151 100644 --- a/src/logic/dataclass.py +++ b/src/logic/dataclass.py @@ -2,6 +2,34 @@ import re from dataclasses import dataclass, field from enum import Enum +@dataclass +class Prof: + id: int = None + title: str= None + firstname: str= None + lastname: str= None + fullname: str= None + mail: str= None + telnr: str= None + + #add function that sets the data based on a dict + def from_dict(self, data: dict): + for key, value in data.items(): + if hasattr(self, key): + setattr(self, key, value) + return self + + #add function that sets the data from a tuple + def from_tuple(self, data: tuple): + setattr(self, "id", data[0]) + setattr(self, "title", data[1]) + setattr(self, "firstname", data[2]) + setattr(self, "lastname", data[3]) + setattr(self, "fullname", data[4]) + setattr(self, "mail", data[5]) + setattr(self, "telnr", data[6]) + return self + @dataclass class ApparatData: @@ -18,15 +46,16 @@ class ApparatData: deleted: int = 0 prof_adis_id: int | None = None apparat_adis_id: int | None = None - - def get_prof_details(self) -> dict: - return { + @property + def prof_details(self) -> Prof: + return Prof().from_dict({ "title": self.prof_title, - "profname": self.profname, - "prof_mail": self.prof_mail, - "prof_tel": self.prof_tel, + "firstname": self.profname.split(',')[1].strip(), + "lastname": self.profname.split(',')[0].strip(), + "mail": self.prof_mail, + "telnr": self.prof_tel, "fullname": f"{self.profname.split(',')[0].strip()} {self.profname.split(',')[1].strip()}", - } + }) def translateToFullname(self): return f"{self.profname.split(',')[0].strip()} {self.profname.split(',')[1].strip()}" @@ -117,3 +146,4 @@ class Subjects(Enum): for i in cls: if i.name == name: return i.id - 1 + diff --git a/src/logic/thread_bookgrabber.py b/src/logic/thread_bookgrabber.py index 451b853..1572e36 100644 --- a/src/logic/thread_bookgrabber.py +++ b/src/logic/thread_bookgrabber.py @@ -50,6 +50,7 @@ class BookGrabber(QThread): self.logger.log_info("Processing entry: " + signature) webdata = WebRequest(self.appnr).get_ppn(entry).get_data() + if webdata == "error": continue diff --git a/src/ui/extensions/__init__.py b/src/ui/extensions/__init__.py index e69de29..afbbfc0 100644 --- a/src/ui/extensions/__init__.py +++ b/src/ui/extensions/__init__.py @@ -0,0 +1 @@ +from .ValidatorButton import ValidatorButton diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index a53cbd1..ec44c38 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -19,6 +19,7 @@ from src import Icon from src.backend import ( Database, generateSemesterByDate, + recreateFile, tempdelete, ) @@ -295,6 +296,7 @@ class Ui(Ui_Semesterapparat): self.app_fach.addItems([subject[1] for subject in self.db.getSubjects()]) def open_documentation(self): + raise NotImplementedError("Documentation not implemented yet") # open the documentation in the default browser webbrowser.open("file:///" + os.path.abspath("docs/index.html")) # documentation = documentationview.DocumentationViewer() @@ -310,7 +312,7 @@ class Ui(Ui_Semesterapparat): widget.deleteLater() statistics = SearchStatisticPage() statistics.apparat_open.connect(self.open_apparat) - statistics.reloadSignal.connect(self.reload) + statistics.refreshSignal.connect(self.update_apparat_list) stats_layout.addWidget(statistics) @@ -431,7 +433,7 @@ class Ui(Ui_Semesterapparat): else self.sem_winter.text() + " " + self.sem_year.text() ) appdata.prof_adis_id = self.prof_id_adis.text() - prof_id = self.db.getProfByName(appdata.profname)[0] + prof_id = self.db.getProfByName(appdata.prof_details.fullname).id self.add_files(prof_id) appdata.apparat_adis_id = self.apparat_id_adis.text() @@ -443,6 +445,8 @@ class Ui(Ui_Semesterapparat): self.chkbx_show_del_media.show() self.cancel_active_selection.setEnabled(False) self.add_medium.setEnabled(False) + #update apparat table + self.get_apparats() def confirm_popup(self, message: str, title: str): popup = popus_confirm(title=title) @@ -538,6 +542,7 @@ class Ui(Ui_Semesterapparat): valid_input = list(valid_input) valid_input[index] = state valid_input = tuple(valid_input) + def set_state(self): # set state of semester and year @@ -561,12 +566,13 @@ class Ui(Ui_Semesterapparat): selected_prof = self.drpdwn_prof_name.currentText() data = self.db.getProfData(selected_prof) # ic(data) - prof_title = data[2] + prof_title = data.title 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]) + self.prof_tel_nr.setText(data.telnr) + self.prof_mail.setText(data.mail) + self.app_name.setFocus() def get_index_of_value(self, table_widget, value): for i in range(table_widget.rowCount()): @@ -614,7 +620,7 @@ class Ui(Ui_Semesterapparat): app_id = self.active_apparat prof_id = self.db.getProfByName( self.drpdwn_prof_name.currentText().replace(",","") - )[0] + ).id files = self.db.getFiles(app_id, prof_id) for file in files: self.dokument_list.insertRow(0) @@ -887,7 +893,7 @@ class Ui(Ui_Semesterapparat): if columnname == "Link": link = __openLink(item.text()) if link is not None: - webbrowser.open(link) + os.system("start " + link) return else: pass @@ -903,7 +909,7 @@ class Ui(Ui_Semesterapparat): # 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]}") + self.drpdwn_prof_name.addItem(f"{prof.lastname}, {prof.firstname}") def add_document(self): # print("Add document") @@ -1113,6 +1119,8 @@ class Ui(Ui_Semesterapparat): # print("starting thread") if prof_id is None: prof_id = self.db.getProfId(self.profdata) + + print("Prof ID is None", prof_id) autoGrabber = BookGrabber(self.active_apparat) autoGrabber.add_values( mode="ARRAY", app_id=app_id, prof_id=prof_id, data=signatures @@ -1188,8 +1196,7 @@ class Ui(Ui_Semesterapparat): appd.deleted = 0 appd.prof_adis_id = self.prof_id_adis.text() appd.apparat_adis_id = self.apparat_id_adis.text() - if not self.validate_fields(): - pass + error = self.db.createApparat(appd) if self.dokument_list.rowCount() > 0: @@ -1260,6 +1267,7 @@ class Ui(Ui_Semesterapparat): def update_apparat_list(self): self.tableWidget_apparate.setRowCount(0) + for apparat in self.apparats: self.insert_apparat_into_table(apparat) @@ -1387,14 +1395,6 @@ class Ui(Ui_Semesterapparat): 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) - # kill the process and restart the application state = self.confirm_popup( "Bitte das Programm neustarten, um Änderungen zu übernehemen", "Einstellungen geändert", @@ -1535,7 +1535,7 @@ class Ui(Ui_Semesterapparat): # if pid == "": # pid = profname # get the row of the clicked cell - prof_id = self.db.getProfByName(pid)[0] + prof_id = self.db.getProfByName(pid).id # if profname == "Name Kein": # profname = pid if self.app_name.text() != "": diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index e4fc6f6..bb8544f 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -2,10 +2,12 @@ from .widget_sources.Ui_search_statistic_page import Ui_Dialog from PyQt6 import QtWidgets, QtGui from PyQt6.QtCore import pyqtSignal from src.backend import Database, generateSemesterByDate -from src.logic import custom_sort +from src.logic import custom_sort, Prof from src import MyLogger +from src.ui import ApparatExtendDialog from src.ui.dialogs import Mail_Dialog from src.ui.widgets import DataGraph, StatusWidget + from natsort import natsorted from icecream import ic @@ -20,7 +22,7 @@ class MyComboBox(QtWidgets.QComboBox): class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): apparat_open = pyqtSignal(str) reloadSignal = pyqtSignal() - + refreshSignal = pyqtSignal() def __init__(self): self.logger = MyLogger("SearchStatisticPage") self.logger.log_info("SearchStatisticPage started") @@ -65,10 +67,24 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): def statistics_table_context_menu(self, position): menu = QtWidgets.QMenu() restore_action = menu.addAction("Wiederherstellen") + extend_action = menu.addAction("Verlängern") menu.addAction(restore_action) restore_action.triggered.connect(self.restore_apparat) + extend_action.triggered.connect(self.extend_apparat) menu.exec(self.tableWidget.mapToGlobal(position)) + def extend_apparat(self): + extend = ApparatExtendDialog() + extend.exec() + if extend.result() == QtWidgets.QDialog.DialogCode.Accepted: + data = extend.get_data() + ic(data) + app_name = self.tableWidget.item(self.tableWidget.currentRow(), 1).text() + app_id = self.db.getApparatId(app_name) + self.db.setNewSemesterDate(app_id, data["semester"], data["dauerapp"]) + #remove the row + self.tableWidget.removeRow(self.tableWidget.currentRow()) + self.refreshSignal.emit() def tabW2_changed(self): if self.tabWidget_2.currentIndex() == 0: @@ -204,7 +220,7 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): 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_person.addItems([f"{person.lastname}, {person.firstname}" 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)