fix: issues (1)
This commit is contained in:
@@ -33,6 +33,8 @@ if not _user_log_dir:
|
||||
if not _user_config_dir:
|
||||
_user_config_dir = str(get_app_base_path() / "config")
|
||||
|
||||
from config import Config # noqa: E402
|
||||
|
||||
LOG_DIR: str = _user_log_dir
|
||||
CONFIG_DIR: str = _user_config_dir
|
||||
|
||||
@@ -49,8 +51,6 @@ except Exception:
|
||||
Path(LOG_DIR).mkdir(parents=True, exist_ok=True)
|
||||
Path(CONFIG_DIR).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
from config import Config
|
||||
|
||||
|
||||
settings = Config(f"{CONFIG_DIR}/config.yaml")
|
||||
DATABASE_DIR: Union[Path, str] = ( # type: ignore
|
||||
|
||||
@@ -1216,9 +1216,9 @@ class Database:
|
||||
Optional[int]: the id of the apparat
|
||||
|
||||
"""
|
||||
log.debug("Creating apparat: {} - {}", app.appnr, app.name)
|
||||
app = apparat.apparat
|
||||
prof = apparat.prof
|
||||
log.debug("Creating apparat: {} - {}", app.appnr, app.name)
|
||||
present_prof = self.getProfByName(prof.name())
|
||||
prof_id = present_prof.id
|
||||
log.debug("Present prof: {}", preview(present_prof, 300))
|
||||
|
||||
@@ -24,5 +24,5 @@ class DocumentationThread(QThread):
|
||||
self._process.terminate() # terminate the subprocess
|
||||
try:
|
||||
self._process.wait(timeout=5) # wait up to 5 seconds
|
||||
except:
|
||||
except Exception:
|
||||
self._process.kill() # force kill if it doesn't stop
|
||||
|
||||
@@ -12,7 +12,7 @@ from .models import (
|
||||
Subjects,
|
||||
XMLMailSubmission,
|
||||
)
|
||||
from .constants import *
|
||||
from .constants import * # noqa: F403
|
||||
from .semester import Semester
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -21,7 +21,7 @@ class Prof:
|
||||
telnr: str | None = None
|
||||
|
||||
# add function that sets the data based on a dict
|
||||
def from_dict(self, data: dict[str, Union[str, int]]):
|
||||
def from_dict(self, data: dict[str, Union[str, int]]) -> 'Prof':
|
||||
for key, value in data.items():
|
||||
if hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
@@ -38,27 +38,40 @@ class Prof:
|
||||
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]
|
||||
def from_tuple(self, data: tuple[Union[int, str, None], ...]) -> 'Prof':
|
||||
self.id = data[0] if data[0] is not None and isinstance(data[0], int) else None
|
||||
self._title = str(data[1]) if data[1] is not None else None
|
||||
self.firstname = str(data[2]) if data[2] is not None else None
|
||||
self.lastname = str(data[3]) if data[3] is not None else None
|
||||
self.fullname = str(data[4]) if data[4] is not None else None
|
||||
self.mail = str(data[5]) if data[5] is not None else None
|
||||
self.telnr = str(data[6]) if data[6] is not None else None
|
||||
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()
|
||||
if self.fullname and "," in self.fullname:
|
||||
parts = self.fullname.split(",")
|
||||
if len(parts) >= 2:
|
||||
self.firstname = parts[1].strip()
|
||||
self.lastname = parts[0].strip()
|
||||
else:
|
||||
return self.fullname
|
||||
|
||||
if comma:
|
||||
return f"{self.lastname}, {self.firstname}"
|
||||
return f"{self.lastname} {self.firstname}"
|
||||
if self.lastname and self.firstname:
|
||||
return f"{self.lastname}, {self.firstname}"
|
||||
elif self.lastname:
|
||||
return self.lastname
|
||||
elif self.firstname:
|
||||
return f", {self.firstname}"
|
||||
elif self.lastname and self.firstname:
|
||||
return f"{self.lastname} {self.firstname}"
|
||||
elif self.lastname:
|
||||
return self.lastname
|
||||
elif self.firstname:
|
||||
return self.firstname
|
||||
return self.fullname
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -90,10 +103,12 @@ class BookData:
|
||||
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
|
||||
if self.year is not None:
|
||||
year_str = regex.sub(r"[^\d]", "", str(self.year))
|
||||
self.year = int(year_str) if year_str else None
|
||||
self.in_library = True if self.signature else False
|
||||
|
||||
def from_dict(self, data: dict) -> BookData:
|
||||
def from_dict(self, data: dict[str, Any]) -> 'BookData':
|
||||
for key, value in data.items():
|
||||
setattr(self, key, value)
|
||||
return self
|
||||
@@ -132,23 +147,26 @@ class BookData:
|
||||
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:
|
||||
def from_dataclass(self, data_obj: Optional[Any]) -> None:
|
||||
if data_obj is None:
|
||||
return
|
||||
for key, value in dataclass.__dict__.items():
|
||||
for key, value in data_obj.__dict__.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def get_book_type(self) -> str:
|
||||
if "Online" in self.pages:
|
||||
if self.pages and "Online" in self.pages:
|
||||
return "eBook"
|
||||
return "Druckausgabe"
|
||||
|
||||
def from_string(self, data: str) -> BookData:
|
||||
def from_string(self, data: str) -> 'BookData':
|
||||
ndata = json.loads(data)
|
||||
# Create a new BookData instance and set its attributes
|
||||
book_data = BookData()
|
||||
for key, value in ndata.items():
|
||||
setattr(book_data, key, value)
|
||||
return book_data
|
||||
|
||||
return BookData(**ndata)
|
||||
|
||||
def from_LehmannsSearchResult(self, result: Any) -> BookData:
|
||||
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
|
||||
@@ -170,7 +188,7 @@ class BookData:
|
||||
def edition_number(self) -> Optional[int]:
|
||||
if self.edition is None:
|
||||
return 0
|
||||
match = regex.search(r"(\d+)", self.edition)
|
||||
match = regex.search(r"(\d+)", self.edition or "")
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return 0
|
||||
@@ -215,13 +233,13 @@ class Subjects(Enum):
|
||||
return self.value[0]
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
def subject_name(self) -> str:
|
||||
return self.value[1]
|
||||
|
||||
@classmethod
|
||||
def get_index(cls, name: str) -> Optional[int]:
|
||||
for i in cls:
|
||||
if i.name == name:
|
||||
if i.subject_name == name:
|
||||
return i.id - 1
|
||||
return None
|
||||
|
||||
@@ -355,25 +373,24 @@ class Book:
|
||||
|
||||
@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
|
||||
subject: str | None = None
|
||||
phoneNumber: int | None = None
|
||||
mail: str | None = None
|
||||
title: str | None = None
|
||||
personName: str | None = None
|
||||
personTitle: str | None = None
|
||||
title_suggestions: list[str] = field(default_factory=list)
|
||||
semester: Union[str, 'Semester', None] = None
|
||||
books: list[Book] = field(default_factory=list)
|
||||
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)),
|
||||
)
|
||||
if self.phoneNumber is not None:
|
||||
phone_str = regex.sub(r"[^\d]", "", str(self.phoneNumber))
|
||||
self.phoneNumber = int(phone_str) if phone_str else None
|
||||
|
||||
@property
|
||||
def nameSetter(self):
|
||||
@@ -399,7 +416,7 @@ class SemapDocument:
|
||||
def renameSemester(self) -> None:
|
||||
from src.services.openai import semester_converter
|
||||
|
||||
if self.semester:
|
||||
if self.semester and isinstance(self.semester, str):
|
||||
if ", Dauer" in self.semester:
|
||||
self.semester = self.semester.split(",")[0]
|
||||
self.eternal = True
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Semester helper class
|
||||
"""Semester helper class.
|
||||
|
||||
A small utility around the *German* academic calendar that distinguishes
|
||||
between *Wintersemester* (WiSe) and *Sommersemester* (SoSe).
|
||||
@@ -7,7 +7,7 @@ Key points
|
||||
----------
|
||||
* A **`Semester`** is identified by a *term* ("SoSe" or "WiSe") and the last two
|
||||
digits of the calendar year in which the term *starts*.
|
||||
* Formatting **never** pads the year with a leading zero – so ``6`` stays ``6``.
|
||||
* Formatting **never** pads the year with a leading zero - so ``6`` stays ``6``.
|
||||
* ``offset(n)`` and the static ``generate_missing`` reliably walk the timeline
|
||||
one semester at a time with correct year transitions:
|
||||
|
||||
@@ -26,13 +26,13 @@ class Semester:
|
||||
"""Represents a German university semester (WiSe or SoSe)."""
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Class‑level defaults – will be *copied* to each instance and then
|
||||
# Class-level defaults - will be *copied* to each instance and then
|
||||
# potentially overwritten in ``__init__``.
|
||||
# ------------------------------------------------------------------
|
||||
_year: int | None = None # Will be set in __post_init__
|
||||
_semester: str | None = None # "WiSe" or "SoSe" – set later
|
||||
_semester: str | None = None # "WiSe" or "SoSe" - set later
|
||||
_month: int | None = None # Will be set in __post_init__
|
||||
value: str | None = None # Human‑readable label, e.g. "WiSe 23/24"
|
||||
value: str | None = None # Human-readable label, e.g. "WiSe 23/24"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Construction helpers
|
||||
@@ -89,17 +89,22 @@ class Semester:
|
||||
# ------------------------------------------------------------------
|
||||
def _generate_semester_from_month(self) -> None:
|
||||
"""Infer *WiSe* / *SoSe* from the month attribute."""
|
||||
self._semester = "WiSe" if (self._month <= 3 or self._month > 9) else "SoSe"
|
||||
if self._month is not None:
|
||||
self._semester = "WiSe" if (self._month <= 3 or self._month > 9) else "SoSe"
|
||||
else:
|
||||
self._semester = "WiSe" # Default value if month is None
|
||||
|
||||
def _compute_value(self) -> None:
|
||||
"""Human‑readable semester label – e.g. ``WiSe 23/24`` or ``SoSe 24``."""
|
||||
year = self._year
|
||||
if self._semester == "WiSe":
|
||||
next_year = (year + 1) % 100 # wrap 99 → 0
|
||||
|
||||
self.value = f"WiSe {year}/{next_year}"
|
||||
else: # SoSe
|
||||
self.value = f"SoSe {year}"
|
||||
"""Human-readable semester label - e.g. ``WiSe 23/24`` or ``SoSe 24``."""
|
||||
if self._year is not None:
|
||||
year = self._year
|
||||
if self._semester == "WiSe":
|
||||
next_year = (year + 1) % 100 # wrap 99 → 0
|
||||
self.value = f"WiSe {year}/{next_year}"
|
||||
else: # SoSe
|
||||
self.value = f"SoSe {year}"
|
||||
else:
|
||||
self.value = "<invalid Semester>"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Public API
|
||||
@@ -117,10 +122,12 @@ class Semester:
|
||||
if value == 0:
|
||||
return Semester(self._year, self._semester)
|
||||
|
||||
if self._year is None:
|
||||
raise ValueError("Cannot offset from a semester with no year")
|
||||
current_idx = self._year * 2 + (0 if self._semester == "SoSe" else 1)
|
||||
target_idx = current_idx + value
|
||||
if target_idx < 0:
|
||||
raise ValueError("offset would result in a negative year – not supported")
|
||||
raise ValueError("offset would result in a negative year - not supported")
|
||||
|
||||
new_year, semester_bit = divmod(target_idx, 2)
|
||||
new_semester = "SoSe" if semester_bit == 0 else "WiSe"
|
||||
@@ -164,10 +171,14 @@ class Semester:
|
||||
|
||||
@property
|
||||
def year(self) -> int:
|
||||
if self._year is None:
|
||||
raise ValueError("Year is not set for this semester")
|
||||
return self._year
|
||||
|
||||
@property
|
||||
def semester(self) -> str:
|
||||
if self._semester is None:
|
||||
raise ValueError("Semester is not set for this semester")
|
||||
return self._semester
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -178,14 +189,14 @@ class Semester:
|
||||
"""Return all consecutive semesters from *start* to *end* (inclusive)."""
|
||||
if not isinstance(start, Semester) or not isinstance(end, Semester):
|
||||
raise TypeError("start and end must be Semester instances")
|
||||
if start.is_future_semester(end) and not start.isMatch(end):
|
||||
if start.is_future_semester(end) and not start.is_match(end):
|
||||
raise ValueError("'start' must not be after 'end'")
|
||||
|
||||
chain: list[Semester] = [start.value]
|
||||
chain: list[str] = [str(start)]
|
||||
current = start
|
||||
while not current.isMatch(end):
|
||||
while not current.is_match(end):
|
||||
current = current.next
|
||||
chain.append(current.value)
|
||||
chain.append(str(current))
|
||||
if len(chain) > 1000: # sanity guard
|
||||
raise RuntimeError("generate_missing exceeded sane iteration limit")
|
||||
return chain
|
||||
@@ -195,9 +206,9 @@ class Semester:
|
||||
# ------------------------------------------------------------------
|
||||
@classmethod
|
||||
def from_string(cls, s: str) -> Semester:
|
||||
"""Parse a human‑readable semester label and return a :class:`Semester`.
|
||||
"""Parse a human-readable semester label and return a :class:`Semester`.
|
||||
|
||||
Accepted formats (case‑insensitive)::
|
||||
Accepted formats (case-insensitive)::
|
||||
|
||||
"SoSe <YY>" → SoSe of year YY
|
||||
"WiSe <YY>/<YY+1>" → Winter term starting in YY
|
||||
@@ -212,7 +223,7 @@ class Semester:
|
||||
m = re.fullmatch(pattern, s, flags=re.IGNORECASE)
|
||||
if not m:
|
||||
raise ValueError(
|
||||
"invalid semester string format – expected 'SoSe YY' or 'WiSe YY/YY' (spacing flexible)",
|
||||
"invalid semester string format - expected 'SoSe YY' or 'WiSe YY/YY' (spacing flexible)",
|
||||
)
|
||||
|
||||
term_raw, y1_str, y2_str = m.groups()
|
||||
@@ -236,7 +247,7 @@ class Semester:
|
||||
return cls(year, "WiSe")
|
||||
|
||||
|
||||
# ------------------------- quick self‑test -------------------------
|
||||
# ------------------------- quick self-test -------------------------
|
||||
if __name__ == "__main__":
|
||||
# Chain generation demo ------------------------------------------------
|
||||
s_start = Semester(6, "SoSe") # SoSe 6
|
||||
|
||||
@@ -123,7 +123,7 @@ class Database:
|
||||
try:
|
||||
if self.db_path is not None:
|
||||
self.run_migrations()
|
||||
except Exception as e:
|
||||
except (sql.Error, OSError, IOError) as e:
|
||||
log.error(f"Error while running migrations: {e}")
|
||||
|
||||
# --- Migration helpers integrated into Database ---
|
||||
@@ -212,9 +212,9 @@ class Database:
|
||||
).__str__()
|
||||
return result[0]
|
||||
|
||||
def getElsaMediaType(self, id):
|
||||
def getElsaMediaType(self, media_id):
|
||||
query = "SELECT type FROM elsa_media WHERE id=?"
|
||||
return self.query_db(query, (id,), one=True)[0]
|
||||
return self.query_db(query, (media_id,), one=True)[0]
|
||||
|
||||
def get_db_contents(self) -> Union[List[Tuple[Any]], None]:
|
||||
"""
|
||||
@@ -736,7 +736,7 @@ class Database:
|
||||
try:
|
||||
bloat.debug("Recreated file blob size: {} bytes", len(blob))
|
||||
bloat.debug("Recreated file blob (preview): {}", preview(blob, 2000))
|
||||
except Exception:
|
||||
except (TypeError, UnicodeDecodeError, ValueError):
|
||||
bloat.debug("Recreated file blob (preview): {}", preview(blob, 2000))
|
||||
tempdir = settings.database.temp.expanduser()
|
||||
if not tempdir.exists():
|
||||
@@ -990,16 +990,16 @@ class Database:
|
||||
person = Prof()
|
||||
return person.from_tuple(data)
|
||||
|
||||
def getProf(self, id) -> Prof:
|
||||
def getProf(self, prof_id) -> Prof:
|
||||
"""Get a professor based on the id
|
||||
|
||||
Args:
|
||||
id ([type]): the id of the professor
|
||||
prof_id ([type]): the id of the professor
|
||||
|
||||
Returns:
|
||||
Prof: a Prof object containing the data of the professor
|
||||
"""
|
||||
data = self.query_db("SELECT * FROM prof WHERE id=?", (id,), one=True)
|
||||
data = self.query_db("SELECT * FROM prof WHERE id=?", (prof_id,), one=True)
|
||||
return Prof().from_tuple(data)
|
||||
|
||||
def getProfs(self) -> list[Prof]:
|
||||
@@ -1278,17 +1278,17 @@ class Database:
|
||||
# print(apparat_nr, app_id)
|
||||
self.query_db("UPDATE media SET deleted=1 WHERE app_id=?", (app_id,))
|
||||
|
||||
def isEternal(self, id):
|
||||
def isEternal(self, apparat_id):
|
||||
"""check if the apparat is eternal (dauerapparat)
|
||||
|
||||
Args:
|
||||
id (int): the id of the apparat to be checked
|
||||
apparat_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
|
||||
"SELECT dauer FROM semesterapparat WHERE appnr=?", (apparat_id,), one=True
|
||||
)
|
||||
|
||||
def getApparatName(self, app_id: Union[str, int], prof_id: Union[str, int]):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
from os.path import basename
|
||||
from pathlib import Path
|
||||
|
||||
from docx import Document
|
||||
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
||||
@@ -69,20 +70,20 @@ class SemesterDocument:
|
||||
full: bool = False,
|
||||
):
|
||||
assert isinstance(apparats, list), SemesterError(
|
||||
"Apparats must be a list of tuples"
|
||||
"Apparats must be a list of tuples",
|
||||
)
|
||||
assert all(isinstance(apparat, tuple) for apparat in apparats), SemesterError(
|
||||
"Apparats must be a list of tuples"
|
||||
"Apparats must be a list of tuples",
|
||||
)
|
||||
assert all(isinstance(apparat[0], int) for apparat in apparats), SemesterError(
|
||||
"Apparat numbers must be integers"
|
||||
"Apparat numbers must be integers",
|
||||
)
|
||||
assert all(isinstance(apparat[1], str) for apparat in apparats), SemesterError(
|
||||
"Apparat names must be strings"
|
||||
"Apparat names must be strings",
|
||||
)
|
||||
assert isinstance(semester, str), SemesterError("Semester must be a string")
|
||||
assert "." not in filename and isinstance(filename, str), SemesterError(
|
||||
"Filename must be a string and not contain an extension"
|
||||
"Filename must be a string and not contain an extension",
|
||||
)
|
||||
self.doc = Document()
|
||||
self.apparats = apparats
|
||||
@@ -108,8 +109,7 @@ class SemesterDocument:
|
||||
log.info("Document printed")
|
||||
|
||||
def set_table_border(self, table):
|
||||
"""
|
||||
Adds a full border to the table.
|
||||
"""Adds a full border to the table.
|
||||
|
||||
:param table: Table object to which the border will be applied.
|
||||
"""
|
||||
@@ -150,7 +150,8 @@ class SemesterDocument:
|
||||
trPr = row._tr.get_or_add_trPr() # Get or add the <w:trPr> element
|
||||
trHeight = OxmlElement("w:trHeight")
|
||||
trHeight.set(
|
||||
qn("w:val"), str(int(Pt(15).pt * 20))
|
||||
qn("w:val"),
|
||||
str(int(Pt(15).pt * 20)),
|
||||
) # Convert points to twips
|
||||
trHeight.set(qn("w:hRule"), "exact") # Use "exact" for fixed height
|
||||
trPr.append(trHeight)
|
||||
@@ -233,7 +234,7 @@ class SemesterDocument:
|
||||
self.save_document(self.filename + ".docx")
|
||||
docpath = os.path.abspath(self.filename + ".docx")
|
||||
doc = word.Documents.Open(docpath)
|
||||
curdir = os.getcwd()
|
||||
curdir = Path.cwd()
|
||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||
doc.Close()
|
||||
word.Quit()
|
||||
@@ -317,7 +318,7 @@ class SemapSchilder:
|
||||
self.save_document()
|
||||
docpath = os.path.abspath(f"{self.filename}.docx")
|
||||
doc = word.Documents.Open(docpath)
|
||||
curdir = os.getcwd()
|
||||
curdir = Path.cwd()
|
||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||
doc.Close()
|
||||
word.Quit()
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# import basic error classes
|
||||
from .DatabaseErrors import *
|
||||
from .DatabaseErrors import * # noqa: F403
|
||||
|
||||
@@ -6,3 +6,11 @@ from .transformers import (
|
||||
RDSData,
|
||||
RISData,
|
||||
)
|
||||
|
||||
# Explicit re-exports to avoid F401 warnings
|
||||
RDS_AVAIL_DATA = RDS_AVAIL_DATA
|
||||
ARRAYData = ARRAYData
|
||||
BibTeXData = BibTeXData
|
||||
COinSData = COinSData
|
||||
RDSData = RDSData
|
||||
RISData = RISData
|
||||
|
||||
@@ -141,7 +141,7 @@ class ARRAYData:
|
||||
source = source.replace("\t", "").replace("\r", "")
|
||||
source = source.split(search)[1].split(")")[0]
|
||||
return _get_line(source, entry).replace("=>", "").strip()
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _get_isbn(source: str) -> list:
|
||||
@@ -157,7 +157,7 @@ class ARRAYData:
|
||||
continue
|
||||
ret.append(isb) if isb not in ret else None
|
||||
return ret
|
||||
except:
|
||||
except Exception:
|
||||
isbn = []
|
||||
return isbn
|
||||
|
||||
@@ -294,7 +294,7 @@ class COinSData:
|
||||
try:
|
||||
data = source.split(f"{search}=")[1] # .split("")[0].strip()
|
||||
return data.split("rft")[0].strip() if "rft" in data else data
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
return BookData(
|
||||
@@ -319,7 +319,7 @@ class RISData:
|
||||
try:
|
||||
data = source.split(f"{search} - ")[1] # .split("")[0].strip()
|
||||
return data.split("\n")[0].strip() if "\n" in data else data
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
return BookData(
|
||||
@@ -356,7 +356,7 @@ class BibTeXData:
|
||||
.replace("[", "")
|
||||
.replace("];", "")
|
||||
)
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
return BookData(
|
||||
|
||||
@@ -9,11 +9,11 @@ from ratelimit import limits, sleep_and_retry
|
||||
|
||||
from src.core.models import BookData
|
||||
from src.shared.logging import log, get_bloat_logger, preview
|
||||
from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
|
||||
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
|
||||
|
||||
# bloat logger for large/raw HTTP responses
|
||||
bloat = get_bloat_logger()
|
||||
from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
|
||||
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
|
||||
|
||||
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
|
||||
|
||||
|
||||
@@ -6,3 +6,11 @@ from .transformers import (
|
||||
RDSData,
|
||||
RISData,
|
||||
)
|
||||
|
||||
# Explicit re-exports to avoid F401 warnings
|
||||
RDS_AVAIL_DATA = RDS_AVAIL_DATA
|
||||
ARRAYData = ARRAYData
|
||||
BibTeXData = BibTeXData
|
||||
COinSData = COinSData
|
||||
RDSData = RDSData
|
||||
RISData = RISData
|
||||
|
||||
@@ -141,7 +141,7 @@ class ARRAYData:
|
||||
source = source.replace("\t", "").replace("\r", "")
|
||||
source = source.split(search)[1].split(")")[0]
|
||||
return _get_line(source, entry).replace("=>", "").strip()
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _get_isbn(source: str) -> list:
|
||||
@@ -157,7 +157,7 @@ class ARRAYData:
|
||||
continue
|
||||
ret.append(isb) if isb not in ret else None
|
||||
return ret
|
||||
except:
|
||||
except Exception:
|
||||
isbn = []
|
||||
return isbn
|
||||
|
||||
@@ -294,7 +294,7 @@ class COinSData:
|
||||
try:
|
||||
data = source.split(f"{search}=")[1] # .split("")[0].strip()
|
||||
return data.split("rft")[0].strip() if "rft" in data else data
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
return BookData(
|
||||
@@ -319,7 +319,7 @@ class RISData:
|
||||
try:
|
||||
data = source.split(f"{search} - ")[1] # .split("")[0].strip()
|
||||
return data.split("\n")[0].strip() if "\n" in data else data
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
return BookData(
|
||||
@@ -356,7 +356,7 @@ class BibTeXData:
|
||||
.replace("[", "")
|
||||
.replace("];", "")
|
||||
)
|
||||
except:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
return BookData(
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import pathlib
|
||||
|
||||
from .semesterapparat_ui_ui import Ui_MainWindow as Ui_Semesterapparat
|
||||
|
||||
# from .dialogs import (
|
||||
# ApparatExtendDialog,
|
||||
# Mail_Dialog,
|
||||
# Settings,
|
||||
# edit_bookdata_ui,
|
||||
# login_ui,
|
||||
# medienadder_ui,
|
||||
# parsed_titles_ui,
|
||||
# popus_confirm,
|
||||
# reminder_ui,
|
||||
# About,
|
||||
# ElsaAddEntry,
|
||||
# )
|
||||
# from .widgets import (
|
||||
# FilePicker,
|
||||
# StatusWidget,
|
||||
# CalendarEntry,
|
||||
# MessageCalendar,
|
||||
# SearchStatisticPage, #
|
||||
# DataGraph,
|
||||
# ElsaDialog,
|
||||
# UserCreate,
|
||||
# EditUser,
|
||||
# EditProf,
|
||||
# )
|
||||
path = pathlib.Path(__file__).parent.absolute()
|
||||
# from .mainwindow import Ui_MainWindow as Ui_MainWindow
|
||||
# from .sap import Ui_MainWindow as MainWindow_SAP
|
||||
import pathlib
|
||||
|
||||
from .semesterapparat_ui_ui import Ui_MainWindow as Ui_Semesterapparat
|
||||
|
||||
# Explicit re-export to avoid F401 warnings
|
||||
Ui_Semesterapparat = Ui_Semesterapparat
|
||||
|
||||
# from .dialogs import (
|
||||
# ApparatExtendDialog,
|
||||
# Mail_Dialog,
|
||||
# Settings,
|
||||
# edit_bookdata_ui,
|
||||
# login_ui,
|
||||
# medienadder_ui,
|
||||
# parsed_titles_ui,
|
||||
# popus_confirm,
|
||||
# reminder_ui,
|
||||
# About,
|
||||
# ElsaAddEntry,
|
||||
# )
|
||||
# from .widgets import (
|
||||
# FilePicker,
|
||||
# StatusWidget,
|
||||
# CalendarEntry,
|
||||
# MessageCalendar,
|
||||
# SearchStatisticPage, #
|
||||
# DataGraph,
|
||||
# ElsaDialog,
|
||||
# UserCreate,
|
||||
# EditUser,
|
||||
# EditProf,
|
||||
# )
|
||||
path = pathlib.Path(__file__).parent.absolute()
|
||||
# from .mainwindow import Ui_MainWindow as Ui_MainWindow
|
||||
# from .sap import Ui_MainWindow as MainWindow_SAP
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
from .newMailTemplateDesigner_ui import Ui_Dialog as NewMailTemplateDesignerDialog
|
||||
|
||||
# Explicit re-export to avoid F401 warnings
|
||||
NewMailTemplateDesignerDialog = NewMailTemplateDesignerDialog
|
||||
|
||||
@@ -9,6 +9,12 @@ from src.services.lehmanns import LehmannsClient
|
||||
from src.services.sru import SWB
|
||||
|
||||
|
||||
def filter_prefer_swb(response):
|
||||
"""Filter function to prefer SWB results when available."""
|
||||
# This is a placeholder implementation - adjust based on actual requirements
|
||||
return response
|
||||
|
||||
|
||||
class CheckThread(QtCore.QThread):
|
||||
updateSignal = QtCore.Signal()
|
||||
total_entries_signal = QtCore.Signal(int)
|
||||
|
||||
@@ -180,7 +180,8 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
|
||||
# self.update_app_media_list()
|
||||
self.populate_prof_dropdown()
|
||||
self.populate_appfach_dropdown()
|
||||
# if the focus is changed from the prof name dropdown, set the prof data if the prof exists in the database, otherwise show a message
|
||||
# if the focus is changed from the prof name dropdown, set the prof data if the
|
||||
# prof exists in the database, otherwise show a message
|
||||
self.drpdwn_prof_name.currentIndexChanged.connect(self.set_prof_data) # type:ignore
|
||||
self.cancel_active_selection.clicked.connect(self.btn_cancel_active_selection) # type:ignore
|
||||
self.check_eternal_app.stateChanged.connect(self.set_state) # type:ignore
|
||||
|
||||
@@ -4,15 +4,36 @@ from src.database import Database
|
||||
from src.utils.icon import Icon
|
||||
|
||||
|
||||
class AdminQueryWidget(QtWidgets.QWidget, Ui_Form):
|
||||
class AdminQueryWidget(QtWidgets.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
self.setupUi()
|
||||
self.setWindowIcon(Icon("db_search").icon)
|
||||
self.db = Database()
|
||||
# Connect the button click to the method
|
||||
self.sendquery.clicked.connect(self.on_pushButton_clicked)
|
||||
|
||||
def setupUi(self):
|
||||
# Create the layout and widgets
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
# Create SQL query input area
|
||||
self.sqlquery = QtWidgets.QTextEdit()
|
||||
self.sqlquery.setPlaceholderText("Enter SQL query here...")
|
||||
|
||||
# Create execute button
|
||||
self.sendquery = QtWidgets.QPushButton("Execute Query")
|
||||
|
||||
# Create results table
|
||||
self.queryResult = QtWidgets.QTableWidget()
|
||||
self.queryResult.setColumnCount(5) # Adjust as needed
|
||||
self.queryResult.setHorizontalHeaderLabels(["Column 1", "Column 2", "Column 3", "Column 4", "Column 5"])
|
||||
|
||||
# Add widgets to layout
|
||||
layout.addWidget(self.sqlquery)
|
||||
layout.addWidget(self.sendquery)
|
||||
layout.addWidget(self.queryResult)
|
||||
|
||||
def on_pushButton_clicked(self):
|
||||
# Handle button click event
|
||||
self.queryResult.setRowCount(0) # Clear previous results
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"""Utilities for managing documentation server functionality."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from src import LOG_DIR
|
||||
|
||||
log_path = os.path.join(LOG_DIR, "web_documentation.log")
|
||||
log_path = Path(LOG_DIR) / "web_documentation.log"
|
||||
|
||||
# Replace the default StreamHandler with a FileHandler
|
||||
logging.basicConfig(
|
||||
@@ -19,13 +21,12 @@ logger = logging.getLogger(__name__) # inherits the same file handler
|
||||
docport = 8000
|
||||
|
||||
|
||||
|
||||
def start_documentation_server():
|
||||
"""
|
||||
Start the Zensical documentation server as a subprocess.
|
||||
|
||||
"""Start the Zensical documentation server as a subprocess.
|
||||
|
||||
Returns:
|
||||
subprocess.Popen: The subprocess object, or None if startup failed.
|
||||
|
||||
"""
|
||||
try:
|
||||
# Prepare subprocess arguments
|
||||
@@ -33,7 +34,7 @@ def start_documentation_server():
|
||||
if sys.platform == "win32":
|
||||
# Hide console window on Windows
|
||||
creationflags = subprocess.CREATE_NO_WINDOW
|
||||
|
||||
|
||||
# Start subprocess with all output suppressed
|
||||
process = subprocess.Popen(
|
||||
["uv", "run", "zensical", "serve"],
|
||||
@@ -41,12 +42,12 @@ def start_documentation_server():
|
||||
stderr=subprocess.DEVNULL,
|
||||
stdin=subprocess.DEVNULL,
|
||||
creationflags=creationflags,
|
||||
cwd=os.getcwd(),
|
||||
cwd=Path.cwd(),
|
||||
)
|
||||
|
||||
|
||||
logger.info(f"Documentation server started with PID {process.pid}")
|
||||
return process
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to start documentation server: {e}")
|
||||
return None
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
from os.path import basename
|
||||
from pathlib import Path
|
||||
|
||||
from docx import Document
|
||||
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
||||
@@ -69,20 +70,20 @@ class SemesterDocument:
|
||||
full: bool = False,
|
||||
):
|
||||
assert isinstance(apparats, list), SemesterError(
|
||||
"Apparats must be a list of tuples"
|
||||
"Apparats must be a list of tuples",
|
||||
)
|
||||
assert all(isinstance(apparat, tuple) for apparat in apparats), SemesterError(
|
||||
"Apparats must be a list of tuples"
|
||||
"Apparats must be a list of tuples",
|
||||
)
|
||||
assert all(isinstance(apparat[0], int) for apparat in apparats), SemesterError(
|
||||
"Apparat numbers must be integers"
|
||||
"Apparat numbers must be integers",
|
||||
)
|
||||
assert all(isinstance(apparat[1], str) for apparat in apparats), SemesterError(
|
||||
"Apparat names must be strings"
|
||||
"Apparat names must be strings",
|
||||
)
|
||||
assert isinstance(semester, str), SemesterError("Semester must be a string")
|
||||
assert "." not in filename and isinstance(filename, str), SemesterError(
|
||||
"Filename must be a string and not contain an extension"
|
||||
"Filename must be a string and not contain an extension",
|
||||
)
|
||||
self.doc = Document()
|
||||
self.apparats = apparats
|
||||
@@ -108,8 +109,7 @@ class SemesterDocument:
|
||||
log.info("Document printed")
|
||||
|
||||
def set_table_border(self, table):
|
||||
"""
|
||||
Adds a full border to the table.
|
||||
"""Adds a full border to the table.
|
||||
|
||||
:param table: Table object to which the border will be applied.
|
||||
"""
|
||||
@@ -150,7 +150,8 @@ class SemesterDocument:
|
||||
trPr = row._tr.get_or_add_trPr() # Get or add the <w:trPr> element
|
||||
trHeight = OxmlElement("w:trHeight")
|
||||
trHeight.set(
|
||||
qn("w:val"), str(int(Pt(15).pt * 20))
|
||||
qn("w:val"),
|
||||
str(int(Pt(15).pt * 20)),
|
||||
) # Convert points to twips
|
||||
trHeight.set(qn("w:hRule"), "exact") # Use "exact" for fixed height
|
||||
trPr.append(trHeight)
|
||||
@@ -233,7 +234,7 @@ class SemesterDocument:
|
||||
self.save_document(self.filename + ".docx")
|
||||
docpath = os.path.abspath(self.filename + ".docx")
|
||||
doc = word.Documents.Open(docpath)
|
||||
curdir = os.getcwd()
|
||||
curdir = Path.cwd()
|
||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||
doc.Close()
|
||||
word.Quit()
|
||||
@@ -317,7 +318,7 @@ class SemapSchilder:
|
||||
self.save_document()
|
||||
docpath = os.path.abspath(f"{self.filename}.docx")
|
||||
doc = word.Documents.Open(docpath)
|
||||
curdir = os.getcwd()
|
||||
curdir = Path.cwd()
|
||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||
doc.Close()
|
||||
word.Quit()
|
||||
|
||||
Reference in New Issue
Block a user