from __future__ import annotations import json from dataclasses import dataclass, field from enum import Enum from typing import Any, Optional, Union import regex from src.core.semester import Semester @dataclass class Prof: id: int | None = None _title: str | None = None firstname: str | None = None lastname: str | None = None fullname: str | None = None mail: str | None = None telnr: str | None = 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: self.id = data[0] self._title = data[1] self.firstname = data[2] self.lastname = data[3] self.fullname = data[4] self.mail = data[5] 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" 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: str | None body: str | None mailto: str | None prof: 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 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: str | None lastname: str | None title: str | None telno: int | None email: str | None app_name: str | None subject: str | None semester: Semester | None books: 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 personName: str | None personTitle: str | None title_suggestions: list[str] = None semester: Union[str, Semester] = None books: list[Book] = None eternal: bool = False title_length: int = 0 title_max_length: int = 0 def __post_init__(self) -> None: """.""" self.title_suggestions = [] self.phoneNumber = int( regex.sub(r"[^\d]", "", str(self.phoneNumber)), ) @property def nameSetter(self): from src.services.openai import name_tester, run_shortener 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 = [] @property def renameSemester(self) -> None: from src.services.openai import semester_converter 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 [] @dataclass class ELSA_Mono: authorName: str year: int signature: str page_from: int page_to: int edition: str | None = None @dataclass class ELSA_Journal: authorName: str year: int issue: str page_from: int page_to: int journal_title: str article_title: str signature: str @dataclass class Person: firstName: str lastName: str personTitle: str | None = None @property def fullName_LNFN(self) -> str: return f"{self.lastName}, {self.firstName}" @dataclass class ELSA_Editorial: # TODO: add dataclass fields pass @dataclass class ELSADocument: mail: str = None personTitle: str = None personName: Optional[str] = None def __post_init__(self) -> None: """.""" self.mail = self.mail.strip() if self.mail else None self.personTitle = self.personTitle.strip() if self.personTitle else None self.personName = self.personName.strip() if self.personName else None