Module database
++Expand source code +
+import datetime
+import os
+import re
+import sqlite3 as sql
+import tempfile
+from src.logic.log import MyLogger
+from icecream import ic
+from typing import List, Tuple, Dict, Any, Optional, Union
+from pathlib import Path
+from omegaconf import OmegaConf
+from src.backend.db import CREATE_TABLE_APPARAT, CREATE_TABLE_MESSAGES, CREATE_TABLE_MEDIA, CREATE_TABLE_APPKONTOS, CREATE_TABLE_FILES, CREATE_TABLE_PROF, CREATE_TABLE_USER, CREATE_TABLE_SUBJECTS
+from src.logic.constants import SEMAP_MEDIA_ACCOUNTS
+from src.logic.dataclass import ApparatData, BookData
+from src.errors import NoResultError, AppPresentError
+from src.utils import load_pickle, dump_pickle,create_blob
+config = OmegaConf.load("config.yaml")
+logger = MyLogger(__name__)
+
+
+class Database:
+ """
+ Initialize the database and create the tables if they do not exist.
+ """
+ def __init__(self, db_path: str = None):
+ """
+ Default constructor for the database class
+
+ Args:
+ db_path (str, optional): Optional Path for testing / specific purposes. Defaults to None.
+ """
+ if db_path is None:
+ self.db_path = config.database.path + config.database.name
+ self.db_path = self.db_path.replace("~", str(Path.home()))
+ else:
+ self.db_path = db_path
+ if self.get_db_contents() is None:
+ logger.log_critical("Database does not exist, creating tables")
+ self.create_tables()
+ def get_db_contents(self)->Union[List[Tuple], None]:
+ """
+ Get the contents of the
+
+ Returns:
+ Union[List[Tuple], None]: _description_
+ """
+ try:
+ with sql.connect(self.db_path) as conn:
+ cursor = conn.cursor()
+ cursor.execute("SELECT * FROM sqlite_master WHERE type='table'")
+ return cursor.fetchall()
+ except sql.OperationalError:
+ return None
+ def connect(self)->sql.Connection:
+ """
+ Connect to the database
+
+ Returns:
+ sql.Connection: The active connection to the database
+ """
+ return sql.connect(self.db_path)
+ def close_connection(self, conn: sql.Connection):
+ """
+ closes the connection to the database
+
+ Args:
+ ----
+ - conn (sql.Connection): the connection to be closed
+ """
+ conn.close()
+ def create_tables(self):
+ """
+ Create the tables in the database
+ """
+ conn = self.connect()
+ cursor = conn.cursor()
+ cursor.execute(CREATE_TABLE_APPARAT)
+ cursor.execute(CREATE_TABLE_MESSAGES)
+ cursor.execute(CREATE_TABLE_MEDIA)
+ cursor.execute(CREATE_TABLE_APPKONTOS)
+ cursor.execute(CREATE_TABLE_FILES)
+ cursor.execute(CREATE_TABLE_PROF)
+ cursor.execute(CREATE_TABLE_USER)
+ cursor.execute(CREATE_TABLE_SUBJECTS)
+ conn.commit()
+ self.close_connection(conn)
+ def insertInto(self, query:str, params:Tuple) -> None:
+ """
+ Insert sent data into the database
+
+ Args:
+ query (str): The query to be executed
+ params (Tuple): the parameters to be inserted into the database
+ """
+ conn = self.connect()
+ cursor = conn.cursor()
+ logger.log_info(f"Inserting {params} into database with query {query}")
+ cursor.execute(query, params)
+ conn.commit()
+ self.close_connection(conn)
+ def query_db(self, query: str, args: Tuple = (), one: bool = False)->Union[Tuple, List[Tuple]]:
+ """
+ Query the Database for the sent query.
+
+ Args:
+ query (str): The query to be executed
+ args (Tuple, optional): The arguments for the query. Defaults to ().
+ one (bool, optional): Return the first result only. Defaults to False.
+
+ Returns:
+ Union[Typle|List[Tuple]]: Returns the result of the query
+ """
+ conn = self.connect()
+ cursor = conn.cursor()
+ logger.log_info(f"Querying database with query {query}, args: {args}")
+ cursor.execute(query, args)
+ rv = cursor.fetchall()
+ conn.commit()
+ self.close_connection(conn)
+ return (rv[0] if rv else None) if one else rv
+
+ # Books
+ def addBookToDatabase(self, bookdata:BookData,app_id:Union[str,int], prof_id:Union[str,int]):
+ """
+ Add books to the database. Both app_id and prof_id are required to add the book to the database, as the app_id and prof_id are used to select the books later on.
+
+ Args:
+ bookdata (BookData): The metadata of the book to be added
+ 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.
+ """
+ conn = self.connect()
+ cursor = conn.cursor()
+ t_query = (
+ f"SELECT bookdata FROM media WHERE app_id={app_id} AND prof_id={prof_id}"
+ )
+ # print(t_query)
+ result = cursor.execute(t_query).fetchall()
+ result = [load_pickle(i[0]) for i in result]
+ if bookdata in result:
+ print("Bookdata already in database")
+ # check if the book was deleted in the apparat
+ query = (
+ "SELECT deleted FROM media WHERE app_id=? AND prof_id=? AND bookdata=?"
+ )
+ params = (app_id, prof_id, dump_pickle(bookdata))
+ result = cursor.execute(query, params).fetchone()
+ if result[0] == 1:
+ print("Book was deleted, updating bookdata")
+ query = "UPDATE media SET deleted=0 WHERE app_id=? AND prof_id=? AND bookdata=?"
+ params = (app_id, prof_id, dump_pickle(bookdata))
+ cursor.execute(query, params)
+ conn.commit()
+ return
+
+ query = (
+ "INSERT INTO media (bookdata, app_id, prof_id,deleted) VALUES (?, ?, ?,?)"
+ )
+ converted = dump_pickle(bookdata)
+ params = (converted, app_id, prof_id, 0)
+ cursor.execute(query, params)
+ conn.commit()
+ self.close_connection(conn)
+ def getBookIdBasedOnSignature(self, app_id:Union[str,int], prof_id:Union[str,int],signature:str)->int:
+ """
+ Get a book id based on the signature of the book.
+
+ Args:
+ app_id (str): The apparat id the book should be associated with
+ prof_id (str): The professor id the book should be associated with
+ signature (str): The signature of the book
+
+ Returns:
+ int: The id of the book
+ """
+ result = self.query_db("SELECT bookdata, id FROM media WHERE app_id=? AND prof_id=?", (app_id,prof_id))
+ books = [(load_pickle(i[0]),i[1]) for i in result]
+ book = [i for i in books if i[0].signature == signature][0][1]
+ return book
+ def getBookBasedOnSignature(self, app_id:Union[str,int], prof_id:Union[str,int],signature:str)->BookData:
+ """
+ Get the book based on the signature of the book.
+
+ Args:
+ app_id (str): The apparat id the book should be associated with
+ prof_id (str): The professor id the book should be associated with
+ signature (str): The signature of the book
+
+ Returns:
+ BookData: The total metadata of the book wrapped in a BookData object
+ """
+ result = self.query_db("SELECT bookdata FROM media WHERE app_id=? AND prof_id=?", (app_id,prof_id))
+ books = [load_pickle(i[0]) for i in result]
+ book = [i for i in books if i.signature == signature][0]
+ return book
+ def getLastBookId(self)->int:
+ """
+ Get the last book id in the database
+
+ Returns:
+ int: ID of the last book in the database
+ """
+ return self.query_db("SELECT id FROM media ORDER BY id DESC", one=True)[0]
+ def searchBook(self, data:dict[str, str])->list[tuple[BookData, int]]:
+ """
+ Search a book in the database based on the sent data.
+
+ Args:
+ data (dict[str, str]): A dictionary containing the data to be searched for. The dictionary can contain the following:
+ - signature: The signature of the book
+ - title: The title of the book
+
+ Returns:
+ list[tuple[BookData, int]]: A list of tuples containing the wrapped Metadata and the id of the book
+ """
+ rdata = self.query_db("SELECT * FROM media WHERE deleted=0")
+ ic(rdata, len(rdata))
+ mode = 0
+ if len(data)== 1:
+ if "signature" in data.keys():
+ mode = 1
+ elif "title" in data.keys():
+ mode = 2
+ elif len(data) == 2:
+ mode = 3
+ else:
+ return None
+ ret = []
+ for book in rdata:
+ bookdata = load_pickle(book[1])
+ app_id = book[2]
+ prof_id = book[3]
+ if mode == 1:
+ if data["signature"] in bookdata.signature:
+ ret.append((bookdata,app_id,prof_id))
+ elif mode == 2:
+ if data["title"] in bookdata.title:
+ ret.append((bookdata,app_id,prof_id))
+ elif mode == 3:
+ if data["signature"] in bookdata.signature and data["title"] in bookdata.title:
+ ret.append((bookdata,app_id,prof_id))
+ ic(ret)
+ return ret
+ def setAvailability(self, book_id:str, available:str):
+ """
+ Set the availability of a book in the database
+
+ Args:
+ book_id (str): The id of the book
+ available (str): The availability of the book
+ """
+ self.query_db("UPDATE media SET available=? WHERE id=?", (available,book_id))
+ def getBookId(self, bookdata:BookData, app_id:Union[str,int], prof_id:Union[str,int])->int:
+ """
+ Get the id of a book based on the metadata of the book
+
+ Args:
+ bookdata (BookData): The wrapped metadata of the book
+ app_id (str): The apparat id the book should be associated with
+ prof_id (str): The professor id the book should be associated with
+
+ Returns:
+ int: ID of the book
+ """
+ result = self.query_db("SELECT id FROM media WHERE bookdata=? AND app_id=? AND prof_id=?", (dump_pickle(bookdata),app_id,prof_id), one=True)
+ return result[0]
+ def getBook(self,book_id:int)->BookData:
+ """
+ Get the book based on the id in the database
+
+ Args:
+ book_id (int): The id of the book
+
+ Returns:
+ BookData: The metadata of the book wrapped in a BookData object
+ """
+ return load_pickle(self.query_db("SELECT bookdata FROM media WHERE id=?", (book_id,), one=True)[0])
+ def getBooks(self, app_id:Union[str,int], prof_id:Union[str,int], deleted=0)->list[dict[int, BookData, int]]:
+ """
+ Get the Books based on the apparat id and the professor id
+
+ Args:
+ app_id (str): The ID of the apparat
+ prof_id (str): The ID of the professor
+ deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0.
+
+ Returns:
+ list[dict[int, BookData, int]]: A list of dictionaries containing the id, the metadata of the book and the availability of the book
+ """
+ qdata = self.query_db(f"SELECT id,bookdata,available FROM media WHERE (app_id={app_id} AND prof_id={prof_id}) AND (deleted={deleted if deleted == 0 else '1 OR deleted=0'})")
+ ret_result = []
+ for result_a in qdata:
+ data = {"id": int, "bookdata": BookData, "available": int}
+ data["id"] = result_a[0]
+ data["bookdata"] = load_pickle(result_a[1])
+ data["available"] = result_a[2]
+ ret_result.append(data)
+ return ret_result
+ def updateBookdata(self, book_id, bookdata:BookData):
+ """
+ Update the bookdata in the database
+
+ Args:
+ book_id (str): The id of the book
+ bookdata (BookData): The new metadata of the book
+ """
+ self.query_db("UPDATE media SET bookdata=? WHERE id=?", (dump_pickle(bookdata),book_id))
+ def deleteBook(self, book_id):
+ """
+ Delete a book from the database
+
+ Args:
+ book_id (str): ID of the book
+ """
+ self.query_db("UPDATE media SET deleted=1 WHERE id=?", (book_id,))
+
+ # File Interactions
+ def getBlob(self, filename, app_id:Union[str,int]):
+ """
+ Get a blob from the database
+
+ Args:
+ filename (str): The name of the file
+ app_id (str): ID of the apparat
+
+ Returns:
+ bytes: The file stored in
+ """
+ return self.query_db("SELECT fileblob FROM files WHERE filename=? AND app_id=?", (filename,app_id), one=True)[0]
+ def insertFile(self, file: list[dict], app_id:Union[str,int], prof_id:Union[str,int]):
+ """Instert a list of files into the database
+
+ Args:
+ file (list[dict]): a list containing all the files to be inserted
+ Structured: [{"name": "filename", "path": "path", "type": "filetype"}]
+ app_id (int): the id of the apparat
+ prof_id (str): the id of the professor
+ """
+ for f in file:
+ filename = f["name"]
+ path = f["path"]
+ filetyp = f["type"]
+ if path == "Database":
+ continue
+ blob = create_blob(path)
+ query = "INSERT OR IGNORE INTO files (filename, fileblob, app_id, filetyp,prof_id) VALUES (?, ?, ?, ?,?)"
+ self.query_db(query, (filename, blob, app_id, filetyp,prof_id))
+ def recreateFile(self, filename:str, app_id:Union[str,int],filetype:str)->str:
+ """Recreate a file from the database
+
+ Args:
+ filename (str): the name of the file
+ app_id (Union[str,int]): the id of the apparat
+ filetype (str): the extension of the file to be created
+
+ Returns:
+ str: The filename of the recreated file
+ """
+ blob = self.getBlob(filename, app_id)
+ tempdir = config.database.tempdir
+ tempdir = tempdir.replace("~", str(Path.home()))
+ tempdir_path = Path(tempdir)
+ if not os.path.exists(tempdir_path):
+ os.mkdir(tempdir_path)
+ file = tempfile.NamedTemporaryFile(
+ delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}"
+ )
+ file.write(blob)
+ print("file created")
+ return file.name
+ def getFiles(self, app_id:Union[str,int], prof_id:int)->list[tuple]:
+ """Get all the files associated with the apparat and the professor
+
+ Args:
+ app_id (Union[str,int]): The id of the apparat
+ prof_id (Union[str,int]): the id of the professor
+
+ Returns:
+ list[tuple]: a list of tuples containing the filename and the filetype for the corresponding apparat and professor
+ """
+ return self.query_db("SELECT filename, filetyp FROM files WHERE app_id=? AND prof_id=?", (app_id,prof_id))
+
+ def getSemersters(self)->list[str]:
+ """Return all the unique semesters in the database
+
+ Returns:
+ list: a list of strings containing the semesters
+ """
+ data = self.query_db("SELECT DISTINCT erstellsemester FROM semesterapparat")
+ return [i[0] for i in data]
+
+ def getSubjects(self):
+ """Get all the subjects in the database
+
+ Returns:
+ list[tuple]: a list of tuples containing the subjects
+ """
+ return self.query_db("SELECT * FROM subjects")
+
+ # Messages
+ def addMessage(self, message:dict,user:str, app_id:Union[str,int]):
+ """add a Message to the database
+
+ Args:
+ message (dict): the message to be added
+ user (str): the user who added the message
+ app_id (Union[str,int]): the id of the apparat
+ """
+ def __getUserId(user):
+ return self.query_db("SELECT id FROM user WHERE username=?", (user,), one=True)[0]
+ user_id = __getUserId(user)
+ self.query_db("INSERT INTO messages (message, user_id, remind_at,appnr) VALUES (?,?,?,?)", (message["message"],user_id,message["remind_at"],app_id))
+ def getMessages(self, date:str)->list[dict[str, str, str, str]]:
+ """Get all the messages for a specific date
+
+ Args:
+ date (str): a date.datetime object formatted as a string in the format "YYYY-MM-DD"
+
+ Returns:
+ list[dict[str, str, str, str]]: a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message
+ """
+ def __get_user_name(user_id):
+ return self.query_db("SELECT username FROM user WHERE id=?", (user_id,), one=True)[0]
+ messages = self.query_db("SELECT * FROM messages WHERE remind_at=?", (date,))
+ ret = [
+ {
+ "message": i[2],
+ "user": __get_user_name(i[4]),
+ "appnr": i[5],
+ "id": i[0]
+ }
+ for i in messages
+ ]
+ return ret
+ def deleteMessage(self, message_id):
+ """Delete a message from the database
+
+ Args:
+ message_id (str): the id of the message
+ """
+ self.query_db("DELETE FROM messages WHERE id=?", (message_id,))
+
+ # Prof data
+ def getProfNameById(self, prof_id:Union[str,int],add_title:bool=False)->str:
+ """Get a professor name based on the id
+
+ Args:
+ prof_id (Union[str,int]): The id of the professor
+ add_title (bool, optional): wether to add the title or no. Defaults to False.
+
+ Returns:
+ str: The name of the professor
+ """
+ prof = self.query_db("SELECT fullname FROM prof WHERE id=?", (prof_id,), one=True)
+ if add_title:
+ return f"{self.getTitleById(prof_id)}{prof[0]}"
+ else:
+ return prof[0]
+ def getTitleById(self, prof_id:Union[str,int])->str:
+ """get the title of a professor based on the id
+
+ Args:
+ prof_id (Union[str,int]): the id of the professor
+
+ Returns:
+ str: the title of the professor, with an added whitespace at the end, if no title is present, an empty string is returned
+ """
+ title = self.query_db("SELECT titel FROM prof WHERE id=?", (prof_id,), one=True)[0]
+ return f"{title} " if title is not None else ""
+ def getProfByName(self, prof_name:str)->tuple:
+ """get all the data of a professor based on the name
+
+ Args:
+ prof_name (str): the name of the professor
+
+ Returns:
+ tuple: the data of the professor
+ """
+ return self.query_db("SELECT * FROM prof WHERE fullname=?", (prof_name,), one=True)
+ def getProfId(self, prof_name:str)->Optional[int]:
+ """Get the id of a professor based on the name
+
+ Args:
+ prof_name (str): the name of the professor
+
+ Returns:
+ Optional[int]: the id of the professor, if the professor is not found, None is returned
+ """
+
+ data = self.getProfByName(prof_name.replace(",", ""))
+ if data is None:
+ return None
+ else:
+ return data[0]
+ def getSpecificProfData(self, prof_id:Union[str,int], fields:List[str])->tuple:
+ """A customisable function to get specific data of a professor based on the id
+
+ Args:
+ prof_id (Union[str,int]): the id of the professor
+ fields (List[str]): a list of fields to be returned
+
+ Returns:
+ tuple: a tuple containing the requested data
+ """
+ query = "SELECT "
+ for field in fields:
+ query += f"{field},"
+ query = query[:-1]
+ query += " FROM prof WHERE id=?"
+ return self.query_db(query, (prof_id,), one=True)[0]
+ def getProfData(self, profname:str):
+ """Get mail, telephone number and title of a professor based on the name
+
+ Args:
+ profname (str): name of the professor
+
+ Returns:
+ tuple: the mail, telephone number and title of the professor
+ """
+ data = self.query_db("SELECT mail, telnr, titel FROM prof WHERE fullname=?", (profname.replace(",",""),), one=True)
+ return data
+ def createProf(self, prof_details:dict):
+ """Create a professor in the database
+
+ Args:
+ prof_details (dict): a dictionary containing the details of the professor
+ """
+ prof_title = prof_details["prof_title"]
+ prof_fname = prof_details["profname"].split(",")[1]
+ prof_fname = prof_fname.strip()
+ prof_lname = prof_details["profname"].split(",")[0]
+ prof_lname = prof_lname.strip()
+ prof_fullname = prof_details["profname"].replace(",", "")
+ prof_mail = prof_details["prof_mail"]
+ prof_tel = prof_details["prof_tel"]
+ params = (prof_title, prof_fname, prof_lname, prof_mail, prof_tel, prof_fullname)
+ query = "INSERT OR IGNORE INTO prof (titel, fname, lname, mail, telnr, fullname) VALUES (?, ?, ?, ?, ?, ?)"
+ self.insertInto(query=query, params=params)
+ def getProfs(self)->list[tuple]:
+ """Return all the professors in the database
+
+ Returns:
+ list[tuple]: a list containing all the professors in individual tuples
+ """
+ return self.query_db("SELECT * FROM prof")
+
+ # Apparat
+ def getAllAparats(self,deleted=0)->list[tuple]:
+ """Get all the apparats in the database
+
+ Args:
+ deleted (int, optional): Switch the result to use . Defaults to 0.
+
+ Returns:
+ list[tuple]: a list of tuples containing the apparats
+ """
+ return self.query_db("SELECT * FROM semesterapparat WHERE deletion_status=?", (deleted,))
+ def getApparatData(self, appnr, appname)->ApparatData:
+ """Get the Apparat data based on the apparat number and the name
+
+ Args:
+ appnr (str): the apparat number
+ appname (str): the name of the apparat
+
+ Raises:
+ NoResultError: an error is raised if no result is found
+
+ Returns:
+ ApparatData: the appended data of the apparat wrapped in an ApparatData object
+ """
+ result = self.query_db("SELECT * FROM semesterapparat WHERE appnr=? AND name=?", (appnr,appname), one=True)
+ if result is None:
+ raise NoResultError("No result found")
+ apparat = ApparatData()
+ apparat.appname = result[1]
+ apparat.appnr = result[4]
+ 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.app_fach = result[3]
+ apparat.erstellsemester = result[5]
+ apparat.semester = result[8]
+ apparat.deleted = result[9]
+ apparat.apparat_adis_id = result[11]
+ apparat.prof_adis_id = result[12]
+ return apparat
+ def getUnavailableApparatNumbers(self)->List[int]:
+ """Get a list of all the apparat numbers in the database that are currently in use
+
+ Returns:
+ List[int]: the list of used apparat numbers
+ """
+ numbers = self.query_db("SELECT appnr FROM semesterapparat WHERE deletion_status=0")
+ numbers = [i[0] for i in numbers]
+ logger.log_info(f"Currently used apparat numbers: {numbers}")
+ return numbers
+ def setNewSemesterDate(self, app_id:Union[str,int], newDate, dauerapp=False):
+ """Set the new semester date for an apparat
+
+ Args:
+ app_id (Union[str,int]): the id of the apparat
+ newDate (str): the new date
+ dauerapp (bool, optional): if the apparat was changed to dauerapparat. Defaults to False.
+ """
+ date = datetime.datetime.strptime(newDate, "%d.%m.%Y").strftime("%Y-%m-%d")
+ if dauerapp:
+ self.query_db("UPDATE semesterapparat SET verlängerung_bis=?, dauerapp=? WHERE appnr=?", (date,dauerapp,app_id))
+ else:
+ self.query_db("UPDATE semesterapparat SET endsemester=? WHERE appnr=?", (date,app_id))
+ def getApparatId(self, apparat_name)->Optional[int]:
+ """get the id of an apparat based on the name
+
+ Args:
+ apparat_name (str): the name of the apparat e.g. "Semesterapparat 1"
+
+ Returns:
+ Optional[int]: the id of the apparat, if the apparat is not found, None is returned
+ """
+ data = self.query_db("SELECT appnr FROM semesterapparat WHERE name=?", (apparat_name,), one=True)
+ if data is None:
+ return None
+ else:
+ return data[0]
+ def createApparat(self, apparat:ApparatData)->int:
+ """create the apparat in the database
+
+ Args:
+ apparat (ApparatData): the wrapped metadata of the apparat
+
+ Raises:
+ AppPresentError: an error describing that the apparats chosen id is already present in the database
+
+ Returns:
+ Optional[int]: the id of the apparat
+ """
+
+ prof_id = self.getProfId(apparat.profname)
+ app_id = self.getApparatId(apparat.appname)
+ if app_id:
+ raise AppPresentError(app_id)
+
+ self.createProf(apparat.get_prof_details())
+ prof_id = 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]}')"
+ logger.log_info(query)
+ self.query_db(query)
+ return self.getApparatId(apparat.appname)
+ def getApparatsByProf(self, prof_id:Union[str,int])->list[tuple]:
+ """Get all apparats based on the professor id
+
+ Args:
+ prof_id (Union[str,int]): the id of the professor
+
+ Returns:
+ list[tuple]: a list of tuples containing the apparats
+ """
+ return self.query_db("SELECT * FROM semesterapparat WHERE prof_id=?", (prof_id,))
+ def getApparatsBySemester(self, semester:str)->dict[list]:
+ """get all apparats based on the semester
+
+ Args:
+ semester (str): the selected semester
+
+ Returns:
+ dict[list]: a list off all created and deleted apparats for the selected semester
+ """
+ data = self.query_db("SELECT name, prof_id FROM semesterapparat WHERE erstellsemester=?", (semester,))
+ conn = self.connect()
+ cursor = conn.cursor()
+ c_tmp = []
+ for i in data:
+ c_tmp.append((i[0], self.getProfNameById(i[1])))
+ query = (
+ f"SELECT name,prof_id FROM semesterapparat WHERE deleted_date='{semester}'"
+ )
+ result = cursor.execute(query).fetchall()
+ d_tmp = []
+ for i in result:
+ d_tmp.append((i[0], self.getProfNameById(i[1])))
+ # group the apparats by prof
+ c_ret = {}
+ for i in c_tmp:
+ if i[1] not in c_ret.keys():
+ c_ret[i[1]] = [i[0]]
+ else:
+ c_ret[i[1]].append(i[0])
+ d_ret = {}
+ for i in d_tmp:
+ if i[1] not in d_ret.keys():
+ d_ret[i[1]] = [i[0]]
+ else:
+ d_ret[i[1]].append(i[0])
+ self.close_connection(conn)
+ return {"created": c_ret, "deleted": d_ret}
+ def getApparatCountBySemester(self)->tuple[list[str],list[int]]:
+ """get a list of all apparats created and deleted by semester
+
+ Returns:
+ tuple[list[str],list[int]]: a tuple containing two lists, the first list contains the semesters, the second list contains the amount of apparats created and deleted for the corresponding semester
+ """
+ conn = self.connect()
+ cursor = conn.cursor()
+ semesters = self.getSemersters()
+ created = []
+ deleted = []
+ for semester in semesters:
+ query = f"SELECT COUNT(*) FROM semesterapparat WHERE erstellsemester='{semester}'"
+ result = cursor.execute(query).fetchone()
+ created.append(result[0])
+ query = f"SELECT COUNT(*) FROM semesterapparat WHERE deletion_status=1 AND deleted_date='{semester}'"
+ result = cursor.execute(query).fetchone()
+ deleted.append(result[0])
+ # store data in a tuple
+ ret = []
+ e_tuple = ()
+ for sem in semesters:
+ e_tuple = (
+ sem,
+ created[semesters.index(sem)],
+ deleted[semesters.index(sem)],
+ )
+ ret.append(e_tuple)
+ self.close_connection(conn)
+ return ret
+ def deleteApparat(self, app_id:Union[str,int], semester:str):
+ """Delete an apparat from the database
+
+ Args:
+ app_id (Union[str, int]): the id of the apparat
+ semester (str): the semester the apparat should be deleted from
+ """
+ self.query_db("UPDATE semesterapparat SET deletion_status=1, deleted_date=? WHERE appnr=?", (semester,app_id))
+ def isEternal(self, id):
+ """check if the apparat is eternal (dauerapparat)
+
+ Args:
+ id (int): the id of the apparat to be checked
+
+ Returns:
+ int: the state of the apparat
+ """
+ return self.query_db("SELECT dauer FROM semesterapparat WHERE appnr=?", (id,), one=True)
+ def getApparatName(self, app_id:Union[str,int], prof_id:Union[str,int]):
+ """get the name of the apparat based on the id
+
+ Args:
+ app_id (Union[str,int]): the id of the apparat
+ prof_id (Union[str,int]): the id of the professor
+
+ Returns:
+ str: the name of the apparat
+ """
+ return self.query_db("SELECT name FROM semesterapparat WHERE appnr=? AND prof_id=?", (app_id,prof_id), one=True)[0]
+ def updateApparat(self, apparat_data:ApparatData):
+ """Update an apparat in the database
+
+ Args:
+ apparat_data (ApparatData): the new metadata of the apparat
+ """
+ query = f"UPDATE semesterapparat SET name = ?, fach = ?, dauer = ?, prof_id = ? WHERE appnr = ?"
+ params = (
+ apparat_data.appname,
+ apparat_data.app_fach,
+ apparat_data.dauerapp,
+ self.getProfId(apparat_data.profname),
+ apparat_data.appnr,
+ )
+ self.query_db(query, params)
+ def checkApparatExists(self, apparat_name:str):
+ """check if the apparat is already present in the database based on the name
+
+ Args:
+ apparat_name (str): the name of the apparat
+
+ Returns:
+ bool: True if the apparat is present, False if not
+ """
+ return True if self.query_db("SELECT appnr FROM semesterapparat WHERE name=?", (apparat_name,), one=True) else False
+ def checkApparatExistsById(self, app_id:Union[str,int])->bool:
+ """a check to see if the apparat is already present in the database, based on the id
+
+ Args:
+ app_id (Union[str, int]): the id of the apparat
+
+ Returns:
+ bool: True if the apparat is present, False if not
+ """
+ return True if self.query_db("SELECT appnr FROM semesterapparat WHERE appnr=?", (app_id,), one=True) else False
+ # Statistics
+ def statistic_request(self, **kwargs: Any):
+ """Take n amount of kwargs and return the result of the query
+ """
+ def __query(query):
+ """execute the query and return the result
+
+ Args:
+ query (str): the constructed query
+
+ Returns:
+ list: the result of the query
+ """
+ conn = self.connect()
+ cursor = conn.cursor()
+ result = cursor.execute(query).fetchall()
+ for result_a in result:
+ orig_value = result_a
+ prof_name = self.getProfNameById(result_a[2])
+ # replace the prof_id with the prof_name
+ result_a = list(result_a)
+ result_a[2] = prof_name
+ result_a = tuple(result_a)
+ result[result.index(orig_value)] = result_a
+ self.close_connection(conn)
+ return result
+ if "deletable" in kwargs.keys():
+ query = f"SELECT * FROM semesterapparat WHERE deletion_status=0 AND dauer=0 AND (erstellsemester!='{kwargs['deletesemester']}' OR verlängerung_bis!='{kwargs['deletesemester']}')"
+ return __query(query)
+ if "dauer" in kwargs.keys():
+ kwargs["dauer"] = kwargs["dauer"].replace("Ja", "1").replace("Nein", "0")
+ query = "SELECT * FROM semesterapparat WHERE "
+ for key, value in kwargs.items() if kwargs.items() is not None else {}:
+ print(key, value)
+ query += f"{key}='{value}' AND "
+ print(query)
+ # remove deletesemester part from normal query, as this will be added to the database upon deleting the apparat
+ if "deletesemester" in kwargs.keys():
+ query = query.replace(
+ f"deletesemester='{kwargs['deletesemester']}' AND ", ""
+ )
+ if "endsemester" in kwargs.keys():
+ if "erstellsemester" in kwargs.keys():
+ query = query.replace(f"endsemester='{kwargs['endsemester']}' AND ", "")
+ query = query.replace(
+ f"erstellsemester='{kwargs['erstellsemester']} AND ", "xyz"
+ )
+ else:
+ query = query.replace(
+ f"endsemester='{kwargs['endsemester']}' AND ", "xyz"
+ )
+ print("replaced")
+ query = query.replace(
+ "xyz",
+ f"(erstellsemester='{kwargs['endsemester']}' OR verlängerung_bis='{kwargs['endsemester']}') AND ",
+ )
+ # remove all x="" parts from the query where x is a key in kwargs
+ query = query[:-5]
+ print(query)
+ return __query(query)
+
+ # Admin data
+ def getUser(self):
+ """Get a single user from the database"""
+ return self.query_db("SELECT * FROM user", one=True)
+ def getUsers(self)->list[tuple]:
+ """Return a list of tuples of all the users in the database"""
+ return self.query_db("SELECT * FROM user")
+
+ def login(self, user, hashed_password):
+ """try to login the user.
+ The salt for the user will be requested from the database and then added to the hashed password. The password will then be compared to the password in the database
+
+ Args:
+ user (str): username that tries to login
+ hashed_password (str): the password the user tries to login with
+
+ Returns:
+ bool: True if the login was successful, False if not
+ """
+ salt = self.query_db("SELECT salt FROM user WHERE username=?", (user,), one=True)[0]
+ if salt is None:
+ return False
+ hashed_password = salt + hashed_password
+ password = self.query_db("SELECT password FROM user WHERE username=?", (user,), one=True)[0]
+ if password == hashed_password:
+ return True
+ else:
+ return False
+ def changePassword(self, user, new_password):
+ """change the password of a user.
+ The password will be added with the salt and then committed to the database
+
+ Args:
+ user (str): username
+ new_password (str): the hashed password
+ """
+ salt = self.query_db("SELECT salt FROM user WHERE username=?", (user,), one=True)[0]
+ new_password = salt + new_password
+ self.query_db("UPDATE user SET password=? WHERE username=?", (new_password,user))
+ def getRole(self, user):
+ """get the role of the user
+
+ Args:
+ user (str): username
+
+ Returns:
+ str: the name of the role
+ """
+ return self.query_db("SELECT role FROM user WHERE username=?", (user,), one=True)[0]
+ def getRoles(self)->list[tuple]:
+ """get all the roles in the database
+
+ Returns:
+ list[str]: a list of all the roles
+ """
+ return self.query_db("SELECT role FROM user")
+ def checkUsername(self, user)->bool:
+ """a check to see if the username is already present in the database
+
+ Args:
+ user (str): the username
+
+ Returns:
+ bool: True if the username is present, False if not
+ """
+ data = self.query_db("SELECT username FROM user WHERE username=?", (user,), one=True)
+ return True if data is not None else False
+ def createUser(self, user, password, role, salt):
+ """create an user from the AdminCommands class.
+
+ Args:
+ user (str): the username of the user
+ password (str): a hashed password
+ role (str): the role of the user
+ salt (str): a salt for the password
+ """
+ self.query_db("INSERT OR IGNORE INTO user (username, password, role, salt) VALUES (?,?,?,?)", (user,password,role,salt))
+ def deleteUser(self, user):
+ """delete an unser
+
+ Args:
+ user (str): username of the user
+ """
+ self.query_db("DELETE FROM user WHERE username=?", (user,))
+ def updateUser(self, username, data:dict[str, str]):
+ """changge the data of a user
+
+ Args:
+ username (str): the username of the user
+ data (dict[str, str]): the data to be changed
+ """
+ conn = self.connect()
+ cursor = conn.cursor()
+ query = "UPDATE user SET "
+ for key,value in data.items():
+ if key == "username":
+ continue
+ query += f"{key}='{value}',"
+ query = query[:-1]
+ query += " WHERE username=?"
+ params = (username,)
+ cursor.execute(query, params)
+ conn.commit()
+ self.close_connection(conn)
+ def getFacultyMember(self, name:str)->tuple:
+ """get a faculty member based on the name
+
+ Args:
+ name (str): the name to be searched for
+
+ Returns:
+ tuple: a tuple containing the data of the faculty member
+ """
+ return self.query_db("SELECT titel, fname,lname,mail,telnr,fullname FROM prof WHERE fullname=?", (name,), one=True)
+ def updateFacultyMember(self, data:dict, oldlname:str, oldfname:str):
+ """update the data of a faculty member
+
+ Args:
+ data (dict): a dictionary containing the data to be updated
+ oldlname (str): the old last name of the faculty member
+ oldfname (str): the old first name of the faculty member
+ """
+ placeholders = ", ".join([f"{i}=:{i} " for i in data.keys()])
+ query = f"UPDATE prof SET {placeholders} WHERE lname = :oldlname AND fname = :oldfname"
+ data["oldlname"] = oldlname
+ data["oldfname"] = oldfname
+ self.query_db(query, data)
+ def getFacultyMembers(self):
+ """get a list of all faculty members
+
+ Returns:
+ list[tuple]: a list of tuples containing the faculty members
+ """
+ return self.query_db("SELECT titel, fname,lname,mail,telnr,fullname FROM prof")
+Classes
+-
+
+class Database +(db_path: str = None) +
+-
++
Initialize the database and create the tables if they do not exist.
+Default constructor for the database class
+Args
+-
+
db_path:str, optional
+- Optional Path for testing / specific purposes. Defaults to None. +
+++Expand source code +
+
+class Database: + """ + Initialize the database and create the tables if they do not exist. + """ + def __init__(self, db_path: str = None): + """ + Default constructor for the database class + + Args: + db_path (str, optional): Optional Path for testing / specific purposes. Defaults to None. + """ + if db_path is None: + self.db_path = config.database.path + config.database.name + self.db_path = self.db_path.replace("~", str(Path.home())) + else: + self.db_path = db_path + if self.get_db_contents() is None: + logger.log_critical("Database does not exist, creating tables") + self.create_tables() + def get_db_contents(self)->Union[List[Tuple], None]: + """ + Get the contents of the + + Returns: + Union[List[Tuple], None]: _description_ + """ + try: + with sql.connect(self.db_path) as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM sqlite_master WHERE type='table'") + return cursor.fetchall() + except sql.OperationalError: + return None + def connect(self)->sql.Connection: + """ + Connect to the database + + Returns: + sql.Connection: The active connection to the database + """ + return sql.connect(self.db_path) + def close_connection(self, conn: sql.Connection): + """ + closes the connection to the database + + Args: + ---- + - conn (sql.Connection): the connection to be closed + """ + conn.close() + def create_tables(self): + """ + Create the tables in the database + """ + conn = self.connect() + cursor = conn.cursor() + cursor.execute(CREATE_TABLE_APPARAT) + cursor.execute(CREATE_TABLE_MESSAGES) + cursor.execute(CREATE_TABLE_MEDIA) + cursor.execute(CREATE_TABLE_APPKONTOS) + cursor.execute(CREATE_TABLE_FILES) + cursor.execute(CREATE_TABLE_PROF) + cursor.execute(CREATE_TABLE_USER) + cursor.execute(CREATE_TABLE_SUBJECTS) + conn.commit() + self.close_connection(conn) + def insertInto(self, query:str, params:Tuple) -> None: + """ + Insert sent data into the database + + Args: + query (str): The query to be executed + params (Tuple): the parameters to be inserted into the database + """ + conn = self.connect() + cursor = conn.cursor() + logger.log_info(f"Inserting {params} into database with query {query}") + cursor.execute(query, params) + conn.commit() + self.close_connection(conn) + def query_db(self, query: str, args: Tuple = (), one: bool = False)->Union[Tuple, List[Tuple]]: + """ + Query the Database for the sent query. + + Args: + query (str): The query to be executed + args (Tuple, optional): The arguments for the query. Defaults to (). + one (bool, optional): Return the first result only. Defaults to False. + + Returns: + Union[Typle|List[Tuple]]: Returns the result of the query + """ + conn = self.connect() + cursor = conn.cursor() + logger.log_info(f"Querying database with query {query}, args: {args}") + cursor.execute(query, args) + rv = cursor.fetchall() + conn.commit() + self.close_connection(conn) + return (rv[0] if rv else None) if one else rv + + # Books + def addBookToDatabase(self, bookdata:BookData,app_id:Union[str,int], prof_id:Union[str,int]): + """ + Add books to the database. Both app_id and prof_id are required to add the book to the database, as the app_id and prof_id are used to select the books later on. + + Args: + bookdata (BookData): The metadata of the book to be added + 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. + """ + conn = self.connect() + cursor = conn.cursor() + t_query = ( + f"SELECT bookdata FROM media WHERE app_id={app_id} AND prof_id={prof_id}" + ) + # print(t_query) + result = cursor.execute(t_query).fetchall() + result = [load_pickle(i[0]) for i in result] + if bookdata in result: + print("Bookdata already in database") + # check if the book was deleted in the apparat + query = ( + "SELECT deleted FROM media WHERE app_id=? AND prof_id=? AND bookdata=?" + ) + params = (app_id, prof_id, dump_pickle(bookdata)) + result = cursor.execute(query, params).fetchone() + if result[0] == 1: + print("Book was deleted, updating bookdata") + query = "UPDATE media SET deleted=0 WHERE app_id=? AND prof_id=? AND bookdata=?" + params = (app_id, prof_id, dump_pickle(bookdata)) + cursor.execute(query, params) + conn.commit() + return + + query = ( + "INSERT INTO media (bookdata, app_id, prof_id,deleted) VALUES (?, ?, ?,?)" + ) + converted = dump_pickle(bookdata) + params = (converted, app_id, prof_id, 0) + cursor.execute(query, params) + conn.commit() + self.close_connection(conn) + def getBookIdBasedOnSignature(self, app_id:Union[str,int], prof_id:Union[str,int],signature:str)->int: + """ + Get a book id based on the signature of the book. + + Args: + app_id (str): The apparat id the book should be associated with + prof_id (str): The professor id the book should be associated with + signature (str): The signature of the book + + Returns: + int: The id of the book + """ + result = self.query_db("SELECT bookdata, id FROM media WHERE app_id=? AND prof_id=?", (app_id,prof_id)) + books = [(load_pickle(i[0]),i[1]) for i in result] + book = [i for i in books if i[0].signature == signature][0][1] + return book + def getBookBasedOnSignature(self, app_id:Union[str,int], prof_id:Union[str,int],signature:str)->BookData: + """ + Get the book based on the signature of the book. + + Args: + app_id (str): The apparat id the book should be associated with + prof_id (str): The professor id the book should be associated with + signature (str): The signature of the book + + Returns: + BookData: The total metadata of the book wrapped in a BookData object + """ + result = self.query_db("SELECT bookdata FROM media WHERE app_id=? AND prof_id=?", (app_id,prof_id)) + books = [load_pickle(i[0]) for i in result] + book = [i for i in books if i.signature == signature][0] + return book + def getLastBookId(self)->int: + """ + Get the last book id in the database + + Returns: + int: ID of the last book in the database + """ + return self.query_db("SELECT id FROM media ORDER BY id DESC", one=True)[0] + def searchBook(self, data:dict[str, str])->list[tuple[BookData, int]]: + """ + Search a book in the database based on the sent data. + + Args: + data (dict[str, str]): A dictionary containing the data to be searched for. The dictionary can contain the following: + - signature: The signature of the book + - title: The title of the book + + Returns: + list[tuple[BookData, int]]: A list of tuples containing the wrapped Metadata and the id of the book + """ + rdata = self.query_db("SELECT * FROM media WHERE deleted=0") + ic(rdata, len(rdata)) + mode = 0 + if len(data)== 1: + if "signature" in data.keys(): + mode = 1 + elif "title" in data.keys(): + mode = 2 + elif len(data) == 2: + mode = 3 + else: + return None + ret = [] + for book in rdata: + bookdata = load_pickle(book[1]) + app_id = book[2] + prof_id = book[3] + if mode == 1: + if data["signature"] in bookdata.signature: + ret.append((bookdata,app_id,prof_id)) + elif mode == 2: + if data["title"] in bookdata.title: + ret.append((bookdata,app_id,prof_id)) + elif mode == 3: + if data["signature"] in bookdata.signature and data["title"] in bookdata.title: + ret.append((bookdata,app_id,prof_id)) + ic(ret) + return ret + def setAvailability(self, book_id:str, available:str): + """ + Set the availability of a book in the database + + Args: + book_id (str): The id of the book + available (str): The availability of the book + """ + self.query_db("UPDATE media SET available=? WHERE id=?", (available,book_id)) + def getBookId(self, bookdata:BookData, app_id:Union[str,int], prof_id:Union[str,int])->int: + """ + Get the id of a book based on the metadata of the book + + Args: + bookdata (BookData): The wrapped metadata of the book + app_id (str): The apparat id the book should be associated with + prof_id (str): The professor id the book should be associated with + + Returns: + int: ID of the book + """ + result = self.query_db("SELECT id FROM media WHERE bookdata=? AND app_id=? AND prof_id=?", (dump_pickle(bookdata),app_id,prof_id), one=True) + return result[0] + def getBook(self,book_id:int)->BookData: + """ + Get the book based on the id in the database + + Args: + book_id (int): The id of the book + + Returns: + BookData: The metadata of the book wrapped in a BookData object + """ + return load_pickle(self.query_db("SELECT bookdata FROM media WHERE id=?", (book_id,), one=True)[0]) + def getBooks(self, app_id:Union[str,int], prof_id:Union[str,int], deleted=0)->list[dict[int, BookData, int]]: + """ + Get the Books based on the apparat id and the professor id + + Args: + app_id (str): The ID of the apparat + prof_id (str): The ID of the professor + deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0. + + Returns: + list[dict[int, BookData, int]]: A list of dictionaries containing the id, the metadata of the book and the availability of the book + """ + qdata = self.query_db(f"SELECT id,bookdata,available FROM media WHERE (app_id={app_id} AND prof_id={prof_id}) AND (deleted={deleted if deleted == 0 else '1 OR deleted=0'})") + ret_result = [] + for result_a in qdata: + data = {"id": int, "bookdata": BookData, "available": int} + data["id"] = result_a[0] + data["bookdata"] = load_pickle(result_a[1]) + data["available"] = result_a[2] + ret_result.append(data) + return ret_result + def updateBookdata(self, book_id, bookdata:BookData): + """ + Update the bookdata in the database + + Args: + book_id (str): The id of the book + bookdata (BookData): The new metadata of the book + """ + self.query_db("UPDATE media SET bookdata=? WHERE id=?", (dump_pickle(bookdata),book_id)) + def deleteBook(self, book_id): + """ + Delete a book from the database + + Args: + book_id (str): ID of the book + """ + self.query_db("UPDATE media SET deleted=1 WHERE id=?", (book_id,)) + + # File Interactions + def getBlob(self, filename, app_id:Union[str,int]): + """ + Get a blob from the database + + Args: + filename (str): The name of the file + app_id (str): ID of the apparat + + Returns: + bytes: The file stored in + """ + return self.query_db("SELECT fileblob FROM files WHERE filename=? AND app_id=?", (filename,app_id), one=True)[0] + def insertFile(self, file: list[dict], app_id:Union[str,int], prof_id:Union[str,int]): + """Instert a list of files into the database + + Args: + file (list[dict]): a list containing all the files to be inserted + Structured: [{"name": "filename", "path": "path", "type": "filetype"}] + app_id (int): the id of the apparat + prof_id (str): the id of the professor + """ + for f in file: + filename = f["name"] + path = f["path"] + filetyp = f["type"] + if path == "Database": + continue + blob = create_blob(path) + query = "INSERT OR IGNORE INTO files (filename, fileblob, app_id, filetyp,prof_id) VALUES (?, ?, ?, ?,?)" + self.query_db(query, (filename, blob, app_id, filetyp,prof_id)) + def recreateFile(self, filename:str, app_id:Union[str,int],filetype:str)->str: + """Recreate a file from the database + + Args: + filename (str): the name of the file + app_id (Union[str,int]): the id of the apparat + filetype (str): the extension of the file to be created + + Returns: + str: The filename of the recreated file + """ + blob = self.getBlob(filename, app_id) + tempdir = config.database.tempdir + tempdir = tempdir.replace("~", str(Path.home())) + tempdir_path = Path(tempdir) + if not os.path.exists(tempdir_path): + os.mkdir(tempdir_path) + file = tempfile.NamedTemporaryFile( + delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}" + ) + file.write(blob) + print("file created") + return file.name + def getFiles(self, app_id:Union[str,int], prof_id:int)->list[tuple]: + """Get all the files associated with the apparat and the professor + + Args: + app_id (Union[str,int]): The id of the apparat + prof_id (Union[str,int]): the id of the professor + + Returns: + list[tuple]: a list of tuples containing the filename and the filetype for the corresponding apparat and professor + """ + return self.query_db("SELECT filename, filetyp FROM files WHERE app_id=? AND prof_id=?", (app_id,prof_id)) + + def getSemersters(self)->list[str]: + """Return all the unique semesters in the database + + Returns: + list: a list of strings containing the semesters + """ + data = self.query_db("SELECT DISTINCT erstellsemester FROM semesterapparat") + return [i[0] for i in data] + + def getSubjects(self): + """Get all the subjects in the database + + Returns: + list[tuple]: a list of tuples containing the subjects + """ + return self.query_db("SELECT * FROM subjects") + + # Messages + def addMessage(self, message:dict,user:str, app_id:Union[str,int]): + """add a Message to the database + + Args: + message (dict): the message to be added + user (str): the user who added the message + app_id (Union[str,int]): the id of the apparat + """ + def __getUserId(user): + return self.query_db("SELECT id FROM user WHERE username=?", (user,), one=True)[0] + user_id = __getUserId(user) + self.query_db("INSERT INTO messages (message, user_id, remind_at,appnr) VALUES (?,?,?,?)", (message["message"],user_id,message["remind_at"],app_id)) + def getMessages(self, date:str)->list[dict[str, str, str, str]]: + """Get all the messages for a specific date + + Args: + date (str): a date.datetime object formatted as a string in the format "YYYY-MM-DD" + + Returns: + list[dict[str, str, str, str]]: a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message + """ + def __get_user_name(user_id): + return self.query_db("SELECT username FROM user WHERE id=?", (user_id,), one=True)[0] + messages = self.query_db("SELECT * FROM messages WHERE remind_at=?", (date,)) + ret = [ + { + "message": i[2], + "user": __get_user_name(i[4]), + "appnr": i[5], + "id": i[0] + } + for i in messages + ] + return ret + def deleteMessage(self, message_id): + """Delete a message from the database + + Args: + message_id (str): the id of the message + """ + self.query_db("DELETE FROM messages WHERE id=?", (message_id,)) + + # Prof data + def getProfNameById(self, prof_id:Union[str,int],add_title:bool=False)->str: + """Get a professor name based on the id + + Args: + prof_id (Union[str,int]): The id of the professor + add_title (bool, optional): wether to add the title or no. Defaults to False. + + Returns: + str: The name of the professor + """ + prof = self.query_db("SELECT fullname FROM prof WHERE id=?", (prof_id,), one=True) + if add_title: + return f"{self.getTitleById(prof_id)}{prof[0]}" + else: + return prof[0] + def getTitleById(self, prof_id:Union[str,int])->str: + """get the title of a professor based on the id + + Args: + prof_id (Union[str,int]): the id of the professor + + Returns: + str: the title of the professor, with an added whitespace at the end, if no title is present, an empty string is returned + """ + title = self.query_db("SELECT titel FROM prof WHERE id=?", (prof_id,), one=True)[0] + return f"{title} " if title is not None else "" + def getProfByName(self, prof_name:str)->tuple: + """get all the data of a professor based on the name + + Args: + prof_name (str): the name of the professor + + Returns: + tuple: the data of the professor + """ + return self.query_db("SELECT * FROM prof WHERE fullname=?", (prof_name,), one=True) + def getProfId(self, prof_name:str)->Optional[int]: + """Get the id of a professor based on the name + + Args: + prof_name (str): the name of the professor + + Returns: + Optional[int]: the id of the professor, if the professor is not found, None is returned + """ + + data = self.getProfByName(prof_name.replace(",", "")) + if data is None: + return None + else: + return data[0] + def getSpecificProfData(self, prof_id:Union[str,int], fields:List[str])->tuple: + """A customisable function to get specific data of a professor based on the id + + Args: + prof_id (Union[str,int]): the id of the professor + fields (List[str]): a list of fields to be returned + + Returns: + tuple: a tuple containing the requested data + """ + query = "SELECT " + for field in fields: + query += f"{field}," + query = query[:-1] + query += " FROM prof WHERE id=?" + return self.query_db(query, (prof_id,), one=True)[0] + def getProfData(self, profname:str): + """Get mail, telephone number and title of a professor based on the name + + Args: + profname (str): name of the professor + + Returns: + tuple: the mail, telephone number and title of the professor + """ + data = self.query_db("SELECT mail, telnr, titel FROM prof WHERE fullname=?", (profname.replace(",",""),), one=True) + return data + def createProf(self, prof_details:dict): + """Create a professor in the database + + Args: + prof_details (dict): a dictionary containing the details of the professor + """ + prof_title = prof_details["prof_title"] + prof_fname = prof_details["profname"].split(",")[1] + prof_fname = prof_fname.strip() + prof_lname = prof_details["profname"].split(",")[0] + prof_lname = prof_lname.strip() + prof_fullname = prof_details["profname"].replace(",", "") + prof_mail = prof_details["prof_mail"] + prof_tel = prof_details["prof_tel"] + params = (prof_title, prof_fname, prof_lname, prof_mail, prof_tel, prof_fullname) + query = "INSERT OR IGNORE INTO prof (titel, fname, lname, mail, telnr, fullname) VALUES (?, ?, ?, ?, ?, ?)" + self.insertInto(query=query, params=params) + def getProfs(self)->list[tuple]: + """Return all the professors in the database + + Returns: + list[tuple]: a list containing all the professors in individual tuples + """ + return self.query_db("SELECT * FROM prof") + + # Apparat + def getAllAparats(self,deleted=0)->list[tuple]: + """Get all the apparats in the database + + Args: + deleted (int, optional): Switch the result to use . Defaults to 0. + + Returns: + list[tuple]: a list of tuples containing the apparats + """ + return self.query_db("SELECT * FROM semesterapparat WHERE deletion_status=?", (deleted,)) + def getApparatData(self, appnr, appname)->ApparatData: + """Get the Apparat data based on the apparat number and the name + + Args: + appnr (str): the apparat number + appname (str): the name of the apparat + + Raises: + NoResultError: an error is raised if no result is found + + Returns: + ApparatData: the appended data of the apparat wrapped in an ApparatData object + """ + result = self.query_db("SELECT * FROM semesterapparat WHERE appnr=? AND name=?", (appnr,appname), one=True) + if result is None: + raise NoResultError("No result found") + apparat = ApparatData() + apparat.appname = result[1] + apparat.appnr = result[4] + 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.app_fach = result[3] + apparat.erstellsemester = result[5] + apparat.semester = result[8] + apparat.deleted = result[9] + apparat.apparat_adis_id = result[11] + apparat.prof_adis_id = result[12] + return apparat + def getUnavailableApparatNumbers(self)->List[int]: + """Get a list of all the apparat numbers in the database that are currently in use + + Returns: + List[int]: the list of used apparat numbers + """ + numbers = self.query_db("SELECT appnr FROM semesterapparat WHERE deletion_status=0") + numbers = [i[0] for i in numbers] + logger.log_info(f"Currently used apparat numbers: {numbers}") + return numbers + def setNewSemesterDate(self, app_id:Union[str,int], newDate, dauerapp=False): + """Set the new semester date for an apparat + + Args: + app_id (Union[str,int]): the id of the apparat + newDate (str): the new date + dauerapp (bool, optional): if the apparat was changed to dauerapparat. Defaults to False. + """ + date = datetime.datetime.strptime(newDate, "%d.%m.%Y").strftime("%Y-%m-%d") + if dauerapp: + self.query_db("UPDATE semesterapparat SET verlängerung_bis=?, dauerapp=? WHERE appnr=?", (date,dauerapp,app_id)) + else: + self.query_db("UPDATE semesterapparat SET endsemester=? WHERE appnr=?", (date,app_id)) + def getApparatId(self, apparat_name)->Optional[int]: + """get the id of an apparat based on the name + + Args: + apparat_name (str): the name of the apparat e.g. "Semesterapparat 1" + + Returns: + Optional[int]: the id of the apparat, if the apparat is not found, None is returned + """ + data = self.query_db("SELECT appnr FROM semesterapparat WHERE name=?", (apparat_name,), one=True) + if data is None: + return None + else: + return data[0] + def createApparat(self, apparat:ApparatData)->int: + """create the apparat in the database + + Args: + apparat (ApparatData): the wrapped metadata of the apparat + + Raises: + AppPresentError: an error describing that the apparats chosen id is already present in the database + + Returns: + Optional[int]: the id of the apparat + """ + + prof_id = self.getProfId(apparat.profname) + app_id = self.getApparatId(apparat.appname) + if app_id: + raise AppPresentError(app_id) + + self.createProf(apparat.get_prof_details()) + prof_id = 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]}')" + logger.log_info(query) + self.query_db(query) + return self.getApparatId(apparat.appname) + def getApparatsByProf(self, prof_id:Union[str,int])->list[tuple]: + """Get all apparats based on the professor id + + Args: + prof_id (Union[str,int]): the id of the professor + + Returns: + list[tuple]: a list of tuples containing the apparats + """ + return self.query_db("SELECT * FROM semesterapparat WHERE prof_id=?", (prof_id,)) + def getApparatsBySemester(self, semester:str)->dict[list]: + """get all apparats based on the semester + + Args: + semester (str): the selected semester + + Returns: + dict[list]: a list off all created and deleted apparats for the selected semester + """ + data = self.query_db("SELECT name, prof_id FROM semesterapparat WHERE erstellsemester=?", (semester,)) + conn = self.connect() + cursor = conn.cursor() + c_tmp = [] + for i in data: + c_tmp.append((i[0], self.getProfNameById(i[1]))) + query = ( + f"SELECT name,prof_id FROM semesterapparat WHERE deleted_date='{semester}'" + ) + result = cursor.execute(query).fetchall() + d_tmp = [] + for i in result: + d_tmp.append((i[0], self.getProfNameById(i[1]))) + # group the apparats by prof + c_ret = {} + for i in c_tmp: + if i[1] not in c_ret.keys(): + c_ret[i[1]] = [i[0]] + else: + c_ret[i[1]].append(i[0]) + d_ret = {} + for i in d_tmp: + if i[1] not in d_ret.keys(): + d_ret[i[1]] = [i[0]] + else: + d_ret[i[1]].append(i[0]) + self.close_connection(conn) + return {"created": c_ret, "deleted": d_ret} + def getApparatCountBySemester(self)->tuple[list[str],list[int]]: + """get a list of all apparats created and deleted by semester + + Returns: + tuple[list[str],list[int]]: a tuple containing two lists, the first list contains the semesters, the second list contains the amount of apparats created and deleted for the corresponding semester + """ + conn = self.connect() + cursor = conn.cursor() + semesters = self.getSemersters() + created = [] + deleted = [] + for semester in semesters: + query = f"SELECT COUNT(*) FROM semesterapparat WHERE erstellsemester='{semester}'" + result = cursor.execute(query).fetchone() + created.append(result[0]) + query = f"SELECT COUNT(*) FROM semesterapparat WHERE deletion_status=1 AND deleted_date='{semester}'" + result = cursor.execute(query).fetchone() + deleted.append(result[0]) + # store data in a tuple + ret = [] + e_tuple = () + for sem in semesters: + e_tuple = ( + sem, + created[semesters.index(sem)], + deleted[semesters.index(sem)], + ) + ret.append(e_tuple) + self.close_connection(conn) + return ret + def deleteApparat(self, app_id:Union[str,int], semester:str): + """Delete an apparat from the database + + Args: + app_id (Union[str, int]): the id of the apparat + semester (str): the semester the apparat should be deleted from + """ + self.query_db("UPDATE semesterapparat SET deletion_status=1, deleted_date=? WHERE appnr=?", (semester,app_id)) + def isEternal(self, id): + """check if the apparat is eternal (dauerapparat) + + Args: + id (int): the id of the apparat to be checked + + Returns: + int: the state of the apparat + """ + return self.query_db("SELECT dauer FROM semesterapparat WHERE appnr=?", (id,), one=True) + def getApparatName(self, app_id:Union[str,int], prof_id:Union[str,int]): + """get the name of the apparat based on the id + + Args: + app_id (Union[str,int]): the id of the apparat + prof_id (Union[str,int]): the id of the professor + + Returns: + str: the name of the apparat + """ + return self.query_db("SELECT name FROM semesterapparat WHERE appnr=? AND prof_id=?", (app_id,prof_id), one=True)[0] + def updateApparat(self, apparat_data:ApparatData): + """Update an apparat in the database + + Args: + apparat_data (ApparatData): the new metadata of the apparat + """ + query = f"UPDATE semesterapparat SET name = ?, fach = ?, dauer = ?, prof_id = ? WHERE appnr = ?" + params = ( + apparat_data.appname, + apparat_data.app_fach, + apparat_data.dauerapp, + self.getProfId(apparat_data.profname), + apparat_data.appnr, + ) + self.query_db(query, params) + def checkApparatExists(self, apparat_name:str): + """check if the apparat is already present in the database based on the name + + Args: + apparat_name (str): the name of the apparat + + Returns: + bool: True if the apparat is present, False if not + """ + return True if self.query_db("SELECT appnr FROM semesterapparat WHERE name=?", (apparat_name,), one=True) else False + def checkApparatExistsById(self, app_id:Union[str,int])->bool: + """a check to see if the apparat is already present in the database, based on the id + + Args: + app_id (Union[str, int]): the id of the apparat + + Returns: + bool: True if the apparat is present, False if not + """ + return True if self.query_db("SELECT appnr FROM semesterapparat WHERE appnr=?", (app_id,), one=True) else False + # Statistics + def statistic_request(self, **kwargs: Any): + """Take n amount of kwargs and return the result of the query + """ + def __query(query): + """execute the query and return the result + + Args: + query (str): the constructed query + + Returns: + list: the result of the query + """ + conn = self.connect() + cursor = conn.cursor() + result = cursor.execute(query).fetchall() + for result_a in result: + orig_value = result_a + prof_name = self.getProfNameById(result_a[2]) + # replace the prof_id with the prof_name + result_a = list(result_a) + result_a[2] = prof_name + result_a = tuple(result_a) + result[result.index(orig_value)] = result_a + self.close_connection(conn) + return result + if "deletable" in kwargs.keys(): + query = f"SELECT * FROM semesterapparat WHERE deletion_status=0 AND dauer=0 AND (erstellsemester!='{kwargs['deletesemester']}' OR verlängerung_bis!='{kwargs['deletesemester']}')" + return __query(query) + if "dauer" in kwargs.keys(): + kwargs["dauer"] = kwargs["dauer"].replace("Ja", "1").replace("Nein", "0") + query = "SELECT * FROM semesterapparat WHERE " + for key, value in kwargs.items() if kwargs.items() is not None else {}: + print(key, value) + query += f"{key}='{value}' AND " + print(query) + # remove deletesemester part from normal query, as this will be added to the database upon deleting the apparat + if "deletesemester" in kwargs.keys(): + query = query.replace( + f"deletesemester='{kwargs['deletesemester']}' AND ", "" + ) + if "endsemester" in kwargs.keys(): + if "erstellsemester" in kwargs.keys(): + query = query.replace(f"endsemester='{kwargs['endsemester']}' AND ", "") + query = query.replace( + f"erstellsemester='{kwargs['erstellsemester']} AND ", "xyz" + ) + else: + query = query.replace( + f"endsemester='{kwargs['endsemester']}' AND ", "xyz" + ) + print("replaced") + query = query.replace( + "xyz", + f"(erstellsemester='{kwargs['endsemester']}' OR verlängerung_bis='{kwargs['endsemester']}') AND ", + ) + # remove all x="" parts from the query where x is a key in kwargs + query = query[:-5] + print(query) + return __query(query) + + # Admin data + def getUser(self): + """Get a single user from the database""" + return self.query_db("SELECT * FROM user", one=True) + def getUsers(self)->list[tuple]: + """Return a list of tuples of all the users in the database""" + return self.query_db("SELECT * FROM user") + + def login(self, user, hashed_password): + """try to login the user. + The salt for the user will be requested from the database and then added to the hashed password. The password will then be compared to the password in the database + + Args: + user (str): username that tries to login + hashed_password (str): the password the user tries to login with + + Returns: + bool: True if the login was successful, False if not + """ + salt = self.query_db("SELECT salt FROM user WHERE username=?", (user,), one=True)[0] + if salt is None: + return False + hashed_password = salt + hashed_password + password = self.query_db("SELECT password FROM user WHERE username=?", (user,), one=True)[0] + if password == hashed_password: + return True + else: + return False + def changePassword(self, user, new_password): + """change the password of a user. + The password will be added with the salt and then committed to the database + + Args: + user (str): username + new_password (str): the hashed password + """ + salt = self.query_db("SELECT salt FROM user WHERE username=?", (user,), one=True)[0] + new_password = salt + new_password + self.query_db("UPDATE user SET password=? WHERE username=?", (new_password,user)) + def getRole(self, user): + """get the role of the user + + Args: + user (str): username + + Returns: + str: the name of the role + """ + return self.query_db("SELECT role FROM user WHERE username=?", (user,), one=True)[0] + def getRoles(self)->list[tuple]: + """get all the roles in the database + + Returns: + list[str]: a list of all the roles + """ + return self.query_db("SELECT role FROM user") + def checkUsername(self, user)->bool: + """a check to see if the username is already present in the database + + Args: + user (str): the username + + Returns: + bool: True if the username is present, False if not + """ + data = self.query_db("SELECT username FROM user WHERE username=?", (user,), one=True) + return True if data is not None else False + def createUser(self, user, password, role, salt): + """create an user from the AdminCommands class. + + Args: + user (str): the username of the user + password (str): a hashed password + role (str): the role of the user + salt (str): a salt for the password + """ + self.query_db("INSERT OR IGNORE INTO user (username, password, role, salt) VALUES (?,?,?,?)", (user,password,role,salt)) + def deleteUser(self, user): + """delete an unser + + Args: + user (str): username of the user + """ + self.query_db("DELETE FROM user WHERE username=?", (user,)) + def updateUser(self, username, data:dict[str, str]): + """changge the data of a user + + Args: + username (str): the username of the user + data (dict[str, str]): the data to be changed + """ + conn = self.connect() + cursor = conn.cursor() + query = "UPDATE user SET " + for key,value in data.items(): + if key == "username": + continue + query += f"{key}='{value}'," + query = query[:-1] + query += " WHERE username=?" + params = (username,) + cursor.execute(query, params) + conn.commit() + self.close_connection(conn) + def getFacultyMember(self, name:str)->tuple: + """get a faculty member based on the name + + Args: + name (str): the name to be searched for + + Returns: + tuple: a tuple containing the data of the faculty member + """ + return self.query_db("SELECT titel, fname,lname,mail,telnr,fullname FROM prof WHERE fullname=?", (name,), one=True) + def updateFacultyMember(self, data:dict, oldlname:str, oldfname:str): + """update the data of a faculty member + + Args: + data (dict): a dictionary containing the data to be updated + oldlname (str): the old last name of the faculty member + oldfname (str): the old first name of the faculty member + """ + placeholders = ", ".join([f"{i}=:{i} " for i in data.keys()]) + query = f"UPDATE prof SET {placeholders} WHERE lname = :oldlname AND fname = :oldfname" + data["oldlname"] = oldlname + data["oldfname"] = oldfname + self.query_db(query, data) + def getFacultyMembers(self): + """get a list of all faculty members + + Returns: + list[tuple]: a list of tuples containing the faculty members + """ + return self.query_db("SELECT titel, fname,lname,mail,telnr,fullname FROM prof")Methods
+-
+
+def addBookToDatabase(self, bookdata: src.logic.dataclass.BookData, app_id: Union[str, int], prof_id: Union[str, int]) +
+-
++
Add books to the database. Both app_id and prof_id are required to add the book to the database, as the app_id and prof_id are used to select the books later on.
+Args
+-
+
bookdata:BookData
+- The metadata of the book to be added +
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. +
+++Expand source code +
+
+def addBookToDatabase(self, bookdata:BookData,app_id:Union[str,int], prof_id:Union[str,int]): + """ + Add books to the database. Both app_id and prof_id are required to add the book to the database, as the app_id and prof_id are used to select the books later on. + + Args: + bookdata (BookData): The metadata of the book to be added + 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. + """ + conn = self.connect() + cursor = conn.cursor() + t_query = ( + f"SELECT bookdata FROM media WHERE app_id={app_id} AND prof_id={prof_id}" + ) + # print(t_query) + result = cursor.execute(t_query).fetchall() + result = [load_pickle(i[0]) for i in result] + if bookdata in result: + print("Bookdata already in database") + # check if the book was deleted in the apparat + query = ( + "SELECT deleted FROM media WHERE app_id=? AND prof_id=? AND bookdata=?" + ) + params = (app_id, prof_id, dump_pickle(bookdata)) + result = cursor.execute(query, params).fetchone() + if result[0] == 1: + print("Book was deleted, updating bookdata") + query = "UPDATE media SET deleted=0 WHERE app_id=? AND prof_id=? AND bookdata=?" + params = (app_id, prof_id, dump_pickle(bookdata)) + cursor.execute(query, params) + conn.commit() + return + + query = ( + "INSERT INTO media (bookdata, app_id, prof_id,deleted) VALUES (?, ?, ?,?)" + ) + converted = dump_pickle(bookdata) + params = (converted, app_id, prof_id, 0) + cursor.execute(query, params) + conn.commit() + self.close_connection(conn)
+ +def addMessage(self, message: dict, user: str, app_id: Union[str, int]) +
+-
++
add a Message to the database
+Args
+-
+
message:dict
+- the message to be added +
user:str
+- the user who added the message +
app_id:Union[str,int]
+- the id of the apparat +
+++Expand source code +
+
+def addMessage(self, message:dict,user:str, app_id:Union[str,int]): + """add a Message to the database + + Args: + message (dict): the message to be added + user (str): the user who added the message + app_id (Union[str,int]): the id of the apparat + """ + def __getUserId(user): + return self.query_db("SELECT id FROM user WHERE username=?", (user,), one=True)[0] + user_id = __getUserId(user) + self.query_db("INSERT INTO messages (message, user_id, remind_at,appnr) VALUES (?,?,?,?)", (message["message"],user_id,message["remind_at"],app_id))
+ +def changePassword(self, user, new_password) +
+-
++
change the password of a user. +The password will be added with the salt and then committed to the database
+Args
+-
+
user:str
+- username +
new_password:str
+- the hashed password +
+++Expand source code +
+
+def changePassword(self, user, new_password): + """change the password of a user. + The password will be added with the salt and then committed to the database + + Args: + user (str): username + new_password (str): the hashed password + """ + salt = self.query_db("SELECT salt FROM user WHERE username=?", (user,), one=True)[0] + new_password = salt + new_password + self.query_db("UPDATE user SET password=? WHERE username=?", (new_password,user))
+ +def checkApparatExists(self, apparat_name: str) +
+-
++
check if the apparat is already present in the database based on the name
+Args
+-
+
apparat_name:str
+- the name of the apparat +
Returns
+-
+
bool
+- True if the apparat is present, False if not +
+++Expand source code +
+
+def checkApparatExists(self, apparat_name:str): + """check if the apparat is already present in the database based on the name + + Args: + apparat_name (str): the name of the apparat + + Returns: + bool: True if the apparat is present, False if not + """ + return True if self.query_db("SELECT appnr FROM semesterapparat WHERE name=?", (apparat_name,), one=True) else False
+ +def checkApparatExistsById(self, app_id: Union[str, int]) ‑> bool +
+-
++
a check to see if the apparat is already present in the database, based on the id
+Args
+-
+
app_id:Union[str, int]
+- the id of the apparat +
Returns
+-
+
bool
+- True if the apparat is present, False if not +
+++Expand source code +
+
+def checkApparatExistsById(self, app_id:Union[str,int])->bool: + """a check to see if the apparat is already present in the database, based on the id + + Args: + app_id (Union[str, int]): the id of the apparat + + Returns: + bool: True if the apparat is present, False if not + """ + return True if self.query_db("SELECT appnr FROM semesterapparat WHERE appnr=?", (app_id,), one=True) else False
+ +def checkUsername(self, user) ‑> bool +
+-
++
a check to see if the username is already present in the database
+Args
+-
+
user:str
+- the username +
Returns
+-
+
bool
+- True if the username is present, False if not +
+++Expand source code +
+
+def checkUsername(self, user)->bool: + """a check to see if the username is already present in the database + + Args: + user (str): the username + + Returns: + bool: True if the username is present, False if not + """ + data = self.query_db("SELECT username FROM user WHERE username=?", (user,), one=True) + return True if data is not None else False
+ +def close_connection(self, conn: sqlite3.Connection) +
+-
++
closes the connection to the database
+Args:
+- conn (sql.Connection): the connection to be closed ++++Expand source code +
+
+def close_connection(self, conn: sql.Connection): + """ + closes the connection to the database + + Args: + ---- + - conn (sql.Connection): the connection to be closed + """ + conn.close()
+ +def connect(self) ‑> sqlite3.Connection +
+-
++
Connect to the database
+Returns
+-
+
sql.Connection
+- The active connection to the database +
+++Expand source code +
+
+def connect(self)->sql.Connection: + """ + Connect to the database + + Returns: + sql.Connection: The active connection to the database + """ + return sql.connect(self.db_path)
+ +def createApparat(self, apparat: src.logic.dataclass.ApparatData) ‑> int +
+-
++
create the apparat in the database
+Args
+-
+
apparat:ApparatData
+- the wrapped metadata of the apparat +
Raises
+-
+
AppPresentError
+- an error describing that the apparats chosen id is already present in the database +
Returns
+-
+
Optional[int]
+- the id of the apparat +
+++Expand source code +
+
+def createApparat(self, apparat:ApparatData)->int: + """create the apparat in the database + + Args: + apparat (ApparatData): the wrapped metadata of the apparat + + Raises: + AppPresentError: an error describing that the apparats chosen id is already present in the database + + Returns: + Optional[int]: the id of the apparat + """ + + prof_id = self.getProfId(apparat.profname) + app_id = self.getApparatId(apparat.appname) + if app_id: + raise AppPresentError(app_id) + + self.createProf(apparat.get_prof_details()) + prof_id = 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]}')" + logger.log_info(query) + self.query_db(query) + return self.getApparatId(apparat.appname)
+ +def createProf(self, prof_details: dict) +
+-
++
Create a professor in the database
+Args
+-
+
prof_details:dict
+- a dictionary containing the details of the professor +
+++Expand source code +
+
+def createProf(self, prof_details:dict): + """Create a professor in the database + + Args: + prof_details (dict): a dictionary containing the details of the professor + """ + prof_title = prof_details["prof_title"] + prof_fname = prof_details["profname"].split(",")[1] + prof_fname = prof_fname.strip() + prof_lname = prof_details["profname"].split(",")[0] + prof_lname = prof_lname.strip() + prof_fullname = prof_details["profname"].replace(",", "") + prof_mail = prof_details["prof_mail"] + prof_tel = prof_details["prof_tel"] + params = (prof_title, prof_fname, prof_lname, prof_mail, prof_tel, prof_fullname) + query = "INSERT OR IGNORE INTO prof (titel, fname, lname, mail, telnr, fullname) VALUES (?, ?, ?, ?, ?, ?)" + self.insertInto(query=query, params=params)
+ +def createUser(self, user, password, role, salt) +
+-
++
create an user from the AdminCommands class.
+Args
+-
+
user:str
+- the username of the user +
password:str
+- a hashed password +
role:str
+- the role of the user +
salt:str
+- a salt for the password +
+++Expand source code +
+
+def createUser(self, user, password, role, salt): + """create an user from the AdminCommands class. + + Args: + user (str): the username of the user + password (str): a hashed password + role (str): the role of the user + salt (str): a salt for the password + """ + self.query_db("INSERT OR IGNORE INTO user (username, password, role, salt) VALUES (?,?,?,?)", (user,password,role,salt))
+ +def create_tables(self) +
+-
++
Create the tables in the database
+++Expand source code +
+
+def create_tables(self): + """ + Create the tables in the database + """ + conn = self.connect() + cursor = conn.cursor() + cursor.execute(CREATE_TABLE_APPARAT) + cursor.execute(CREATE_TABLE_MESSAGES) + cursor.execute(CREATE_TABLE_MEDIA) + cursor.execute(CREATE_TABLE_APPKONTOS) + cursor.execute(CREATE_TABLE_FILES) + cursor.execute(CREATE_TABLE_PROF) + cursor.execute(CREATE_TABLE_USER) + cursor.execute(CREATE_TABLE_SUBJECTS) + conn.commit() + self.close_connection(conn)
+ +def deleteApparat(self, app_id: Union[str, int], semester: str) +
+-
++
Delete an apparat from the database
+Args
+-
+
app_id:Union[str, int]
+- the id of the apparat +
semester:str
+- the semester the apparat should be deleted from +
+++Expand source code +
+
+def deleteApparat(self, app_id:Union[str,int], semester:str): + """Delete an apparat from the database + + Args: + app_id (Union[str, int]): the id of the apparat + semester (str): the semester the apparat should be deleted from + """ + self.query_db("UPDATE semesterapparat SET deletion_status=1, deleted_date=? WHERE appnr=?", (semester,app_id))
+ +def deleteBook(self, book_id) +
+-
++
Delete a book from the database
+Args
+-
+
book_id:str
+- ID of the book +
+++Expand source code +
+
+def deleteBook(self, book_id): + """ + Delete a book from the database + + Args: + book_id (str): ID of the book + """ + self.query_db("UPDATE media SET deleted=1 WHERE id=?", (book_id,))
+ +def deleteMessage(self, message_id) +
+-
++
Delete a message from the database
+Args
+-
+
message_id:str
+- the id of the message +
+++Expand source code +
+
+def deleteMessage(self, message_id): + """Delete a message from the database + + Args: + message_id (str): the id of the message + """ + self.query_db("DELETE FROM messages WHERE id=?", (message_id,))
+ +def deleteUser(self, user) +
+-
++
delete an unser
+Args
+-
+
user:str
+- username of the user +
+++Expand source code +
+
+def deleteUser(self, user): + """delete an unser + + Args: + user (str): username of the user + """ + self.query_db("DELETE FROM user WHERE username=?", (user,))
+ +def getAllAparats(self, deleted=0) ‑> list[tuple] +
+-
++
Get all the apparats in the database
+Args
+-
+
deleted:int, optional
+- Switch the result to use . Defaults to 0. +
Returns
+-
+
list[tuple]
+- a list of tuples containing the apparats +
+++Expand source code +
+
+def getAllAparats(self,deleted=0)->list[tuple]: + """Get all the apparats in the database + + Args: + deleted (int, optional): Switch the result to use . Defaults to 0. + + Returns: + list[tuple]: a list of tuples containing the apparats + """ + return self.query_db("SELECT * FROM semesterapparat WHERE deletion_status=?", (deleted,))
+ +def getApparatCountBySemester(self) ‑> tuple[list[str], list[int]] +
+-
++
get a list of all apparats created and deleted by semester
+Returns
+-
+
tuple[list[str],list[int]]
+- a tuple containing two lists, the first list contains the semesters, the second list contains the amount of apparats created and deleted for the corresponding semester +
+++Expand source code +
+
+def getApparatCountBySemester(self)->tuple[list[str],list[int]]: + """get a list of all apparats created and deleted by semester + + Returns: + tuple[list[str],list[int]]: a tuple containing two lists, the first list contains the semesters, the second list contains the amount of apparats created and deleted for the corresponding semester + """ + conn = self.connect() + cursor = conn.cursor() + semesters = self.getSemersters() + created = [] + deleted = [] + for semester in semesters: + query = f"SELECT COUNT(*) FROM semesterapparat WHERE erstellsemester='{semester}'" + result = cursor.execute(query).fetchone() + created.append(result[0]) + query = f"SELECT COUNT(*) FROM semesterapparat WHERE deletion_status=1 AND deleted_date='{semester}'" + result = cursor.execute(query).fetchone() + deleted.append(result[0]) + # store data in a tuple + ret = [] + e_tuple = () + for sem in semesters: + e_tuple = ( + sem, + created[semesters.index(sem)], + deleted[semesters.index(sem)], + ) + ret.append(e_tuple) + self.close_connection(conn) + return ret
+ +def getApparatData(self, appnr, appname) ‑> src.logic.dataclass.ApparatData +
+-
++
Get the Apparat data based on the apparat number and the name
+Args
+-
+
appnr:str
+- the apparat number +
appname:str
+- the name of the apparat +
Raises
+-
+
NoResultError
+- an error is raised if no result is found +
Returns
+-
+
ApparatData
+- the appended data of the apparat wrapped in an ApparatData object +
+++Expand source code +
+
+def getApparatData(self, appnr, appname)->ApparatData: + """Get the Apparat data based on the apparat number and the name + + Args: + appnr (str): the apparat number + appname (str): the name of the apparat + + Raises: + NoResultError: an error is raised if no result is found + + Returns: + ApparatData: the appended data of the apparat wrapped in an ApparatData object + """ + result = self.query_db("SELECT * FROM semesterapparat WHERE appnr=? AND name=?", (appnr,appname), one=True) + if result is None: + raise NoResultError("No result found") + apparat = ApparatData() + apparat.appname = result[1] + apparat.appnr = result[4] + 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.app_fach = result[3] + apparat.erstellsemester = result[5] + apparat.semester = result[8] + apparat.deleted = result[9] + apparat.apparat_adis_id = result[11] + apparat.prof_adis_id = result[12] + return apparat
+ +def getApparatId(self, apparat_name) ‑> Optional[int] +
+-
++
get the id of an apparat based on the name
+Args
+-
+
apparat_name:str
+- the name of the apparat e.g. "Semesterapparat 1" +
Returns
+-
+
Optional[int]
+- the id of the apparat, if the apparat is not found, None is returned +
+++Expand source code +
+
+def getApparatId(self, apparat_name)->Optional[int]: + """get the id of an apparat based on the name + + Args: + apparat_name (str): the name of the apparat e.g. "Semesterapparat 1" + + Returns: + Optional[int]: the id of the apparat, if the apparat is not found, None is returned + """ + data = self.query_db("SELECT appnr FROM semesterapparat WHERE name=?", (apparat_name,), one=True) + if data is None: + return None + else: + return data[0]
+ +def getApparatName(self, app_id: Union[str, int], prof_id: Union[str, int]) +
+-
++
get the name of the apparat based on the id
+Args
+-
+
app_id:Union[str,int]
+- the id of the apparat +
prof_id:Union[str,int]
+- the id of the professor +
Returns
+-
+
str
+- the name of the apparat +
+++Expand source code +
+
+def getApparatName(self, app_id:Union[str,int], prof_id:Union[str,int]): + """get the name of the apparat based on the id + + Args: + app_id (Union[str,int]): the id of the apparat + prof_id (Union[str,int]): the id of the professor + + Returns: + str: the name of the apparat + """ + return self.query_db("SELECT name FROM semesterapparat WHERE appnr=? AND prof_id=?", (app_id,prof_id), one=True)[0]
+ +def getApparatsByProf(self, prof_id: Union[str, int]) ‑> list[tuple] +
+-
++
Get all apparats based on the professor id
+Args
+-
+
prof_id:Union[str,int]
+- the id of the professor +
Returns
+-
+
list[tuple]
+- a list of tuples containing the apparats +
+++Expand source code +
+
+def getApparatsByProf(self, prof_id:Union[str,int])->list[tuple]: + """Get all apparats based on the professor id + + Args: + prof_id (Union[str,int]): the id of the professor + + Returns: + list[tuple]: a list of tuples containing the apparats + """ + return self.query_db("SELECT * FROM semesterapparat WHERE prof_id=?", (prof_id,))
+ +def getApparatsBySemester(self, semester: str) ‑> dict[list] +
+-
++
get all apparats based on the semester
+Args
+-
+
semester:str
+- the selected semester +
Returns
+-
+
dict[list]
+- a list off all created and deleted apparats for the selected semester +
+++Expand source code +
+
+def getApparatsBySemester(self, semester:str)->dict[list]: + """get all apparats based on the semester + + Args: + semester (str): the selected semester + + Returns: + dict[list]: a list off all created and deleted apparats for the selected semester + """ + data = self.query_db("SELECT name, prof_id FROM semesterapparat WHERE erstellsemester=?", (semester,)) + conn = self.connect() + cursor = conn.cursor() + c_tmp = [] + for i in data: + c_tmp.append((i[0], self.getProfNameById(i[1]))) + query = ( + f"SELECT name,prof_id FROM semesterapparat WHERE deleted_date='{semester}'" + ) + result = cursor.execute(query).fetchall() + d_tmp = [] + for i in result: + d_tmp.append((i[0], self.getProfNameById(i[1]))) + # group the apparats by prof + c_ret = {} + for i in c_tmp: + if i[1] not in c_ret.keys(): + c_ret[i[1]] = [i[0]] + else: + c_ret[i[1]].append(i[0]) + d_ret = {} + for i in d_tmp: + if i[1] not in d_ret.keys(): + d_ret[i[1]] = [i[0]] + else: + d_ret[i[1]].append(i[0]) + self.close_connection(conn) + return {"created": c_ret, "deleted": d_ret}
+ +def getBlob(self, filename, app_id: Union[str, int]) +
+-
++
Get a blob from the database
+Args
+-
+
filename:str
+- The name of the file +
app_id:str
+- ID of the apparat +
Returns
+-
+
bytes
+- The file stored in +
+++Expand source code +
+
+def getBlob(self, filename, app_id:Union[str,int]): + """ + Get a blob from the database + + Args: + filename (str): The name of the file + app_id (str): ID of the apparat + + Returns: + bytes: The file stored in + """ + return self.query_db("SELECT fileblob FROM files WHERE filename=? AND app_id=?", (filename,app_id), one=True)[0]
+ +def getBook(self, book_id: int) ‑> src.logic.dataclass.BookData +
+-
++
Get the book based on the id in the database
+Args
+-
+
book_id:int
+- The id of the book +
Returns
+-
+
BookData
+- The metadata of the book wrapped in a BookData object +
+++Expand source code +
+
+def getBook(self,book_id:int)->BookData: + """ + Get the book based on the id in the database + + Args: + book_id (int): The id of the book + + Returns: + BookData: The metadata of the book wrapped in a BookData object + """ + return load_pickle(self.query_db("SELECT bookdata FROM media WHERE id=?", (book_id,), one=True)[0])
+ +def getBookBasedOnSignature(self, app_id: Union[str, int], prof_id: Union[str, int], signature: str) ‑> src.logic.dataclass.BookData +
+-
++
Get the book based on the signature of the book.
+Args
+-
+
app_id:str
+- The apparat id the book should be associated with +
prof_id:str
+- The professor id the book should be associated with +
signature:str
+- The signature of the book +
Returns
+-
+
BookData
+- The total metadata of the book wrapped in a BookData object +
+++Expand source code +
+
+def getBookBasedOnSignature(self, app_id:Union[str,int], prof_id:Union[str,int],signature:str)->BookData: + """ + Get the book based on the signature of the book. + + Args: + app_id (str): The apparat id the book should be associated with + prof_id (str): The professor id the book should be associated with + signature (str): The signature of the book + + Returns: + BookData: The total metadata of the book wrapped in a BookData object + """ + result = self.query_db("SELECT bookdata FROM media WHERE app_id=? AND prof_id=?", (app_id,prof_id)) + books = [load_pickle(i[0]) for i in result] + book = [i for i in books if i.signature == signature][0] + return book
+ +def getBookId(self, bookdata: src.logic.dataclass.BookData, app_id: Union[str, int], prof_id: Union[str, int]) ‑> int +
+-
++
Get the id of a book based on the metadata of the book
+Args
+-
+
bookdata:BookData
+- The wrapped metadata of the book +
app_id:str
+- The apparat id the book should be associated with +
prof_id:str
+- The professor id the book should be associated with +
Returns
+-
+
int
+- ID of the book +
+++Expand source code +
+
+def getBookId(self, bookdata:BookData, app_id:Union[str,int], prof_id:Union[str,int])->int: + """ + Get the id of a book based on the metadata of the book + + Args: + bookdata (BookData): The wrapped metadata of the book + app_id (str): The apparat id the book should be associated with + prof_id (str): The professor id the book should be associated with + + Returns: + int: ID of the book + """ + result = self.query_db("SELECT id FROM media WHERE bookdata=? AND app_id=? AND prof_id=?", (dump_pickle(bookdata),app_id,prof_id), one=True) + return result[0]
+ +def getBookIdBasedOnSignature(self, app_id: Union[str, int], prof_id: Union[str, int], signature: str) ‑> int +
+-
++
Get a book id based on the signature of the book.
+Args
+-
+
app_id:str
+- The apparat id the book should be associated with +
prof_id:str
+- The professor id the book should be associated with +
signature:str
+- The signature of the book +
Returns
+-
+
int
+- The id of the book +
+++Expand source code +
+
+def getBookIdBasedOnSignature(self, app_id:Union[str,int], prof_id:Union[str,int],signature:str)->int: + """ + Get a book id based on the signature of the book. + + Args: + app_id (str): The apparat id the book should be associated with + prof_id (str): The professor id the book should be associated with + signature (str): The signature of the book + + Returns: + int: The id of the book + """ + result = self.query_db("SELECT bookdata, id FROM media WHERE app_id=? AND prof_id=?", (app_id,prof_id)) + books = [(load_pickle(i[0]),i[1]) for i in result] + book = [i for i in books if i[0].signature == signature][0][1] + return book
+ +def getBooks(self, app_id: Union[str, int], prof_id: Union[str, int], deleted=0) ‑> list[dict[int, src.logic.dataclass.BookData, int]] +
+-
++
Get the Books based on the apparat id and the professor id
+Args
+-
+
app_id:str
+- The ID of the apparat +
prof_id:str
+- The ID of the professor +
deleted:int, optional
+- The state of the book. Set to 1 to include deleted ones. Defaults to 0. +
Returns
+-
+
list[dict[int, BookData, int]]
+- A list of dictionaries containing the id, the metadata of the book and the availability of the book +
+++Expand source code +
+
+def getBooks(self, app_id:Union[str,int], prof_id:Union[str,int], deleted=0)->list[dict[int, BookData, int]]: + """ + Get the Books based on the apparat id and the professor id + + Args: + app_id (str): The ID of the apparat + prof_id (str): The ID of the professor + deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0. + + Returns: + list[dict[int, BookData, int]]: A list of dictionaries containing the id, the metadata of the book and the availability of the book + """ + qdata = self.query_db(f"SELECT id,bookdata,available FROM media WHERE (app_id={app_id} AND prof_id={prof_id}) AND (deleted={deleted if deleted == 0 else '1 OR deleted=0'})") + ret_result = [] + for result_a in qdata: + data = {"id": int, "bookdata": BookData, "available": int} + data["id"] = result_a[0] + data["bookdata"] = load_pickle(result_a[1]) + data["available"] = result_a[2] + ret_result.append(data) + return ret_result
+ +def getFacultyMember(self, name: str) ‑> tuple +
+-
++
get a faculty member based on the name
+Args
+-
+
name:str
+- the name to be searched for +
Returns
+-
+
tuple
+- a tuple containing the data of the faculty member +
+++Expand source code +
+
+def getFacultyMember(self, name:str)->tuple: + """get a faculty member based on the name + + Args: + name (str): the name to be searched for + + Returns: + tuple: a tuple containing the data of the faculty member + """ + return self.query_db("SELECT titel, fname,lname,mail,telnr,fullname FROM prof WHERE fullname=?", (name,), one=True)
+ +def getFacultyMembers(self) +
+-
++
get a list of all faculty members
+Returns
+-
+
list[tuple]
+- a list of tuples containing the faculty members +
+++Expand source code +
+
+def getFacultyMembers(self): + """get a list of all faculty members + + Returns: + list[tuple]: a list of tuples containing the faculty members + """ + return self.query_db("SELECT titel, fname,lname,mail,telnr,fullname FROM prof")
+ +def getFiles(self, app_id: Union[str, int], prof_id: int) ‑> list[tuple] +
+-
++
Get all the files associated with the apparat and the professor
+Args
+-
+
app_id:Union[str,int]
+- The id of the apparat +
prof_id:Union[str,int]
+- the id of the professor +
Returns
+-
+
list[tuple]
+- a list of tuples containing the filename and the filetype for the corresponding apparat and professor +
+++Expand source code +
+
+def getFiles(self, app_id:Union[str,int], prof_id:int)->list[tuple]: + """Get all the files associated with the apparat and the professor + + Args: + app_id (Union[str,int]): The id of the apparat + prof_id (Union[str,int]): the id of the professor + + Returns: + list[tuple]: a list of tuples containing the filename and the filetype for the corresponding apparat and professor + """ + return self.query_db("SELECT filename, filetyp FROM files WHERE app_id=? AND prof_id=?", (app_id,prof_id))
+ +def getLastBookId(self) ‑> int +
+-
++
Get the last book id in the database
+Returns
+-
+
int
+- ID of the last book in the database +
+++Expand source code +
+
+def getLastBookId(self)->int: + """ + Get the last book id in the database + + Returns: + int: ID of the last book in the database + """ + return self.query_db("SELECT id FROM media ORDER BY id DESC", one=True)[0]
+ +def getMessages(self, date: str) ‑> list[dict[str, str, str, str]] +
+-
++
Get all the messages for a specific date
+Args
+-
+
date:str
+- a date.datetime object formatted as a string in the format "YYYY-MM-DD" +
Returns
+-
+
list[dict[str, str, str, str]]
+- a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message +
+++Expand source code +
+
+def getMessages(self, date:str)->list[dict[str, str, str, str]]: + """Get all the messages for a specific date + + Args: + date (str): a date.datetime object formatted as a string in the format "YYYY-MM-DD" + + Returns: + list[dict[str, str, str, str]]: a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message + """ + def __get_user_name(user_id): + return self.query_db("SELECT username FROM user WHERE id=?", (user_id,), one=True)[0] + messages = self.query_db("SELECT * FROM messages WHERE remind_at=?", (date,)) + ret = [ + { + "message": i[2], + "user": __get_user_name(i[4]), + "appnr": i[5], + "id": i[0] + } + for i in messages + ] + return ret
+ +def getProfByName(self, prof_name: str) ‑> tuple +
+-
++
get all the data of a professor based on the name
+Args
+-
+
prof_name:str
+- the name of the professor +
Returns
+-
+
tuple
+- the data of the professor +
+++Expand source code +
+
+def getProfByName(self, prof_name:str)->tuple: + """get all the data of a professor based on the name + + Args: + prof_name (str): the name of the professor + + Returns: + tuple: the data of the professor + """ + return self.query_db("SELECT * FROM prof WHERE fullname=?", (prof_name,), one=True)
+ +def getProfData(self, profname: str) +
+-
++
Get mail, telephone number and title of a professor based on the name
+Args
+-
+
profname:str
+- name of the professor +
Returns
+-
+
tuple
+- the mail, telephone number and title of the professor +
+++Expand source code +
+
+def getProfData(self, profname:str): + """Get mail, telephone number and title of a professor based on the name + + Args: + profname (str): name of the professor + + Returns: + tuple: the mail, telephone number and title of the professor + """ + data = self.query_db("SELECT mail, telnr, titel FROM prof WHERE fullname=?", (profname.replace(",",""),), one=True) + return data
+ +def getProfId(self, prof_name: str) ‑> Optional[int] +
+-
++
Get the id of a professor based on the name
+Args
+-
+
prof_name:str
+- the name of the professor +
Returns
+-
+
Optional[int]
+- the id of the professor, if the professor is not found, None is returned +
+++Expand source code +
+
+def getProfId(self, prof_name:str)->Optional[int]: + """Get the id of a professor based on the name + + Args: + prof_name (str): the name of the professor + + Returns: + Optional[int]: the id of the professor, if the professor is not found, None is returned + """ + + data = self.getProfByName(prof_name.replace(",", "")) + if data is None: + return None + else: + return data[0]
+ +def getProfNameById(self, prof_id: Union[str, int], add_title: bool = False) ‑> str +
+-
++
Get a professor name based on the id
+Args
+-
+
prof_id:Union[str,int]
+- The id of the professor +
add_title:bool, optional
+- wether to add the title or no. Defaults to False. +
Returns
+-
+
str
+- The name of the professor +
+++Expand source code +
+
+def getProfNameById(self, prof_id:Union[str,int],add_title:bool=False)->str: + """Get a professor name based on the id + + Args: + prof_id (Union[str,int]): The id of the professor + add_title (bool, optional): wether to add the title or no. Defaults to False. + + Returns: + str: The name of the professor + """ + prof = self.query_db("SELECT fullname FROM prof WHERE id=?", (prof_id,), one=True) + if add_title: + return f"{self.getTitleById(prof_id)}{prof[0]}" + else: + return prof[0]
+ +def getProfs(self) ‑> list[tuple] +
+-
++
Return all the professors in the database
+Returns
+-
+
list[tuple]
+- a list containing all the professors in individual tuples +
+++Expand source code +
+
+def getProfs(self)->list[tuple]: + """Return all the professors in the database + + Returns: + list[tuple]: a list containing all the professors in individual tuples + """ + return self.query_db("SELECT * FROM prof")
+ +def getRole(self, user) +
+-
++
get the role of the user
+Args
+-
+
user:str
+- username +
Returns
+-
+
str
+- the name of the role +
+++Expand source code +
+
+def getRole(self, user): + """get the role of the user + + Args: + user (str): username + + Returns: + str: the name of the role + """ + return self.query_db("SELECT role FROM user WHERE username=?", (user,), one=True)[0]
+ +def getRoles(self) ‑> list[tuple] +
+-
++
get all the roles in the database
+Returns
+-
+
list[str]
+- a list of all the roles +
+++Expand source code +
+
+def getRoles(self)->list[tuple]: + """get all the roles in the database + + Returns: + list[str]: a list of all the roles + """ + return self.query_db("SELECT role FROM user")
+ +def getSemersters(self) ‑> list[str] +
+-
++
Return all the unique semesters in the database
+Returns
+-
+
list
+- a list of strings containing the semesters +
+++Expand source code +
+
+def getSemersters(self)->list[str]: + """Return all the unique semesters in the database + + Returns: + list: a list of strings containing the semesters + """ + data = self.query_db("SELECT DISTINCT erstellsemester FROM semesterapparat") + return [i[0] for i in data]
+ +def getSpecificProfData(self, prof_id: Union[str, int], fields: List[str]) ‑> tuple +
+-
++
A customisable function to get specific data of a professor based on the id
+Args
+-
+
prof_id:Union[str,int]
+- the id of the professor +
fields:List[str]
+- a list of fields to be returned +
Returns
+-
+
tuple
+- a tuple containing the requested data +
+++Expand source code +
+
+def getSpecificProfData(self, prof_id:Union[str,int], fields:List[str])->tuple: + """A customisable function to get specific data of a professor based on the id + + Args: + prof_id (Union[str,int]): the id of the professor + fields (List[str]): a list of fields to be returned + + Returns: + tuple: a tuple containing the requested data + """ + query = "SELECT " + for field in fields: + query += f"{field}," + query = query[:-1] + query += " FROM prof WHERE id=?" + return self.query_db(query, (prof_id,), one=True)[0]
+ +def getSubjects(self) +
+-
++
Get all the subjects in the database
+Returns
+-
+
list[tuple]
+- a list of tuples containing the subjects +
+++Expand source code +
+
+def getSubjects(self): + """Get all the subjects in the database + + Returns: + list[tuple]: a list of tuples containing the subjects + """ + return self.query_db("SELECT * FROM subjects")
+ +def getTitleById(self, prof_id: Union[str, int]) ‑> str +
+-
++
get the title of a professor based on the id
+Args
+-
+
prof_id:Union[str,int]
+- the id of the professor +
Returns
+-
+
str
+- the title of the professor, with an added whitespace at the end, if no title is present, an empty string is returned +
+++Expand source code +
+
+def getTitleById(self, prof_id:Union[str,int])->str: + """get the title of a professor based on the id + + Args: + prof_id (Union[str,int]): the id of the professor + + Returns: + str: the title of the professor, with an added whitespace at the end, if no title is present, an empty string is returned + """ + title = self.query_db("SELECT titel FROM prof WHERE id=?", (prof_id,), one=True)[0] + return f"{title} " if title is not None else ""
+
+ -
++
Get a list of all the apparat numbers in the database that are currently in use
+Returns
+-
+
List[int]
+- the list of used apparat numbers +
+++Expand source code +
+
+def getUnavailableApparatNumbers(self)->List[int]: + """Get a list of all the apparat numbers in the database that are currently in use + + Returns: + List[int]: the list of used apparat numbers + """ + numbers = self.query_db("SELECT appnr FROM semesterapparat WHERE deletion_status=0") + numbers = [i[0] for i in numbers] + logger.log_info(f"Currently used apparat numbers: {numbers}") + return numbers
+ +def getUser(self) +
+-
++
Get a single user from the database
+++Expand source code +
+
+def getUser(self): + """Get a single user from the database""" + return self.query_db("SELECT * FROM user", one=True)
+ +def getUsers(self) ‑> list[tuple] +
+-
++
Return a list of tuples of all the users in the database
+++Expand source code +
+
+def getUsers(self)->list[tuple]: + """Return a list of tuples of all the users in the database""" + return self.query_db("SELECT * FROM user")
+ +def get_db_contents(self) ‑> Optional[List[Tuple]] +
+-
++
Get the contents of the
+Returns
+-
+
Union[List[Tuple], None]
+- description +
+++Expand source code +
+
+def get_db_contents(self)->Union[List[Tuple], None]: + """ + Get the contents of the + + Returns: + Union[List[Tuple], None]: _description_ + """ + try: + with sql.connect(self.db_path) as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM sqlite_master WHERE type='table'") + return cursor.fetchall() + except sql.OperationalError: + return None
+ +def insertFile(self, file: list[dict], app_id: Union[str, int], prof_id: Union[str, int]) +
+-
++
Instert a list of files into the database
+Args
+-
+
file:list[dict]
+- a list containing all the files to be inserted +
Structured
+- [{"name": "filename", "path": "path", "type": "filetype"}] +
app_id:int
+- the id of the apparat +
prof_id:str
+- the id of the professor +
+++Expand source code +
+
+def insertFile(self, file: list[dict], app_id:Union[str,int], prof_id:Union[str,int]): + """Instert a list of files into the database + + Args: + file (list[dict]): a list containing all the files to be inserted + Structured: [{"name": "filename", "path": "path", "type": "filetype"}] + app_id (int): the id of the apparat + prof_id (str): the id of the professor + """ + for f in file: + filename = f["name"] + path = f["path"] + filetyp = f["type"] + if path == "Database": + continue + blob = create_blob(path) + query = "INSERT OR IGNORE INTO files (filename, fileblob, app_id, filetyp,prof_id) VALUES (?, ?, ?, ?,?)" + self.query_db(query, (filename, blob, app_id, filetyp,prof_id))
+ +def insertInto(self, query: str, params: Tuple) ‑> None +
+-
++
Insert sent data into the database
+Args
+-
+
query:str
+- The query to be executed +
params:Tuple
+- the parameters to be inserted into the database +
+++Expand source code +
+
+def insertInto(self, query:str, params:Tuple) -> None: + """ + Insert sent data into the database + + Args: + query (str): The query to be executed + params (Tuple): the parameters to be inserted into the database + """ + conn = self.connect() + cursor = conn.cursor() + logger.log_info(f"Inserting {params} into database with query {query}") + cursor.execute(query, params) + conn.commit() + self.close_connection(conn)
+ +def isEternal(self, id) +
+-
++
check if the apparat is eternal (dauerapparat)
+Args
+-
+
id:int
+- the id of the apparat to be checked +
Returns
+-
+
int
+- the state of the apparat +
+++Expand source code +
+
+def isEternal(self, id): + """check if the apparat is eternal (dauerapparat) + + Args: + id (int): the id of the apparat to be checked + + Returns: + int: the state of the apparat + """ + return self.query_db("SELECT dauer FROM semesterapparat WHERE appnr=?", (id,), one=True)
+ +def login(self, user, hashed_password) +
+-
++
try to login the user. +The salt for the user will be requested from the database and then added to the hashed password. The password will then be compared to the password in the database
+Args
+-
+
user:str
+- username that tries to login +
hashed_password:str
+- the password the user tries to login with +
Returns
+-
+
bool
+- True if the login was successful, False if not +
+++Expand source code +
+
+def login(self, user, hashed_password): + """try to login the user. + The salt for the user will be requested from the database and then added to the hashed password. The password will then be compared to the password in the database + + Args: + user (str): username that tries to login + hashed_password (str): the password the user tries to login with + + Returns: + bool: True if the login was successful, False if not + """ + salt = self.query_db("SELECT salt FROM user WHERE username=?", (user,), one=True)[0] + if salt is None: + return False + hashed_password = salt + hashed_password + password = self.query_db("SELECT password FROM user WHERE username=?", (user,), one=True)[0] + if password == hashed_password: + return True + else: + return False
+ +def query_db(self, query: str, args: Tuple = (), one: bool = False) ‑> Union[Tuple, List[Tuple]] +
+-
++
Query the Database for the sent query.
+Args
+-
+
query:str
+- The query to be executed +
args:Tuple, optional
+- The arguments for the query. Defaults to (). +
one:bool, optional
+- Return the first result only. Defaults to False. +
Returns
+Union[Typle|List[Tuple]]: Returns the result of the query
+++Expand source code +
+
+def query_db(self, query: str, args: Tuple = (), one: bool = False)->Union[Tuple, List[Tuple]]: + """ + Query the Database for the sent query. + + Args: + query (str): The query to be executed + args (Tuple, optional): The arguments for the query. Defaults to (). + one (bool, optional): Return the first result only. Defaults to False. + + Returns: + Union[Typle|List[Tuple]]: Returns the result of the query + """ + conn = self.connect() + cursor = conn.cursor() + logger.log_info(f"Querying database with query {query}, args: {args}") + cursor.execute(query, args) + rv = cursor.fetchall() + conn.commit() + self.close_connection(conn) + return (rv[0] if rv else None) if one else rv
+ +def recreateFile(self, filename: str, app_id: Union[str, int], filetype: str) ‑> str +
+-
++
Recreate a file from the database
+Args
+-
+
filename:str
+- the name of the file +
app_id:Union[str,int]
+- the id of the apparat +
filetype:str
+- the extension of the file to be created +
Returns
+-
+
str
+- The filename of the recreated file +
+++Expand source code +
+
+def recreateFile(self, filename:str, app_id:Union[str,int],filetype:str)->str: + """Recreate a file from the database + + Args: + filename (str): the name of the file + app_id (Union[str,int]): the id of the apparat + filetype (str): the extension of the file to be created + + Returns: + str: The filename of the recreated file + """ + blob = self.getBlob(filename, app_id) + tempdir = config.database.tempdir + tempdir = tempdir.replace("~", str(Path.home())) + tempdir_path = Path(tempdir) + if not os.path.exists(tempdir_path): + os.mkdir(tempdir_path) + file = tempfile.NamedTemporaryFile( + delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}" + ) + file.write(blob) + print("file created") + return file.name
+ +def searchBook(self, data: dict[str, str]) ‑> list[tuple[src.logic.dataclass.BookData, int]] +
+-
++
Search a book in the database based on the sent data.
+Args
+-
+
data:dict[str, str]
+- A dictionary containing the data to be searched for. The dictionary can contain the following: +
-
+
- signature: The signature of the book +
- title: The title of the book +
Returns
+-
+
list[tuple[BookData, int]]
+- A list of tuples containing the wrapped Metadata and the id of the book +
+++Expand source code +
+
+def searchBook(self, data:dict[str, str])->list[tuple[BookData, int]]: + """ + Search a book in the database based on the sent data. + + Args: + data (dict[str, str]): A dictionary containing the data to be searched for. The dictionary can contain the following: + - signature: The signature of the book + - title: The title of the book + + Returns: + list[tuple[BookData, int]]: A list of tuples containing the wrapped Metadata and the id of the book + """ + rdata = self.query_db("SELECT * FROM media WHERE deleted=0") + ic(rdata, len(rdata)) + mode = 0 + if len(data)== 1: + if "signature" in data.keys(): + mode = 1 + elif "title" in data.keys(): + mode = 2 + elif len(data) == 2: + mode = 3 + else: + return None + ret = [] + for book in rdata: + bookdata = load_pickle(book[1]) + app_id = book[2] + prof_id = book[3] + if mode == 1: + if data["signature"] in bookdata.signature: + ret.append((bookdata,app_id,prof_id)) + elif mode == 2: + if data["title"] in bookdata.title: + ret.append((bookdata,app_id,prof_id)) + elif mode == 3: + if data["signature"] in bookdata.signature and data["title"] in bookdata.title: + ret.append((bookdata,app_id,prof_id)) + ic(ret) + return ret
+ +def setAvailability(self, book_id: str, available: str) +
+-
++
Set the availability of a book in the database
+Args
+-
+
book_id:str
+- The id of the book +
available:str
+- The availability of the book +
+++Expand source code +
+
+def setAvailability(self, book_id:str, available:str): + """ + Set the availability of a book in the database + + Args: + book_id (str): The id of the book + available (str): The availability of the book + """ + self.query_db("UPDATE media SET available=? WHERE id=?", (available,book_id))
+ +def setNewSemesterDate(self, app_id: Union[str, int], newDate, dauerapp=False) +
+-
++
Set the new semester date for an apparat
+Args
+-
+
app_id:Union[str,int]
+- the id of the apparat +
newDate:str
+- the new date +
dauerapp:bool, optional
+- if the apparat was changed to dauerapparat. Defaults to False. +
+++Expand source code +
+
+def setNewSemesterDate(self, app_id:Union[str,int], newDate, dauerapp=False): + """Set the new semester date for an apparat + + Args: + app_id (Union[str,int]): the id of the apparat + newDate (str): the new date + dauerapp (bool, optional): if the apparat was changed to dauerapparat. Defaults to False. + """ + date = datetime.datetime.strptime(newDate, "%d.%m.%Y").strftime("%Y-%m-%d") + if dauerapp: + self.query_db("UPDATE semesterapparat SET verlängerung_bis=?, dauerapp=? WHERE appnr=?", (date,dauerapp,app_id)) + else: + self.query_db("UPDATE semesterapparat SET endsemester=? WHERE appnr=?", (date,app_id))
+ +def statistic_request(self, **kwargs: Any) +
+-
++
Take n amount of kwargs and return the result of the query
+++Expand source code +
+
+def statistic_request(self, **kwargs: Any): + """Take n amount of kwargs and return the result of the query + """ + def __query(query): + """execute the query and return the result + + Args: + query (str): the constructed query + + Returns: + list: the result of the query + """ + conn = self.connect() + cursor = conn.cursor() + result = cursor.execute(query).fetchall() + for result_a in result: + orig_value = result_a + prof_name = self.getProfNameById(result_a[2]) + # replace the prof_id with the prof_name + result_a = list(result_a) + result_a[2] = prof_name + result_a = tuple(result_a) + result[result.index(orig_value)] = result_a + self.close_connection(conn) + return result + if "deletable" in kwargs.keys(): + query = f"SELECT * FROM semesterapparat WHERE deletion_status=0 AND dauer=0 AND (erstellsemester!='{kwargs['deletesemester']}' OR verlängerung_bis!='{kwargs['deletesemester']}')" + return __query(query) + if "dauer" in kwargs.keys(): + kwargs["dauer"] = kwargs["dauer"].replace("Ja", "1").replace("Nein", "0") + query = "SELECT * FROM semesterapparat WHERE " + for key, value in kwargs.items() if kwargs.items() is not None else {}: + print(key, value) + query += f"{key}='{value}' AND " + print(query) + # remove deletesemester part from normal query, as this will be added to the database upon deleting the apparat + if "deletesemester" in kwargs.keys(): + query = query.replace( + f"deletesemester='{kwargs['deletesemester']}' AND ", "" + ) + if "endsemester" in kwargs.keys(): + if "erstellsemester" in kwargs.keys(): + query = query.replace(f"endsemester='{kwargs['endsemester']}' AND ", "") + query = query.replace( + f"erstellsemester='{kwargs['erstellsemester']} AND ", "xyz" + ) + else: + query = query.replace( + f"endsemester='{kwargs['endsemester']}' AND ", "xyz" + ) + print("replaced") + query = query.replace( + "xyz", + f"(erstellsemester='{kwargs['endsemester']}' OR verlängerung_bis='{kwargs['endsemester']}') AND ", + ) + # remove all x="" parts from the query where x is a key in kwargs + query = query[:-5] + print(query) + return __query(query)
+ +def updateApparat(self, apparat_data: src.logic.dataclass.ApparatData) +
+-
++
Update an apparat in the database
+Args
+-
+
apparat_data:ApparatData
+- the new metadata of the apparat +
+++Expand source code +
+
+def updateApparat(self, apparat_data:ApparatData): + """Update an apparat in the database + + Args: + apparat_data (ApparatData): the new metadata of the apparat + """ + query = f"UPDATE semesterapparat SET name = ?, fach = ?, dauer = ?, prof_id = ? WHERE appnr = ?" + params = ( + apparat_data.appname, + apparat_data.app_fach, + apparat_data.dauerapp, + self.getProfId(apparat_data.profname), + apparat_data.appnr, + ) + self.query_db(query, params)
+ +def updateBookdata(self, book_id, bookdata: src.logic.dataclass.BookData) +
+-
++
Update the bookdata in the database
+Args
+-
+
book_id:str
+- The id of the book +
bookdata:BookData
+- The new metadata of the book +
+++Expand source code +
+
+def updateBookdata(self, book_id, bookdata:BookData): + """ + Update the bookdata in the database + + Args: + book_id (str): The id of the book + bookdata (BookData): The new metadata of the book + """ + self.query_db("UPDATE media SET bookdata=? WHERE id=?", (dump_pickle(bookdata),book_id))
+ +def updateFacultyMember(self, data: dict, oldlname: str, oldfname: str) +
+-
++
update the data of a faculty member
+Args
+-
+
data:dict
+- a dictionary containing the data to be updated +
oldlname:str
+- the old last name of the faculty member +
oldfname:str
+- the old first name of the faculty member +
+++Expand source code +
+
+def updateFacultyMember(self, data:dict, oldlname:str, oldfname:str): + """update the data of a faculty member + + Args: + data (dict): a dictionary containing the data to be updated + oldlname (str): the old last name of the faculty member + oldfname (str): the old first name of the faculty member + """ + placeholders = ", ".join([f"{i}=:{i} " for i in data.keys()]) + query = f"UPDATE prof SET {placeholders} WHERE lname = :oldlname AND fname = :oldfname" + data["oldlname"] = oldlname + data["oldfname"] = oldfname + self.query_db(query, data)
+ +def updateUser(self, username, data: dict[str, str]) +
+-
++
changge the data of a user
+Args
+-
+
username:str
+- the username of the user +
data:dict[str, str]
+- the data to be changed +
+++Expand source code +
+
+def updateUser(self, username, data:dict[str, str]): + """changge the data of a user + + Args: + username (str): the username of the user + data (dict[str, str]): the data to be changed + """ + conn = self.connect() + cursor = conn.cursor() + query = "UPDATE user SET " + for key,value in data.items(): + if key == "username": + continue + query += f"{key}='{value}'," + query = query[:-1] + query += " WHERE username=?" + params = (username,) + cursor.execute(query, params) + conn.commit() + self.close_connection(conn)
+
+