chore: restructured project, updated readme

This commit is contained in:
2025-10-29 09:31:40 +01:00
parent a4460ec17b
commit ee62c65ae7
70 changed files with 8518 additions and 100 deletions

410
src/core/models.py Normal file
View File

@@ -0,0 +1,410 @@
import json
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Optional, Union
import regex
from src.logic.openai import name_tester, run_shortener, semester_converter
from src.logic.semester import Semester
@dataclass
class Prof:
id: Optional[int] = None
_title: Optional[str] = None
firstname: Optional[str] = None
lastname: Optional[str] = None
fullname: Optional[str] = None
mail: Optional[str] = None
telnr: Optional[str] = None
# add function that sets the data based on a dict
def from_dict(self, data: dict[str, Union[str, int]]):
for key, value in data.items():
if hasattr(self, key):
setattr(self, key, value)
return self
@property
def title(self) -> str:
if self._title is None or self._title == "None":
return ""
return self._title
@title.setter
def title(self, value: str):
self._title = value
# add function that sets the data from a tuple
def from_tuple(self, data: tuple[Union[str, int], ...]) -> "Prof":
setattr(self, "id", data[0])
setattr(self, "_title", data[1])
setattr(self, "firstname", data[2])
setattr(self, "lastname", data[3])
setattr(self, "fullname", data[4])
setattr(self, "mail", data[5])
setattr(self, "telnr", data[6])
return self
def name(self, comma: bool = False) -> Optional[str]:
if self.firstname is None and self.lastname is None:
if "," in self.fullname:
self.firstname = self.fullname.split(",")[1].strip()
self.lastname = self.fullname.split(",")[0].strip()
else:
return self.fullname
if comma:
return f"{self.lastname}, {self.firstname}"
return f"{self.lastname} {self.firstname}"
@dataclass
class BookData:
ppn: str | None = None
title: str | None = None
signature: str | None = None
edition: str | None = None
link: str | None = None
isbn: Union[str, list[str], None] = field(default_factory=list)
author: str | None = None
language: Union[str, list[str], None] = field(default_factory=list)
publisher: str | None = None
place: str | None = None
year: int | None = None
pages: str | None = None
library_location: str | None = None
in_apparat: bool | None = False
adis_idn: str | None = None
old_book: Any | None = None
media_type: str | None = None #
in_library: bool | None = None # whether the book is in the library or not
medianr: int | None = None # Media number in the library system
def __post_init__(self):
self.library_location = (
str(self.library_location) if self.library_location else None
)
if isinstance(self.language, list) and self.language:
self.language = [lang.strip() for lang in self.language if lang.strip()]
self.language = ",".join(self.language)
self.year = regex.sub(r"[^\d]", "", str(self.year)) if self.year else None
self.in_library = True if self.signature else False
def from_dict(self, data: dict) -> "BookData":
for key, value in data.items():
setattr(self, key, value)
return self
def merge(self, other: "BookData") -> "BookData":
for key, value in other.__dict__.items():
# merge lists, if the attribute is a list, extend it
if isinstance(value, list):
current_value = getattr(self, key)
if current_value is None:
current_value = []
elif not isinstance(current_value, list):
current_value = [current_value]
# extend the list with the new values, but only if they are not already in the list
for v in value:
if v not in current_value:
current_value.append(v)
setattr(self, key, current_value)
if value is not None and (
getattr(self, key) is None or getattr(self, key) == ""
):
setattr(self, key, value)
# in language, drop all entries that are longer than 3 characters
if isinstance(self.language, list):
self.language = [lang for lang in self.language if len(lang) <= 4]
return self
@property
def to_dict(self) -> str:
"""Convert the dataclass to a dictionary."""
data_dict = {
key: value for key, value in self.__dict__.items() if value is not None
}
# remove old_book from data_dict
if "old_book" in data_dict:
del data_dict["old_book"]
return json.dumps(data_dict, ensure_ascii=False)
def from_dataclass(self, dataclass: Optional[Any]) -> None:
if dataclass is None:
return
for key, value in dataclass.__dict__.items():
setattr(self, key, value)
def get_book_type(self) -> str:
if "Online" in self.pages:
return "eBook"
else:
return "Druckausgabe"
def from_string(self, data: str) -> "BookData":
ndata = json.loads(data)
return BookData(**ndata)
def from_LehmannsSearchResult(self, result: Any) -> "BookData":
self.title = result.title
self.author = "; ".join(result.authors) if result.authors else None
self.edition = str(result.edition) if result.edition else None
self.link = result.url
self.isbn = (
result.isbn13
if isinstance(result.isbn13, list)
else [result.isbn13]
if result.isbn13
else []
)
self.pages = str(result.pages) if result.pages else None
self.publisher = result.publisher
self.year = str(result.year) if result.year else None
# self.pages = str(result.pages) if result.pages else None
return self
@property
def edition_number(self) -> Optional[int]:
if self.edition is None:
return 0
match = regex.search(r"(\d+)", self.edition)
if match:
return int(match.group(1))
return 0
@dataclass
class MailData:
subject: Optional[str] = None
body: Optional[str] = None
mailto: Optional[str] = None
prof: Optional[str] = None
class Subjects(Enum):
BIOLOGY = (1, "Biologie")
CHEMISTRY = (2, "Chemie")
GERMAN = (3, "Deutsch")
ENGLISH = (4, "Englisch")
PEDAGOGY = (5, "Erziehungswissenschaft")
FRENCH = (6, "Französisch")
GEOGRAPHY = (7, "Geographie")
HISTORY = (8, "Geschichte")
HEALTH_EDUCATION = (9, "Gesundheitspädagogik")
HTW = (10, "Haushalt / Textil")
ART = (11, "Kunst")
MATH_IT = (12, "Mathematik / Informatik")
MEDIAPEDAGOGY = (13, "Medien in der Bildung")
MUSIC = (14, "Musik")
PHILOSOPHY = (15, "Philosophie")
PHYSICS = (16, "Physik")
POLITICS = (17, "Politikwissenschaft")
PRORECTORATE = (18, "Prorektorat Lehre und Studium")
PSYCHOLOGY = (19, "Psychologie")
SOCIOLOGY = (20, "Soziologie")
SPORT = (21, "Sport")
TECHNIC = (22, "Technik")
THEOLOGY = (23, "Theologie")
ECONOMICS = (24, "Wirtschaftslehre")
@property
def id(self) -> int:
return self.value[0]
@property
def name(self) -> str:
return self.value[1]
@classmethod
def get_index(cls, name: str) -> Optional[int]:
for i in cls:
if i.name == name:
return i.id - 1
return None
@dataclass
class Apparat:
id: int | None = None
name: str | None = None
prof_id: int | None = None
subject: str | None = None
appnr: int | None = None
created_semester: str | None = None
extended_at: str | None = None
eternal: bool = False
extend_until: str | None = None
deleted: int | None = None
deleted_date: str | None = None
apparat_id_adis: str | None = None
prof_id_adis: str | None = None
konto: int | None = None
def from_tuple(self, data: tuple[Any, ...]) -> "Apparat":
self.id = data[0]
self.name = data[1]
self.prof_id = data[2]
self.subject = data[3]
self.appnr = data[4]
self.created_semester = data[5]
self.extended_at = data[6]
self.eternal = data[7]
self.extend_until = data[8]
self.deleted = data[9]
self.deleted_date = data[10]
self.apparat_id_adis = data[11]
self.prof_id_adis = data[12]
self.konto = data[13]
return self
@property
def get_semester(self) -> Optional[str]:
if self.extend_until is not None:
return self.extend_until
else:
return self.created_semester
@dataclass
class ELSA:
id: int | None = None
date: str | None = None
semester: str | None = None
prof_id: int | None = None
def from_tuple(self, data: tuple[Any, ...]) -> "ELSA":
self.id = data[0]
self.date = data[1]
self.semester = data[2]
self.prof_id = data[3]
return self
@dataclass
class ApparatData:
prof: Prof = field(default_factory=Prof)
apparat: Apparat = field(default_factory=Apparat)
@dataclass
class XMLMailSubmission:
name: Optional[str] = None
lastname: Optional[str] = None
title: Optional[str] = None
telno: Optional[int] = None
email: Optional[str] = None
app_name: Optional[str] = None
subject: Optional[str] = None
semester: Optional[Semester] = None
books: Optional[list[BookData]] = None
@dataclass
class Book:
author: str = None
year: str = None
edition: str = None
title: str = None
location: str = None
publisher: str = None
signature: str = None
internal_notes: str = None
@property
def has_signature(self) -> bool:
return self.signature is not None and self.signature != ""
@property
def is_empty(self) -> bool:
return all(
[
self.author == "",
self.year == "",
self.edition == "",
self.title == "",
self.location == "",
self.publisher == "",
self.signature == "",
self.internal_notes == "",
]
)
def from_dict(self, data: dict[str, Any]):
for key, value in data.items():
value = value.strip()
if value == "\u2002\u2002\u2002\u2002\u2002":
value = ""
if key == "Autorenname(n):Nachname, Vorname":
self.author = value
elif key == "Jahr/Auflage":
self.year = value.split("/")[0] if "/" in value else value
self.edition = value.split("/")[1] if "/" in value else ""
elif key == "Titel":
self.title = value
elif key == "Ort und Verlag":
self.location = value.split(",")[0] if "," in value else value
self.publisher = value.split(",")[1] if "," in value else ""
elif key == "Standnummer":
self.signature = value.strip()
elif key == "Interne Vermerke":
self.internal_notes = value
@dataclass
class SemapDocument:
subject: str = None
phoneNumber: int = None
mail: str = None
title: str = None
title_suggestions: list[str] = None
semester: Union[str, Semester] = None
books: list[Book] = None
eternal: bool = False
personName: str = None
personTitle: str = None
title_length = 0
title_max_length = 0
def __post_init__(self):
self.title_suggestions = []
@property
def nameSetter(self):
data = name_tester(self.personTitle)
name = f"{data['last_name']}, {data['first_name']}"
if data["title"] is not None:
title = data["title"]
self.personTitle = title
self.personName = name
self.title_length = len(self.title) + 3 + len(self.personName.split(",")[0])
if self.title_length > 40:
name_len = len(self.personName.split(",")[0])
self.title_max_length = 38 - name_len
suggestions = run_shortener(self.title, self.title_max_length)
for suggestion in suggestions:
self.title_suggestions.append(suggestion["shortened_string"])
else:
self.title_suggestions = []
pass
@property
def renameSemester(self) -> None:
if self.semester:
if ", Dauer" in self.semester:
self.semester = self.semester.split(",")[0]
self.eternal = True
self.semester = Semester().from_string(self.semester)
else:
self.semester = Semester().from_string(
semester_converter(self.semester)
)
@property
def signatures(self) -> list[str]:
if self.books is not None:
return [book.signature for book in self.books if book.has_signature]
return []