65 Commits

Author SHA1 Message Date
0406fe4f6f Refactor and enhance type hints across multiple modules
- Updated the `from_tuple` method in `Prof` class to specify return type.
- Added type hints for various methods in `LehmannsClient`, `OpenAI`, `WebRequest`, and `ZoteroController` classes to improve code clarity and type safety.
- Modified `pdf_to_csv` function to return a string instead of a DataFrame.
- Enhanced error handling and type hints in `wordparser` and `xmlparser` modules.
- Removed unused UI file `Ui_medianadder.ts`.
- Improved the layout and structure of the `semesterapparat_ui` to enhance user experience.
- Updated file picker to support `.doc` files in addition to `.docx`.
- Added unique item handling in `Ui` class to prevent duplicates in apparat list.
- General code cleanup and consistency improvements across various files.
2025-10-21 09:09:54 +02:00
560d8285b5 Feat: add delete edition dialog with fuzzy search 2025-10-10 09:10:06 +02:00
3cc6e793d2 more AI optimizations, reworked logger 2025-10-09 12:35:15 +02:00
7e07bdea0c commit AI suggested performance enhancements 2025-10-07 14:42:40 +02:00
06965db26a minor and major reworks: rename swb to SRU, add a test for pdf parsing
major: rework mail to send mail as plaintext instead of html, preventing the bleed-in of html text
2025-10-07 14:15:10 +02:00
0df7fd9fe6 add template for empty mail for individualized emails 2025-10-07 14:12:25 +02:00
713dbc1a1d UI: changes to new edition UIs 2025-10-07 14:11:57 +02:00
e061c1f5a9 rework threads and also use app_ids where applicable 2025-10-07 14:11:14 +02:00
8e9eff4f3a move semester dataclass into logic dir 2025-10-07 14:10:13 +02:00
6a11b3482e add icons 2025-10-07 14:09:11 +02:00
d35b2e816e UI: refactor mail template dialog for plaintext handling, improve logging, and update UI elements 2025-09-22 09:47:18 +02:00
11d5d67538 DB: add newedtion schema 2025-09-22 09:46:13 +02:00
ebf8363b2a UI: update main UI window to allow drops on document list, add support for newedition check & order 2025-09-22 09:45:51 +02:00
a2631570ec UI: allow drops for semap document list 2025-09-22 09:45:02 +02:00
9831aa3a62 UI: update templates, update code 2025-09-22 09:44:28 +02:00
c4be1d8bfa files: reorganize imports, remove print lines 2025-09-22 09:42:15 +02:00
7079b4d47f add insert, request, ordered functions for new edition books 2025-09-22 09:37:25 +02:00
65c86a65cd mail: add new mail to request new editions of books 2025-09-22 09:36:39 +02:00
f4e75831d5 rework email templates 2025-09-18 07:15:34 +02:00
4f28cfe55c add sender name to mail config dataclass 2025-09-18 07:15:15 +02:00
8b8c1c9393 chore: add logging, minor changes 2025-09-18 07:14:40 +02:00
247db562b1 add sound: error 2025-09-17 14:47:24 +02:00
1263faa23f delete files 2025-09-17 14:27:12 +02:00
fd6684cc47 add label setter and clearer 2025-09-08 10:36:57 +02:00
1ee7901d49 add eta, items/s signals, calculate values 2025-09-08 10:36:18 +02:00
e934a2b3f1 add ppn search 2025-09-08 10:35:47 +02:00
7be9dba9ca refresh displayed table entities after signatures were checked 2025-09-08 10:35:29 +02:00
6f21c22d22 UI: add eta label 2025-09-08 10:34:59 +02:00
1f34442397 rework catalogue wrapper to split entries based on space div 2025-09-08 10:34:34 +02:00
373257864f update searchpage, add updater thread and emit signal for progress bar
add context menu and related actions
2025-09-03 12:33:52 +02:00
b577a69dad add a progressbar to the statusbar which displays the search progress on searchstat page for signature update 2025-09-03 12:32:42 +02:00
a64fa9770f refactor searchBook to allow for regex matches 2025-09-03 12:32:05 +02:00
0061708785 Merge pull request 'merge main into dev' (#13) from main into dev
Reviewed-on: http://git.theprivateserver.de/PHB/SemesterapparatsManager/pulls/13
2025-09-03 09:44:13 +01:00
a3b68c2b77 Refactor initialization: streamline log and config directory creation; enhance module exports in backend and UI widgets 2025-09-03 10:42:12 +02:00
0ac5051aef Add Catalogue class for book searching and connection handling 2025-09-03 10:41:40 +02:00
bf419ec3bf Implement startup check and table creation in Database class; add methods to retrieve books and manage apparat deletion 2025-09-03 10:41:26 +02:00
c6f356fda4 Refactor user interface: enhance sound playback functionality and integrate signature update process,
fix broken database calls by using the app_id instead of the previously used appnr
2025-09-03 10:40:57 +02:00
087b8753fb Add progress dialog and threading for signature updates 2025-09-03 10:39:36 +02:00
09ec304637 Refactor WebRequest class: improve location handling and logging for Semesterapparat 2025-09-03 10:38:59 +02:00
f6ab64a8ee UI-Signatures: implement signature update functionality with progress tracking 2025-09-03 10:37:44 +02:00
4254567bfb delete faulty files 2025-09-03 10:37:18 +02:00
9ce46abdce UI-Signatures: add selector for signaturecheck 2025-09-03 10:36:56 +02:00
8cce13f6e5 UI-Statistics: add button for mass extend, add extend functionality 2025-09-03 10:36:07 +02:00
f22cbcd26a UI: add new admin option 2025-09-03 10:34:25 +02:00
6f22186b67 add ding sound 2025-09-03 10:33:54 +02:00
a231213276 switch from chardet to charset-normalizer 2025-09-03 10:33:39 +02:00
b344d806e2 refactor: reorganize imports and enhance logging setup; improve book processing logic in NewEditionCheckerThread 2025-09-03 10:33:15 +02:00
0e3199e289 add warning log to fix crashing bug 2025-09-03 10:32:39 +02:00
c00eb102ff remove qtqdm dependency, switch from chardet to charset-normalizer 2025-09-03 10:32:17 +02:00
63b2a1b7a3 Implement code changes to enhance functionality and improve performance 2025-09-01 14:33:02 +02:00
5a4156ba04 feat: enhance user interface with new edition checking functionality and sound notifications 2025-09-01 14:32:33 +02:00
af53b0310f refactor: update import statement and enhance word_to_semap function with AI parameter 2025-09-01 14:32:05 +02:00
ce7d22b26b feat: add from_LehmannsSearchResult method to BookData for processing Lehmanns search results 2025-09-01 14:31:46 +02:00
5f15352401 feat: implement NewEditionCheckerThread and related utility functions for book data processing 2025-09-01 14:31:23 +02:00
7da2b3f65d feat: add getProfMailById method to retrieve professor's email by ID
refactor: reorganize import statements and clean up commented code
2025-09-01 14:31:02 +02:00
5bf5eeae00 add APIs to parse data from SWB and Lehmanns 2025-09-01 14:30:37 +02:00
c6cbb1d825 refactor: reorganize imports and enhance Mail_Dialog to handle accepted_books 2025-09-01 14:30:10 +02:00
3bfb788f42 refactor: update __all__ exports and reorganize imports in __init__.py 2025-09-01 14:29:50 +02:00
1c5dfc8f3e sort imports 2025-09-01 14:29:32 +02:00
4c26aa8d21 fix: correct parameter name in QFileDialog method 2025-09-01 14:29:17 +02:00
b67a160e7a add sound to be played once check complete 2025-09-01 14:28:59 +02:00
d8fabdbe11 add progressbar for checking editions 2025-09-01 14:28:39 +02:00
ee8ea9dfda add files for edition checker 2025-09-01 14:28:05 +02:00
ec0f72337d add ui files for edition checker 2025-09-01 14:27:48 +02:00
6ae52b6626 add new mail template 2025-09-01 14:27:20 +02:00
125 changed files with 7519 additions and 8070 deletions

View File

@@ -50,6 +50,7 @@ class Mail:
smtp_server: str smtp_server: str
port: int port: int
sender: str sender: str
sender_name: str
password: str password: str
use_user_name: bool use_user_name: bool
printer_mail: str printer_mail: str

1
icons/manage_search.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M80-200v-80h400v80H80Zm0-200v-80h200v80H80Zm0-200v-80h200v80H80Zm744 400L670-354q-24 17-52.5 25.5T560-320q-83 0-141.5-58.5T360-520q0-83 58.5-141.5T560-720q83 0 141.5 58.5T760-520q0 29-8.5 57.5T726-410l154 154-56 56ZM560-400q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35Z"/></svg>

After

Width:  |  Height:  |  Size: 420 B

1
icons/search_results.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M400-320q100 0 170-70t70-170q0-100-70-170t-170-70q-100 0-170 70t-70 170q0 100 70 170t170 70Zm-40-120v-280h80v280h-80Zm-140 0v-200h80v200h-80Zm280 0v-160h80v160h-80ZM824-80 597-307q-41 32-91 49.5T400-240q-134 0-227-93T80-560q0-134 93-227t227-93q134 0 227 93t93 227q0 56-17.5 106T653-363l227 227-56 56Z"/></svg>

After

Width:  |  Height:  |  Size: 425 B

1
icons/trash.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>

After

Width:  |  Height:  |  Size: 319 B

View File

@@ -0,0 +1,13 @@
Subject: Bitte um Bestellung von Neuerwerbungen für Semesterapparat {AppNr} - {AppName}
Hallo zusammen,
für den Semesterapparat {AppNr} - {Appname} wurden folgende Neuauflagen gefunden:
{newEditionsOrdered}
Wäre es möglich, diese, oder neuere Auflagen (wenn vorhanden), zu bestellen?
{signature}

View File

@@ -1,54 +0,0 @@
Message-ID: <987b46cf-2d8b-4a27-acb3-c50f61d3d85d@ph-freiburg.de>
Date: Tue, 31 Oct 2023 11:38:34 +0100
MIME-Version: 1.0
User-Agent: Mozilla Thunderbird
From: Alexander Kirchner <alexander.kirchner@ph-freiburg.de>
Subject: =?UTF-8?Q?Information_bez=C3=BCglich_der_Aufl=C3=B6sung_des_Semeste?=
=?UTF-8?Q?rapparates_=7BAppNr=7D?=
Content-Language: de-DE
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
attachmentreminder=0; deliveryformat=0
X-Identity-Key: id1
Fcc: imap://aky547@imap.ph-freiburg.de/INBOX/Sent
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<p>Sehr geehrte/r {Profname}, <br>
</p>
<p><br>
</p>
auf die E-Mail bezüglich der Auflösung oder Verlängerung der
Semesterapparate haben wir von Ihnen keine Rückmeldung erhalten.
Deshalb gehen wir davon aus, dass der Apparat aufgelöst werden kann.
Die Medien, die im Apparat aufgestellt waren, werden nun wieder
regulär ausleihbar und sind dann an ihren Standorten bei den Fächern
zu finden. <br>
<br>
Falls Sie den Apparat erneut, oder einen neuen Apparat anlegen
wollen, können Sie mir das ausgefüllte Formular zur Einrichtung des
Apparates (<a class="moz-txt-link-freetext"
href="https://www.ph-freiburg.de/bibliothek/lernen/semesterapparate/info-lehrende-sem.html">https://www.ph-freiburg.de/bibliothek/lernen/semesterapparate/info-lehrende-sem.html</a>)
zukommen lassen. Im Falle einer Verlängerung des Apparates reicht
eine Antwort auf diese Mail.
<p><br>
</p>
<p>Bei Fragen können Sie sich jederzeit an mich wenden.<br>
</p>
<p><br>
</p>
<pre class="moz-signature" cols="72">--
Freundliche Grüße
Alexander Kirchner
Bibliothek der Pädagogischen Hochschule Freiburg
Tel. 0761/682-778</pre>
</body>
</html>

View File

@@ -1,37 +1,17 @@
Message-ID: <b44248a9-025e-e86c-85d7-5949534f0ac4@ph-freiburg.de>
Date: Mon, 17 Jul 2023 12:59:04 +0200
MIME-Version: 1.0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101
Thunderbird/102.13.0
Content-Language: de-DE
From: {user_name} <{user_mail}>
Subject: =?UTF-8?Q?Information_bez=c3=bcglich_der_Aufl=c3=b6sung_des_Semeste?=
=?UTF-8?Q?rapparates_=7bAppNr=7d?=
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
attachmentreminder=0; deliveryformat=0
X-Identity-Key: id1
Fcc: imap://aky547@imap.ph-freiburg.de/INBOX/Sent
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
<html> Subject: Information bezüglich der Auflösung des Semesterapparates {AppNr}
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head> {greeting}
<body>{greeting}
<br> auf die E-Mail bezüglich der Auflösung oder Verlängerung der Semesterapparate haben wir von Ihnen keine Rückmeldung erhalten. Deshalb gehen wir davon aus, dass der Apparat aufgelöst werden kann.
<p>auf die E-Mail bezüglich der Auflösung oder Verlängerung der Semesterapparate haben wir von Ihnen keine Rückmeldung erhalten. Deshalb gehen wir davon aus, dass der Apparat aufgelöst werden kann.</p> Die Medien, die in den Apparaten aufgestellt waren, werden nun wieder regulär ausleihbar und sind dann an ihren Standorten bei den Fächern zu finden.
<p> Die Medien, die in den Apparaten aufgestellt waren, werden nun wieder regulär ausleihbar und sind dann an ihren Standorten bei den Fächern zu finden.</p>
<p></p> Falls Sie den Apparat erneut, oder einen neuen Apparat anlegen wollen,
<p>Falls Sie den Apparat erneut, oder einen neuen Apparat anlegen wollen, können Sie mir das ausgefüllte Formular zur Einrichtung des Apparates (<a class="moz-txt-link-freetext" href="https://www.ph-freiburg.de/bibliothek/lernen/semesterapparate/info-lehrende-sem.html">https://www.ph-freiburg.de/bibliothek/lernen/semesterapparate/info-lehrende-sem.html</a>) zukommen lassen.</p> können Sie mir das ausgefüllte Formular zur Einrichtung des Apparates
<p>Im Falle einer Verlängerung des Apparates reicht eine Antwort auf diese Mail.<br> https://www.ph-freiburg.de/bibliothek/lernen/semesterapparate/info-lehrende-sem.html
</p> zukommen lassen. Im Falle einer Verlängerung des Apparates reicht eine Antwort auf diese Mail.
<p>Bei Fragen können Sie sich jederzeit an mich wenden.<br>
</p> Bei Fragen können Sie sich jederzeit an mich wenden.
<p><br>
</p>
<pre class="moz-signature" cols="72">--
{signature} {signature}
</pre>
</body>
</html>

View File

@@ -1,36 +1,16 @@
Message-ID: <db617c48-29d6-d3d8-a67c-e9a6cf9b5bdb@ph-freiburg.de>
Date: Tue, 12 Sep 2023 13:01:35 +0200
MIME-Version: 1.0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101
Thunderbird/102.15.0
From: Alexander Kirchner <alexander.kirchner@ph-freiburg.de>
Subject: Information zum Semesterapparat {AppNr} - {Appname}
Content-Language: de-DE
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0;
attachmentreminder=0; deliveryformat=0
X-Identity-Key: id1
Fcc: imap://aky547@imap.ph-freiburg.de/INBOX/Sent
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
<html> Subject: Information zum Semesterapparat {AppNr} - {AppName}
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head> {greeting}
<body>{greeting}
<br> Ihr Semesterapparat {Appname} wurde angelegt.
<p>Ihr Semesterapparat {Appname} wurde angelegt.</p> Unter folgendem Link können Sie die Apparate einsehen:
<p>Unter folgendem Link können Sie die Apparate einsehen:</p> https://bsz.ibs-bw.de/aDISWeb/app?service=direct/0/Home/$DirectLink&amp;sp=SOPAC42&amp;sp=SWI00000002&amp;noRedir
<p><a class="moz-txt-link-freetext" href="https://bsz.ibs-bw.de/aDISWeb/app?service=direct/0/Home/$DirectLink&amp;sp=SOPAC42&amp;sp=SWI00000002&amp;noRedir">https://bsz.ibs-bw.de/aDISWeb/app?service=direct/0/Home/$DirectLink&amp;sp=SOPAC42&amp;sp=SWI00000002&amp;noRedir</a></p>
<p>Ihr Apparat ist unter {AppSubject} > {Profname} > {AppNr} {Appname}.<br> Ihr Apparat ist unter {AppSubject} > {Profname} > {AppNr} {Appname}
</p>
<p><br> Noch nicht vorhandene Medien wurden vorgemerkt und werden nach Rückkehr in die Bibliothek eingearbeitet.
</p> Bei Fragen können Sie sich per Mail bei mir melden.
<p>Noch nicht vorhandene Medien wurden vorgemerkt und werden nach Rückkehr in die Bibliothek eingearbeitet.</p>
<p>Bei Fragen können Sie sich per Mail bei mir melden.<br>
</p>
<pre class="moz-signature" cols="72">--
{signature} {signature}
</pre>
</body>
</html>

View File

@@ -0,0 +1,10 @@
Subject: Information zur Auflösung des Semesterapparates {AppNr} - {Appname}
{greeting}
Ihr Semesterapparat "{Appname} ({AppNr})" wurde wie besprochen aufgelöst.
Die Medien sind von nun an wieder in den Regalen zu finden.
{signature}

View File

@@ -1,18 +0,0 @@
Subject: Information zur Auflösung des Semesterapparates {AppNr} - {Appname}
MIME-Version: 1.0
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: 8bit
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{greeting}</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ihr Semesterapparat "{Appname} ({AppNr})" wurde wie besprochen aufgelöst. </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Die Medien sind von nun an wieder in den Regalen zu finden.</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><pre class="moz-signature" cols="72">-- </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{signature}</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></pre></p></body></html>

View File

@@ -0,0 +1,15 @@
Subject: Neuauflagen für Semesterapparat {AppNr} - {AppName}
{greeting}
Für Ihren Semesterapparat {AppNr} - {Appname} wurden folgende Neuauflagen gefunden:
{newEditions}
Sollen wir die alte(n) Auflage(n) aus dem Apparat durch diese austauschen?
Nicht vorhandene Exemplare werden an die Erwerbungsabteilung weitergegeben
und nach Erhalt der Medien in den Apparat eingearbeitet.
{signature}

View File

@@ -0,0 +1,9 @@
Subject: CHANGEME
{greeting}
{signature}

11
main.py
View File

@@ -1,11 +1,14 @@
from src import first_launch, settings
from src.ui.widgets.welcome_wizard import launch_wizard as startup
from PySide6 import QtWidgets
import sys import sys
from src.ui.userInterface import launch_gui as UI
from PySide6 import QtWidgets
from src import first_launch, settings
from src.shared.logging import configure
from src.ui.userInterface import launch_gui as UI
from src.ui.widgets.welcome_wizard import launch_wizard as startup
if __name__ == "__main__": if __name__ == "__main__":
configure("INFO")
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
if not first_launch: if not first_launch:
setup = startup() setup = startup()

View File

@@ -3,15 +3,16 @@ name = "semesterapparatsmanager"
version = "1.0.0" version = "1.0.0"
description = "Add your description here" description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.13"
dependencies = [ dependencies = [
"appdirs>=1.4.4", "appdirs>=1.4.4",
"beautifulsoup4>=4.12.3", "beautifulsoup4>=4.13.5",
"bump-my-version>=0.29.0", "bump-my-version>=0.29.0",
"chardet>=5.2.0", "charset-normalizer>=3.4.3",
"comtypes>=1.4.9", "comtypes>=1.4.9",
"darkdetect>=0.8.0", "darkdetect>=0.8.0",
"docx2pdf>=0.1.8", "docx2pdf>=0.1.8",
"httpx>=0.28.1",
"loguru>=0.7.3", "loguru>=0.7.3",
"mkdocs>=1.6.1", "mkdocs>=1.6.1",
"mkdocs-material>=9.5.49", "mkdocs-material>=9.5.49",
@@ -35,6 +36,9 @@ dev = [
"icecream>=2.1.4", "icecream>=2.1.4",
"nuitka>=2.5.9", "nuitka>=2.5.9",
] ]
swbtest = [
"alive-progress>=3.3.0",
]
[tool.bumpversion] [tool.bumpversion]
current_version = "1.0.0" current_version = "1.0.0"
@@ -61,3 +65,7 @@ post_commit_hooks = []
filename = "src/__init__.py" filename = "src/__init__.py"
[[tool.bumpversion.files]] [[tool.bumpversion.files]]
filename = ".version" filename = ".version"
[[tool.uv.index]]
url = "https://git.theprivateserver.de/api/packages/WorldTeacher/pypi/simple/"
default = false

View File

@@ -3,30 +3,31 @@ __author__ = "Alexander Kirchner"
__all__ = ["__version__", "__author__", "Icon", "settings"] __all__ = ["__version__", "__author__", "Icon", "settings"]
import os import os
from pathlib import Path
from typing import Union
from appdirs import AppDirs from appdirs import AppDirs
from config import Config from config import Config
app = AppDirs("SemesterApparatsManager", "SAM") app = AppDirs("SemesterApparatsManager", "SAM")
LOG_DIR = app.user_log_dir LOG_DIR: str = app.user_log_dir # type: ignore
CONFIG_DIR = app.user_config_dir CONFIG_DIR: str = app.user_config_dir # type: ignore
if not os.path.exists(LOG_DIR): if not os.path.exists(LOG_DIR): # type: ignore
os.makedirs(LOG_DIR) os.makedirs(LOG_DIR) # type: ignore
if not os.path.exists(CONFIG_DIR): if not os.path.exists(CONFIG_DIR): # type: ignore
os.makedirs(CONFIG_DIR) os.makedirs(CONFIG_DIR) # type: ignore
settings = Config(f"{CONFIG_DIR}/config.yaml") settings = Config(f"{CONFIG_DIR}/config.yaml")
DATABASE_DIR = ( DATABASE_DIR: Union[Path, str] = ( # type: ignore
app.user_config_dir if settings.database.path is None else settings.database.path app.user_config_dir if settings.database.path is None else settings.database.path # type: ignore
) )
if not os.path.exists(DATABASE_DIR): if not os.path.exists(DATABASE_DIR): # type: ignore
os.makedirs(DATABASE_DIR) os.makedirs(DATABASE_DIR) # type: ignore
first_launch = settings.exists first_launch = settings.exists
if not os.path.exists(settings.database.temp.expanduser()): if not os.path.exists(settings.database.temp.expanduser()): # type: ignore
settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True) settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True) # type: ignore
from .utils.icon import Icon from .utils.icon import Icon
if not os.path.exists("logs"): if not os.path.exists("logs"):

View File

@@ -1,8 +1,22 @@
from .semester import Semester __all__ = [
from .database import Database "AdminCommands",
"AutoAdder",
"AvailChecker",
"BookGrabber",
"Database",
"DocumentationThread",
"NewEditionCheckerThread",
"recreateElsaFile",
"recreateFile",
"Catalogue",
]
from .admin_console import AdminCommands from .admin_console import AdminCommands
from .thread_bookgrabber import BookGrabber from .catalogue import Catalogue
from .threads_availchecker import AvailChecker from .create_file import recreateElsaFile, recreateFile
from .threads_autoadder import AutoAdder from .database import Database
from .documentation_thread import DocumentationThread from .documentation_thread import DocumentationThread
from .create_file import recreateFile, recreateElsaFile from .thread_bookgrabber import BookGrabber
from .thread_neweditions import NewEditionCheckerThread
from .threads_autoadder import AutoAdder
from .threads_availchecker import AvailChecker

292
src/backend/catalogue.py Normal file
View File

@@ -0,0 +1,292 @@
from typing import List
import regex
import requests
from bs4 import BeautifulSoup
from src.logic import BookData as Book
from src.shared.logging import log
URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND"
BASE = "https://rds.ibs-bw.de"
class Catalogue:
def __init__(self, timeout=15):
self.timeout = timeout
reachable = self.check_connection()
if not reachable:
log.error("No internet connection available.")
raise ConnectionError("No internet connection available.")
def check_connection(self):
try:
response = requests.get("https://www.google.com", timeout=self.timeout)
if response.status_code == 200:
return True
except requests.exceptions.RequestException as e:
log.error(f"Could not connect to google.com: {e}")
def search_book(self, searchterm: str):
response = requests.get(URL.format(searchterm), timeout=self.timeout)
return response.text
def search(self, link: str):
response = requests.get(link, timeout=self.timeout)
return response.text
def get_book_links(self, searchterm: str) -> List[str]:
response = self.search_book(searchterm)
soup = BeautifulSoup(response, "html.parser")
links = soup.find_all("a", class_="title getFull")
res: List[str] = []
for link in links:
res.append(BASE + link["href"]) # type: ignore
return res
def get_book(self, searchterm: str):
log.info(f"Searching for term: {searchterm}")
links = self.get_book_links(searchterm)
print(links)
for elink in links:
result = self.search(elink)
# in result search for class col-xs-12 rds-dl RDS_LOCATION
# if found, return text of href
soup = BeautifulSoup(result, "html.parser")
# Optional (unchanged): title and ppn if you need them
title_el = soup.find("div", class_="headline text")
title = title_el.get_text(strip=True) if title_el else None
ppn_el = soup.find(
"div", class_="col-xs-12 col-md-5 col-lg-4 rds-dl-head RDS_PPN"
)
# in ppn_el, get text of div col-xs-12 col-md-7 col-lg-8 rds-dl-panel
ppn = (
ppn_el.find_next_sibling(
"div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel"
).get_text(strip=True)
if ppn_el
else None
)
# get edition text at div class col-xs-12 col-md-5 col-lg-4 rds-dl-head RDS_EDITION
edition_el = soup.find(
"div", class_="col-xs-12 col-md-5 col-lg-4 rds-dl-head RDS_EDITION"
)
edition = (
edition_el.find_next_sibling(
"div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel"
).get_text(strip=True)
if edition_el
else None
)
authors = soup.find_all(
"div", class_="col-xs-12 col-md-5 col-lg-4 rds-dl-head RDS_PERSON"
)
author = None
if authors:
# get the names of the a href links in the div col-xs-12 col-md-7 col-lg-8 rds-dl-panel
author_names = []
for author in authors:
panel = author.find_next_sibling(
"div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel"
)
if panel:
links = panel.find_all("a")
for link in links:
author_names.append(link.text.strip())
author = (
";".join(author_names) if len(author_names) > 1 else author_names[0]
)
signature = None
panel = soup.select_one("div.panel-body")
if panel:
# Collect the RDS_* blocks in order, using the 'space' divs as separators
groups = []
cur = {}
for node in panel.select(
"div.rds-dl.RDS_SIGNATURE, div.rds-dl.RDS_STATUS, div.rds-dl.RDS_LOCATION, div.col-xs-12.space"
):
classes = node.get("class", [])
# Separator between entries
if "space" in classes:
if cur:
groups.append(cur)
cur = {}
continue
# Read the value from the corresponding panel cell
val_el = node.select_one(".rds-dl-panel")
val = (
val_el.get_text(" ", strip=True)
if val_el
else node.get_text(" ", strip=True)
)
if "RDS_SIGNATURE" in classes:
cur["signature"] = val
elif "RDS_STATUS" in classes:
cur["status"] = val
elif "RDS_LOCATION" in classes:
cur["location"] = val
if cur: # append the last group if not followed by a space
groups.append(cur)
# Find the signature for the entry whose location mentions "Semesterapparat"
for g in groups:
loc = g.get("location", "").lower()
if "semesterapparat" in loc:
signature = g.get("signature")
return Book(
title=title,
ppn=ppn,
signature=signature,
library_location=loc.split("-")[-1],
link=elink,
author=author,
edition=edition,
)
else:
return Book(
title=title,
ppn=ppn,
signature=signature,
library_location=loc.split("\n\n")[-1],
link=elink,
author=author,
edition=edition,
)
def get(self, ppn: str) -> Book | None:
# based on PPN, get title, people, edition, year, language, pages, isbn,
link = f"https://rds.ibs-bw.de/phfreiburg/opac/RDSIndexrecord/{ppn}"
result = self.search(link)
soup = BeautifulSoup(result, "html.parser")
def get_ppn(self, searchterm: str) -> str | None:
links = self.get_book_links(searchterm)
ppn = None
for link in links:
result = self.search(link)
soup = BeautifulSoup(result, "html.parser")
print(link)
ppn = link.split("/")[-1]
if ppn and regex.match(r"^\d{8,10}[X\d]?$", ppn):
return ppn
return ppn
def get_semesterapparat_number(self, searchterm: str) -> int:
links = self.get_book_links(searchterm)
for link in links:
result = self.search(link)
# in result search for class col-xs-12 rds-dl RDS_LOCATION
# if found, return text of href
soup = BeautifulSoup(result, "html.parser")
locations = soup.find_all("div", class_="col-xs-12 rds-dl RDS_LOCATION")
for location_el in locations:
if "Semesterapparat-" in location_el.text:
match = regex.search(r"Semesterapparat-(\d+)", location_el.text)
if match:
return int(match.group(1))
if "Handbibliothek-" in location_el.text:
return location_el.text.strip().split("\n\n")[-1].strip()
return location_el.text.strip().split("\n\n")[-1].strip()
return 0
def get_author(self, link: str) -> str:
links = self.get_book_links(f"kid:{link}")
author = None
for link in links:
# print(link)
result = self.search(link)
soup = BeautifulSoup(result, "html.parser")
# get all authors, return them as a string seperated by ;
authors = soup.find_all(
"div", class_="col-xs-12 col-md-5 col-lg-4 rds-dl-head RDS_PERSON"
)
if authors:
# get the names of the a href links in the div col-xs-12 col-md-7 col-lg-8 rds-dl-panel
author_names = []
for author in authors:
panel = author.find_next_sibling(
"div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel"
)
if panel:
links = panel.find_all("a")
for link in links:
author_names.append(link.text.strip())
author = "; ".join(author_names)
return author
def get_signature(self, isbn: str):
links = self.get_book_links(f"{isbn}")
signature = None
for link in links:
result = self.search(link)
soup = BeautifulSoup(result, "html.parser")
panel = soup.select_one("div.panel-body")
if panel:
# Collect the RDS_* blocks in order, using the 'space' divs as separators
groups = []
cur = {}
for node in panel.select(
"div.rds-dl.RDS_SIGNATURE, div.rds-dl.RDS_STATUS, div.rds-dl.RDS_LOCATION, div.col-xs-12.space"
):
classes = node.get("class", [])
# Separator between entries
if "space" in classes:
if cur:
groups.append(cur)
cur = {}
continue
# Read the value from the corresponding panel cell
val_el = node.select_one(".rds-dl-panel")
val = (
val_el.get_text(" ", strip=True)
if val_el
else node.get_text(" ", strip=True)
)
if "RDS_SIGNATURE" in classes:
cur["signature"] = val
elif "RDS_STATUS" in classes:
cur["status"] = val
elif "RDS_LOCATION" in classes:
cur["location"] = val
if cur: # append the last group if not followed by a space
groups.append(cur)
# Find the signature for the entry whose location mentions "Semesterapparat"
for g in groups:
print(g)
loc = g.get("location", "").lower()
if "semesterapparat" in loc:
signature = g.get("signature")
return signature
else:
signature = g.get("signature")
return signature
print("No signature found")
return signature
def in_library(self, ppn: str) -> bool:
if ppn is None:
return False
links = self.get_book_links(f"kid:{ppn}")
return len(links) > 0
def get_location(self, ppn: str) -> str | None:
if ppn is None:
return None
link = self.get_book(f"{ppn}")
if link is None:
return None
return link.library_location

View File

@@ -1,8 +1,8 @@
import datetime import datetime
import json import json
import os import os
import re
import sqlite3 as sql import sqlite3 as sql
import sys
import tempfile import tempfile
from dataclasses import asdict from dataclasses import asdict
from pathlib import Path from pathlib import Path
@@ -12,7 +12,7 @@ from typing import Any, List, Optional, Tuple, Union
import loguru import loguru
from src import LOG_DIR, settings, DATABASE_DIR from src import DATABASE_DIR, settings
from src.backend.db import ( from src.backend.db import (
CREATE_ELSA_FILES_TABLE, CREATE_ELSA_FILES_TABLE,
CREATE_ELSA_MEDIA_TABLE, CREATE_ELSA_MEDIA_TABLE,
@@ -21,6 +21,7 @@ from src.backend.db import (
CREATE_TABLE_FILES, CREATE_TABLE_FILES,
CREATE_TABLE_MEDIA, CREATE_TABLE_MEDIA,
CREATE_TABLE_MESSAGES, CREATE_TABLE_MESSAGES,
CREATE_TABLE_NEWEDITIONS,
CREATE_TABLE_PROF, CREATE_TABLE_PROF,
CREATE_TABLE_SUBJECTS, CREATE_TABLE_SUBJECTS,
CREATE_TABLE_USER, CREATE_TABLE_USER,
@@ -28,15 +29,10 @@ from src.backend.db import (
from src.errors import AppPresentError, NoResultError from src.errors import AppPresentError, NoResultError
from src.logic import ELSA, Apparat, ApparatData, BookData, Prof from src.logic import ELSA, Apparat, ApparatData, BookData, Prof
from src.logic.constants import SEMAP_MEDIA_ACCOUNTS from src.logic.constants import SEMAP_MEDIA_ACCOUNTS
from src.logic.semester import Semester
from src.utils.blob import create_blob from src.utils.blob import create_blob
from .semester import Semester
log = loguru.logger log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
ascii_lowercase = lower + digits + punctuation ascii_lowercase = lower + digits + punctuation
@@ -68,6 +64,60 @@ class Database:
self.db_path = db_path self.db_path = db_path
log.debug(f"Database path: {self.db_path}") log.debug(f"Database path: {self.db_path}")
self.db_initialized = False self.db_initialized = False
self.startup_check()
def startup_check(self):
# check existence of all tables. if any is missing, recreate the table
if not self.db_initialized:
self.initializeDatabase()
tables = self.get_db_contents()
tables = [t[1] for t in tables] if tables is not None else []
required_tables = [
"semesterapparat",
"messages",
"media",
"files",
"prof",
"user",
"subjects",
"elsa",
"elsa_files",
"elsa_media",
"neweditions",
]
for table in required_tables:
if table not in tables:
log.critical(f"Table {table} is missing, recreating...")
self.create_table(table)
def create_table(self, table_name: str):
match table_name:
case "semesterapparat":
query = CREATE_TABLE_APPARAT
case "messages":
query = CREATE_TABLE_MESSAGES
case "media":
query = CREATE_TABLE_MEDIA
case "files":
query = CREATE_TABLE_FILES
case "prof":
query = CREATE_TABLE_PROF
case "user":
query = CREATE_TABLE_USER
case "subjects":
query = CREATE_TABLE_SUBJECTS
case "elsa":
query = CREATE_ELSA_TABLE
case "elsa_files":
query = CREATE_ELSA_FILES_TABLE
case "elsa_media":
query = CREATE_ELSA_MEDIA_TABLE
case "neweditions":
query = CREATE_TABLE_NEWEDITIONS
case _:
log.error(f"Table {table_name} is not a valid table name")
self.query_db(query)
def initializeDatabase(self): def initializeDatabase(self):
if not self.db_initialized: if not self.db_initialized:
@@ -94,7 +144,7 @@ class Database:
self.create_tables() self.create_tables()
self.insertSubjects() self.insertSubjects()
def getElsaMediaID(self, work_author, signature, pages): def getElsaMediaID(self, work_author: str, signature: str, pages: str):
query = ( query = (
"SELECT id FROM elsa_media WHERE work_author=? AND signature=? AND pages=?" "SELECT id FROM elsa_media WHERE work_author=? AND signature=? AND pages=?"
) )
@@ -110,7 +160,7 @@ class Database:
query = "SELECT type FROM elsa_media WHERE id=?" query = "SELECT type FROM elsa_media WHERE id=?"
return self.query_db(query, (id,), one=True)[0] return self.query_db(query, (id,), one=True)[0]
def get_db_contents(self) -> Union[List[Tuple], None]: def get_db_contents(self) -> Union[List[Tuple[Any]], None]:
""" """
Get the contents of the Get the contents of the
@@ -132,7 +182,13 @@ class Database:
Returns: Returns:
sql.Connection: The active connection to the database sql.Connection: The active connection to the database
""" """
return sql.connect(self.db_path) conn = sql.connect(self.db_path)
# Fast pragmas suitable for a desktop app DB
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA synchronous=NORMAL;")
conn.execute("PRAGMA temp_store=MEMORY;")
conn.execute("PRAGMA mmap_size=134217728;") # 128MB
return conn
def close_connection(self, conn: sql.Connection): def close_connection(self, conn: sql.Connection):
""" """
@@ -160,6 +216,25 @@ class Database:
cursor.execute(CREATE_ELSA_TABLE) cursor.execute(CREATE_ELSA_TABLE)
cursor.execute(CREATE_ELSA_FILES_TABLE) cursor.execute(CREATE_ELSA_FILES_TABLE)
cursor.execute(CREATE_ELSA_MEDIA_TABLE) cursor.execute(CREATE_ELSA_MEDIA_TABLE)
# Helpful indices to speed up frequent lookups and joins
cursor.execute(
"CREATE INDEX IF NOT EXISTS idx_media_app_prof ON media(app_id, prof_id);"
)
cursor.execute(
"CREATE INDEX IF NOT EXISTS idx_media_deleted ON media(deleted);"
)
cursor.execute(
"CREATE INDEX IF NOT EXISTS idx_media_available ON media(available);"
)
cursor.execute(
"CREATE INDEX IF NOT EXISTS idx_messages_remind_at ON messages(remind_at);"
)
cursor.execute(
"CREATE INDEX IF NOT EXISTS idx_semesterapparat_prof ON semesterapparat(prof_id);"
)
cursor.execute(
"CREATE INDEX IF NOT EXISTS idx_semesterapparat_appnr ON semesterapparat(appnr);"
)
conn.commit() conn.commit()
self.close_connection(conn) self.close_connection(conn)
@@ -173,7 +248,7 @@ class Database:
""" """
conn = self.connect() conn = self.connect()
cursor = conn.cursor() cursor = conn.cursor()
log.debug(f"Inserting {params} into database with query {query}") log.debug(f"Inserting into DB: {query}")
cursor.execute(query, params) cursor.execute(query, params)
conn.commit() conn.commit()
self.close_connection(conn) self.close_connection(conn)
@@ -182,7 +257,7 @@ class Database:
def query_db( def query_db(
self, self,
query: str, query: str,
args: Tuple[Any, Any] = (), # type:ignore args: Tuple[Any] = (), # type:ignore
one: bool = False, # type:ignore one: bool = False, # type:ignore
) -> Union[Tuple[Any, Any], List[Tuple[Any, Any]]]: ) -> Union[Tuple[Any, Any], List[Tuple[Any, Any]]]:
""" """
@@ -201,12 +276,12 @@ class Database:
logs_query = query logs_query = query
logs_args = args logs_args = args
if "fileblob" in query: # if "fileblob" in query:
# set fileblob arg in logger to "too long" # # set fileblob arg in logger to "too long"
logs_query = query # logs_query = query
fileblob_location = query.find("fileblob") # fileblob_location = query.find("fileblob")
# remove fileblob from query # # remove fileblob from query
logs_query = query[:fileblob_location] + "fileblob = too long" # logs_query = query[:fileblob_location] + "fileblob = too long"
log_message = f"Querying database with query {logs_query}, args: {logs_args}" log_message = f"Querying database with query {logs_query}, args: {logs_args}"
# if "INSERT" in query: # if "INSERT" in query:
@@ -332,49 +407,66 @@ class Database:
""" """
return self.query_db("SELECT id FROM media ORDER BY id DESC", one=True)[0] 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]]: def searchBook(
self, data: dict[str, str]
) -> Optional[list[tuple["BookData", int, int]]]:
""" """
Search a book in the database based on the sent data. Search a book in the database using regex against signature/title.
Args: Args:
data (dict[str, str]): A dictionary containing the data to be searched for. The dictionary can contain the following: data: may contain:
- signature: The signature of the book - "signature": regex to match against BookData.signature
- title: The title of the book - "title": regex to match against BookData.title
Returns: Returns:
list[tuple[BookData, int]]: A list of tuples containing the wrapped Metadata and the id of the book list of (BookData, app_id, prof_id) tuples, or None if invalid args
""" """
rdata = self.query_db("SELECT * FROM media WHERE deleted=0")
# log.debug(rdata, len(rdata)) # Determine mode (kept compatible with your original logic)
mode = 0 mode = 0
if len(data) == 1: if len(data) == 1 and "signature" in data:
if "signature" in data.keys(): mode = 1
mode = 1 elif len(data) == 1 and "title" in data:
elif "title" in data.keys(): mode = 2
mode = 2 elif len(data) == 2 and "signature" in data and "title" in data:
elif len(data) == 2:
mode = 3 mode = 3
else: else:
return None return None
ret = []
for book in rdata: def _compile(expr: str) -> re.Pattern:
bookdata = BookData().from_string(book[1]) try:
app_id = book[2] return re.compile(expr, re.IGNORECASE | re.UNICODE)
prof_id = book[3] except re.error:
# If user provided a broken regex, treat it as a literal
return re.compile(re.escape(expr), re.IGNORECASE | re.UNICODE)
sig_re = _compile(data["signature"]) if mode in (1, 3) else None
title_re = _compile(data["title"]) if mode in (2, 3) else None
# Fetch candidates once
rows = self.query_db("SELECT * FROM media WHERE deleted=0")
results: list[tuple["BookData", int, int]] = []
for row in rows:
bookdata = BookData().from_string(
row[1]
) # assumes row[1] is the serialized bookdata
app_id = row[2]
prof_id = row[3]
sig_val = bookdata.signature
title_val = bookdata.title
if mode == 1: if mode == 1:
if data["signature"] in bookdata.signature: if sig_re.search(sig_val):
ret.append((bookdata, app_id, prof_id)) results.append((bookdata, app_id, prof_id))
elif mode == 2: elif mode == 2:
if data["title"] in bookdata.title: if title_re.search(title_val):
ret.append((bookdata, app_id, prof_id)) results.append((bookdata, app_id, prof_id))
elif mode == 3: else: # mode == 3
if ( if sig_re.search(sig_val) and title_re.search(title_val):
data["signature"] in bookdata.signature results.append((bookdata, app_id, prof_id))
and data["title"] in bookdata.title
): return results
ret.append((bookdata, app_id, prof_id))
# log.debug(ret)
return ret
def setAvailability(self, book_id: str, available: str): def setAvailability(self, book_id: str, available: str):
""" """
@@ -402,7 +494,7 @@ class Database:
""" """
result = self.query_db( result = self.query_db(
"SELECT id FROM media WHERE bookdata=? AND app_id=? AND prof_id=?", "SELECT id FROM media WHERE bookdata=? AND app_id=? AND prof_id=?",
(dump_pickle(bookdata), app_id, prof_id), (bookdata.to_dict, app_id, prof_id),
one=True, one=True,
) )
return result[0] return result[0]
@@ -435,6 +527,7 @@ class Database:
deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0. deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0.
Returns: Returns:
list[dict[int, BookData, int]]: A list of dictionaries containing the id, the metadata of the book and the availability of the book 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( qdata = self.query_db(
@@ -451,6 +544,68 @@ class Database:
ret_result.append(data) ret_result.append(data)
return ret_result return ret_result
def getAllBooks(self) -> list[dict[str, Union[int, BookData]]]:
"""
Get all books in the database that are not set as deleted
Returns
-------
list[dict[str, Union[int, BookData]]]
A list of dictionaries containing the id and the metadata of the book
"""
# return all books in the database
qdata = self.query_db("SELECT id,bookdata FROM media WHERE deleted=0")
ret_result: list[dict[str, Any]] = []
if qdata is None:
return []
for result_a in qdata:
data: dict[str, Any] = {"id": int, "bookdata": BookData}
data["id"] = result_a[0]
data["bookdata"] = BookData().from_string(result_a[1])
ret_result.append(data)
return ret_result
def getApparatNrByBookId(self, book_id):
appNr = self.query_db(
"SELECT appnr FROM semesterapparat WHERE id IN (SELECT app_id FROM media WHERE id=?)",
(book_id,),
one=True,
)
return appNr[0] if appNr else None
def getBooksByProfId(
self, prof_id: int, deleted: int = 0
) -> list[dict[str, Union[int, BookData]]]:
"""
Get the Books based on the professor id
Parameters
----------
prof_id : int
The ID of the professor
deleted : int, optional
If set to 1, it will include deleted books, by default 0
Returns
-------
list[dict[str, Union[int, BookData]]]
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 prof_id={prof_id} AND (deleted={deleted if deleted == 0 else '1 OR deleted=0'})"
)
ret_result = []
if qdata is None:
return []
for result_a in qdata:
data: dict[str, Any] = {"id": int, "bookdata": BookData, "available": int}
data["id"] = result_a[0]
data["bookdata"] = BookData().from_string(result_a[1])
data["available"] = result_a[2]
ret_result.append(data)
return ret_result
def updateBookdata(self, book_id: int, bookdata: BookData): def updateBookdata(self, book_id: int, bookdata: BookData):
""" """
Update the bookdata in the database Update the bookdata in the database
@@ -472,6 +627,16 @@ class Database:
""" """
self.query_db("UPDATE media SET deleted=1 WHERE id=?", (book_id,)) self.query_db("UPDATE media SET deleted=1 WHERE id=?", (book_id,))
def deleteBooks(self, ids: list[int]):
"""
Delete multiple books from the database
Args:
ids (list[int]): A list of book ids to be deleted
"""
query = f"UPDATE media SET deleted=1 WHERE id IN ({','.join(['?'] * len(ids))})"
self.query_db(query, tuple(ids))
# File Interactions # File Interactions
def getBlob(self, filename: str, app_id: Union[str, int]) -> bytes: def getBlob(self, filename: str, app_id: Union[str, int]) -> bytes:
""" """
@@ -525,11 +690,12 @@ class Database:
str: The filename of the recreated file str: The filename of the recreated file
""" """
blob = self.getBlob(filename, app_id) blob = self.getBlob(filename, app_id)
log.debug(blob)
tempdir = settings.database.temp.expanduser() tempdir = settings.database.temp.expanduser()
if not tempdir.exists(): if not tempdir.exists():
tempdir.mkdir(parents=True, exist_ok=True) tempdir.mkdir(parents=True, exist_ok=True)
file = tempfile.NamedTemporaryFile( file = tempfile.NamedTemporaryFile(
delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}" delete=False, dir=tempdir, mode="wb", suffix=f".{filetype}"
) )
file.write(blob) file.write(blob)
# log.debug("file created") # log.debug("file created")
@@ -701,6 +867,20 @@ class Database:
else: else:
return prof[0] return prof[0]
def getProfMailById(self, prof_id: Union[str, int]) -> str:
"""get the mail of a professor based on the id
Args:
prof_id (Union[str,int]): the id of the professor
Returns:
str: the mail of the professor
"""
mail = self.query_db("SELECT mail FROM prof WHERE id=?", (prof_id,), one=True)[
0
]
return mail if mail is not None else ""
def getTitleById(self, prof_id: Union[str, int]) -> str: def getTitleById(self, prof_id: Union[str, int]) -> str:
"""get the title of a professor based on the id """get the title of a professor based on the id
@@ -877,6 +1057,23 @@ class Database:
(newDate, today, app_id), (newDate, today, app_id),
) )
def getId(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 id FROM semesterapparat WHERE name=?", (apparat_name,), one=True
)
if data is None:
return None
else:
return data[0]
def getApparatId(self, apparat_name) -> Optional[int]: def getApparatId(self, apparat_name) -> Optional[int]:
"""get the id of an apparat based on the name """get the id of an apparat based on the name
@@ -1014,22 +1211,22 @@ class Database:
self.close_connection(conn) self.close_connection(conn)
return ret return ret
def deleteApparat(self, app_id: Union[str, int], semester): def deleteApparat(self, apparat: Apparat, semester: str):
"""Delete an apparat from the database """Delete an apparat from the database
Args: Args:
app_id (Union[str, int]): the id of the apparat apparat: (Apparat): the apparat to be deleted
semester (str): the semester the apparat should be deleted from semester (str): the semester the apparat should be deleted from
""" """
log.info(f"Deleting apparat with id {app_id} in semester {semester}") apparat_nr = apparat.appnr
app_id = self.getId(apparat.name)
self.query_db( self.query_db(
"UPDATE semesterapparat SET deletion_status=1, deleted_date=? WHERE appnr=?", "UPDATE semesterapparat SET deletion_status=1, deleted_date=? WHERE appnr=? AND name=?",
(semester, app_id), (semester, apparat_nr, apparat.name),
)
self.query_db(
"UPDATE media SET deleted=1 WHERE app_id=?",
(app_id,),
) )
# delete all books associated with the app_id
# 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, id):
"""check if the apparat is eternal (dauerapparat) """check if the apparat is eternal (dauerapparat)
@@ -1101,11 +1298,11 @@ class Database:
else False else False
) )
def checkApparatExistsById(self, app_id: Union[str, int]) -> bool: def checkApparatExistsByNr(self, app_nr: Union[str, int]) -> bool:
"""a check to see if the apparat is already present in the database, based on the id """a check to see if the apparat is already present in the database, based on the nr. This query will exclude deleted apparats
Args: Args:
app_id (Union[str, int]): the id of the apparat app_nr (Union[str, int]): the id of the apparat
Returns: Returns:
bool: True if the apparat is present, False if not bool: True if the apparat is present, False if not
@@ -1113,7 +1310,9 @@ class Database:
return ( return (
True True
if self.query_db( if self.query_db(
"SELECT appnr FROM semesterapparat WHERE appnr=?", (app_id,), one=True "SELECT id FROM semesterapparat WHERE appnr=? and deletion_status=0",
(app_nr,),
one=True,
) )
else False else False
) )
@@ -1498,7 +1697,7 @@ class Database:
tempdir.mkdir(parents=True, exist_ok=True) tempdir.mkdir(parents=True, exist_ok=True)
file = tempfile.NamedTemporaryFile( file = tempfile.NamedTemporaryFile(
delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}" delete=False, dir=tempdir, mode="wb", suffix=f".{filetype}"
) )
file.write(blob) file.write(blob)
# log.debug("file created") # log.debug("file created")
@@ -1561,9 +1760,9 @@ class Database:
telnr = profdata.telnr telnr = profdata.telnr
title = profdata.title title = profdata.title
query = f"INSERT INTO prof (fname, lname, fullname, mail, telnr,titel) VALUES ('{fname}','{lname}','{fullname}','{mail}','{telnr}','{title}')" query = "INSERT INTO prof (fname, lname, fullname, mail, telnr, titel) VALUES (?,?,?,?,?,?)"
log.debug(query) log.debug(query)
cursor.execute(query) cursor.execute(query, (fname, lname, fullname, mail, telnr, title))
conn.commit() conn.commit()
conn.close() conn.close()
@@ -1606,10 +1805,10 @@ class Database:
fullname = profdata["profname"] fullname = profdata["profname"]
else: else:
fullname = profdata.name() fullname = profdata.name()
query = f"SELECT id FROM prof WHERE fullname = '{fullname}'" query = "SELECT id FROM prof WHERE fullname = ?"
log.debug(query) log.debug(query)
cursor.execute(query) cursor.execute(query, (fullname,))
result = cursor.fetchone() result = cursor.fetchone()
if result: if result:
return result[0] return result[0]
@@ -1624,10 +1823,10 @@ class Database:
""" """
conn = self.connect() conn = self.connect()
cursor = conn.cursor() cursor = conn.cursor()
query = f"SELECT * FROM prof WHERE fullname = '{fullname}'" query = "SELECT * FROM prof WHERE fullname = ?"
log.debug(query) log.debug(query)
result = cursor.execute(query).fetchone() result = cursor.execute(query, (fullname,)).fetchone()
if result: if result:
return Prof().from_tuple(result) return Prof().from_tuple(result)
else: else:
@@ -1643,8 +1842,8 @@ class Database:
int | None: The id of the prof or None if not found int | None: The id of the prof or None if not found
""" """
query = f"SELECT prof_id from semesterapparat WHERE appnr = '{apprarat_id}' and deletion_status = 0" query = "SELECT prof_id from semesterapparat WHERE appnr = ? and deletion_status = 0"
data = self.query_db(query) data = self.query_db(query, (apprarat_id,))
if data: if data:
log.info("Prof ID: " + str(data[0][0])) log.info("Prof ID: " + str(data[0][0]))
return data[0][0] return data[0][0]
@@ -1655,20 +1854,13 @@ class Database:
# get book data # get book data
new_apparat_id = apparat new_apparat_id = apparat
new_prof_id = self.getProfIDByApparat(new_apparat_id) new_prof_id = self.getProfIDByApparat(new_apparat_id)
query = f""" query = (
INSERT INTO media (bookdata, app_id, prof_id, deleted, available, reservation) "INSERT INTO media (bookdata, app_id, prof_id, deleted, available, reservation) "
SELECT "SELECT bookdata, ?, ?, 0, available, reservation FROM media WHERE id = ?"
bookdata, )
'{new_apparat_id}',
'{new_prof_id}',
0,
available,
reservation
FROM media
where id = '{book_id}'"""
connection = self.connect() connection = self.connect()
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute(query) cursor.execute(query, (new_apparat_id, new_prof_id, book_id))
connection.commit() connection.commit()
connection.close() connection.close()
@@ -1680,16 +1872,18 @@ class Database:
appratat (int): the ID of the new apparat appratat (int): the ID of the new apparat
""" """
# get book data # get book data
query = f"UPDATE media SET app_id = '{appratat}' WHERE id = '{book_id}'" query = "UPDATE media SET app_id = ? WHERE id = ?"
connection = self.connect() connection = self.connect()
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute(query) cursor.execute(query, (appratat, book_id))
connection.commit() connection.commit()
connection.close() connection.close()
def getApparatNameByAppNr(self, appnr: int): def getApparatNameByAppNr(self, appnr: int):
query = f"SELECT name FROM semesterapparat WHERE appnr = '{appnr}' and deletion_status = 0" query = (
data = self.query_db(query) "SELECT name FROM semesterapparat WHERE appnr = ? and deletion_status = 0"
)
data = self.query_db(query, (appnr,))
if data: if data:
return data[0][0] return data[0][0]
else: else:
@@ -1702,3 +1896,71 @@ class Database:
result = cursor.fetchone() result = cursor.fetchone()
connection.close() connection.close()
return result return result
def getBookIdByPPN(self, ppn: str) -> int:
query = "SELECT id FROM media WHERE bookdata LIKE ?"
data = self.query_db(query, (f"%{ppn}%",))
if data:
return data[0][0]
else:
return None
def getNewEditionsByApparat(self, apparat_id: int) -> list[BookData]:
"""Get all new editions for a specific apparat
Args:
apparat_id (int): the id of the apparat
Returns:
list[tuple]: A list of tuples containing the new editions data
"""
query = "SELECT * FROM neweditions WHERE for_apparat=? AND ordered=0"
results = self.query_db(query, (apparat_id,))
res = []
for result in results:
# keep only new edition payload; old edition can be reconstructed if needed
res.append(BookData().from_string(result[1]))
return res
def setOrdered(self, newBook_id: int):
query = "UPDATE neweditions SET ordered=1 WHERE id=?"
self.query_db(query, (newBook_id,))
def getBooksWithNewEditions(self, app_id) -> List[BookData]:
# select all bookdata from media, based on the old_edition_id in neweditions where for_apparat = app_id; also get the new_edition bookdata
query = "SELECT m.bookdata, new_bookdata FROM media m JOIN neweditions n ON m.id = n.old_edition_id WHERE n.for_apparat = ?"
results = self.query_db(query, (app_id,))
# store results in tuple old,new
res = []
for result in results:
oldedition = BookData().from_string(result[0])
newedition = BookData().from_string(result[1])
res.append((oldedition, newedition))
return res
def getNewEditionId(self, newBook: BookData):
query = "SELECT id FROM neweditions WHERE new_bookdata LIKE ?"
args = (
newBook.isbn[0] if newBook.isbn and len(newBook.isbn) > 0 else newBook.ppn
)
params = (f"%{args}%",)
data = self.query_db(query, params, one=True)
if data:
return data[0]
else:
return None
def insertNewEdition(self, newBook: BookData, oldBookId: int, for_apparat: int):
# check if new edition already in table, check based on newBook.ppn
check_query = "SELECT id FROM neweditions WHERE new_bookdata LIKE ?"
check_params = (f"%{newBook.ppn}%",)
data = self.query_db(check_query, check_params, one=True)
if data:
log.info("New edition already in table, skipping insert")
return
query = "INSERT INTO neweditions (new_bookdata, old_edition_id, for_apparat) VALUES (?,?,?)"
params = (newBook.to_dict, oldBookId, for_apparat)
self.query_db(query, params)

View File

@@ -101,3 +101,12 @@ CREATE_ELSA_MEDIA_TABLE = """CREATE TABLE elsa_media (
elsa_id INTEGER NOT NULL, elsa_id INTEGER NOT NULL,
FOREIGN KEY (elsa_id) REFERENCES elsa (id) FOREIGN KEY (elsa_id) REFERENCES elsa (id)
)""" )"""
CREATE_TABLE_NEWEDITIONS = """CREATE TABLE neweditions (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
new_bookdata TEXT,
old_edition_id INTEGER,
for_apparat INTEGER,
ordered BOOLEAN DEFAULT (0),
FOREIGN KEY (old_edition_id) REFERENCES media (id),
FOREIGN KEY (for_apparat) REFERENCES semesterapparat (id)
)"""

View File

@@ -1,19 +1,10 @@
from PySide6.QtCore import QThread from PySide6.QtCore import QThread, Signal
from PySide6.QtCore import Signal
from src.backend import Database from src.backend import Database
from src.logic.webrequest import BibTextTransformer, WebRequest from src.logic.webrequest import BibTextTransformer, WebRequest
import loguru from src.shared.logging import log
import sys
from src import LOG_DIR
log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
# Logger configured centrally in main; this module just uses `log`
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
log.add(sys.stdout, level="INFO")
class BookGrabber(QThread): class BookGrabber(QThread):
@@ -31,9 +22,10 @@ class BookGrabber(QThread):
self.book_id = None self.book_id = None
self.use_any = False self.use_any = False
self.use_exact = False self.use_exact = False
self.app_id = None self.app_nr = None
self.tstate = (self.app_id, self.prof_id, self.mode, self.data) self.tstate = (self.app_id, self.prof_id, self.mode, self.data)
self.request = WebRequest() self.request = WebRequest()
self.db = Database()
def add_values( def add_values(
self, app_id: int, prof_id: int, mode: str, data, any_book=False, exact=False self, app_id: int, prof_id: int, mode: str, data, any_book=False, exact=False
@@ -45,13 +37,15 @@ class BookGrabber(QThread):
self.use_any = any_book self.use_any = any_book
self.use_exact = exact self.use_exact = exact
log.info(f"Working on {len(self.data)} entries") log.info(f"Working on {len(self.data)} entries")
self.tstate = (self.app_id, self.prof_id, self.mode, self.data) self.tstate = (self.app_nr, self.prof_id, self.mode, self.data)
log.debug("State: " + str(self.tstate)) log.debug("State: " + str(self.tstate))
self.request.set_apparat(self.app_id) app_nr = self.db.query_db(
"SELECT appnr FROM semesterapparat WHERE id = ?", (self.app_id,)
)[0][0]
self.request.set_apparat(app_nr)
# log.debug(self.tstate) # log.debug(self.tstate)
def run(self): def run(self):
self.db = Database()
item = 0 item = 0
iterdata = self.data iterdata = self.data
# log.debug(iterdata) # log.debug(iterdata)
@@ -91,7 +85,7 @@ class BookGrabber(QThread):
state = 0 state = 0
for result in transformer.RDS_DATA: for result in transformer.RDS_DATA:
# log.debug(result.RDS_LOCATION) # log.debug(result.RDS_LOCATION)
if str(self.app_id) in result.RDS_LOCATION: if str(self.app_nr) in result.RDS_LOCATION:
state = 1 state = 1
break break
@@ -126,27 +120,27 @@ class BookGrabberTest(QThread):
self.is_Running = True self.is_Running = True
log.info("Starting worker thread") log.info("Starting worker thread")
self.data = None self.data = None
self.app_id = None self.app_nr = None
self.prof_id = None self.prof_id = None
self.mode = None self.mode = None
self.book_id = None self.book_id = None
self.use_any = False self.use_any = False
self.use_exact = False self.use_exact = False
self.app_id = appnr self.app_nr = appnr
self.tstate = (self.app_id, self.prof_id, self.mode, self.data) self.tstate = (self.app_nr, self.prof_id, self.mode, self.data)
self.results = [] self.results = []
def add_values( def add_values(
self, app_id: int, prof_id: int, mode: str, data, any_book=False, exact=False self, app_nr: int, prof_id: int, mode: str, data, any_book=False, exact=False
): ):
self.app_id = app_id self.app_nr = app_nr
self.prof_id = prof_id self.prof_id = prof_id
self.mode = mode self.mode = mode
self.data = data self.data = data
self.use_any = any_book self.use_any = any_book
self.use_exact = exact self.use_exact = exact
log.info(f"Working on {len(self.data)} entries") log.info(f"Working on {len(self.data)} entries")
self.tstate = (self.app_id, self.prof_id, self.mode, self.data) self.tstate = (self.app_nr, self.prof_id, self.mode, self.data)
log.debug("State: " + str(self.tstate)) log.debug("State: " + str(self.tstate))
# log.debug(self.tstate) # log.debug(self.tstate)
@@ -159,7 +153,7 @@ class BookGrabberTest(QThread):
signature = str(entry) signature = str(entry)
log.info("Processing entry: " + signature) log.info("Processing entry: " + signature)
webdata = WebRequest().set_apparat(self.app_id).get_ppn(entry) webdata = WebRequest().set_apparat(self.app_nr).get_ppn(entry)
if self.use_any: if self.use_any:
webdata = webdata.use_any_book webdata = webdata.use_any_book
webdata = webdata.get_data() webdata = webdata.get_data()
@@ -186,7 +180,7 @@ class BookGrabberTest(QThread):
state = 0 state = 0
for result in transformer.RDS_DATA: for result in transformer.RDS_DATA:
# log.debug(result.RDS_LOCATION) # log.debug(result.RDS_LOCATION)
if str(self.app_id) in result.RDS_LOCATION: if str(self.app_nr) in result.RDS_LOCATION:
state = 1 state = 1
break break

View File

@@ -0,0 +1,345 @@
import os
import re
from concurrent.futures import ThreadPoolExecutor
from math import ceil
from queue import Empty, Queue
from time import monotonic # <-- NEW
from typing import List, Optional
from PySide6.QtCore import QThread, Signal
# from src.logic.webrequest import BibTextTransformer, WebRequest
from src.backend.catalogue import Catalogue
from src.logic import BookData
from src.logic.SRU import SWB
from src.shared.logging import log
# use all available cores - 2, but at least 1
THREAD_COUNT = max(os.cpu_count() - 2, 1)
THREAD_MIN_ITEMS = 5
# Logger configured centrally in main; use shared `log`
swb = SWB()
dnb = SWB()
cat = Catalogue()
RVK_ALLOWED = r"[A-Z0-9.\-\/]" # conservative RVK character set
def find_newer_edition(
swb_result: BookData, dnb_result: List[BookData]
) -> Optional[List[BookData]]:
"""
New edition if:
- year > swb.year OR
- edition_number > swb.edition_number
BUT: discard any candidate with year < swb.year (if both years are known).
Same-work check:
- Compare RVK roots of signatures (after stripping trailing '+N' and '(N)').
- If both have signatures and RVKs differ -> skip.
Preferences (in order):
1) RVK matches SWB
2) Print over Online-Ressource
3) Has signature
4) Newer: (year desc, edition_number desc)
"""
def strip_copy_and_edition(s: str) -> str:
s = re.sub(r"\(\s*\d+\s*\)", "", s) # remove '(N)'
s = re.sub(r"\s*\+\s*\d+\s*$", "", s) # remove trailing '+N'
return s
def extract_rvk_root(sig: Optional[str]) -> str:
if not sig:
return ""
t = strip_copy_and_edition(sig.upper())
t = re.sub(r"\s+", " ", t).strip()
m = re.match(rf"^([A-Z]{{1,3}}\s*{RVK_ALLOWED}*)", t)
if not m:
cleaned = re.sub(rf"[^{RVK_ALLOWED} ]+", "", t).strip()
return cleaned.split(" ")[0] if cleaned else ""
return re.sub(r"\s+", " ", m.group(1)).strip()
def has_sig(b: BookData) -> bool:
return bool(getattr(b, "signature", None))
def is_online(b: BookData) -> bool:
return (getattr(b, "media_type", None) or "").strip() == "Online-Ressource"
def is_print(b: BookData) -> bool:
return not is_online(b)
def rvk_matches_swb(b: BookData) -> bool:
if not has_sig(b) or not has_sig(swb_result):
return False
return extract_rvk_root(b.signature) == extract_rvk_root(swb_result.signature)
def strictly_newer(b: BookData) -> bool:
# Hard guard: if both years are known and candidate is older, discard
if (
b.year is not None
and swb_result.year is not None
and b.year < swb_result.year
):
return False
newer_by_year = (
b.year is not None
and swb_result.year is not None
and b.year > swb_result.year
)
newer_by_edition = (
b.edition_number is not None
and swb_result.edition_number is not None
and b.edition_number > swb_result.edition_number
)
# Thanks to the guard above, newer_by_edition can't pick something with a smaller year.
return newer_by_year or newer_by_edition
swb_has_sig = has_sig(swb_result)
swb_rvk = extract_rvk_root(getattr(swb_result, "signature", None))
# 1) Filter: same work (by RVK if both have sigs) AND strictly newer
candidates: List[BookData] = []
for b in dnb_result:
if has_sig(b) and swb_has_sig:
if extract_rvk_root(b.signature) != swb_rvk:
continue # different work
if strictly_newer(b):
candidates.append(b)
if not candidates:
return None
# 2) Dedupe by PPN → prefer (rvk-match, is-print, has-signature)
def pref_score(x: BookData) -> tuple[int, int, int]:
return (
1 if rvk_matches_swb(x) else 0,
1 if is_print(x) else 0,
1 if has_sig(x) else 0,
)
by_ppn: dict[Optional[str], BookData] = {}
for b in candidates:
key = getattr(b, "ppn", None)
prev = by_ppn.get(key)
if prev is None or pref_score(b) > pref_score(prev):
by_ppn[key] = b
deduped = list(by_ppn.values())
if not deduped:
return None
# 3) Preserve all qualifying newer editions, but order by preference
def sort_key(b: BookData):
year = b.year if b.year is not None else -1
ed = b.edition_number if b.edition_number is not None else -1
return (
1 if rvk_matches_swb(b) else 0,
1 if is_print(b) else 0,
1 if has_sig(b) else 0,
year,
ed,
)
deduped.sort(key=sort_key, reverse=True)
return deduped
class NewEditionCheckerThread(QThread):
updateSignal = Signal(int, int) # (processed, total)
updateProgress = Signal(int, int) # (processed, total)
total_entries_signal = Signal(int)
resultsSignal = Signal(list) # list[tuple[BookData, list[BookData]]]
# NEW: metrics signals
rateSignal = Signal(float) # items per second ("it/s")
etaSignal = Signal(int) # seconds remaining (-1 when unknown)
def __init__(self, entries: Optional[list["BookData"]] = None, parent=None):
super().__init__(parent)
self.entries: list["BookData"] = entries if entries is not None else []
self.results: list[tuple["BookData", list["BookData"]]] = []
def reset(self):
self.entries = []
self.results = []
# ---------- internal helpers ----------
@staticmethod
def _split_evenly(items: list, parts: int) -> list[list]:
"""Split items as evenly as possible into `parts` chunks (no empty tails)."""
if parts <= 1 or len(items) <= 1:
return [items]
n = len(items)
base = n // parts
extra = n % parts
chunks = []
i = 0
for k in range(parts):
size = base + (1 if k < extra else 0)
if size == 0:
continue
chunks.append(items[i : i + size])
i += size
return chunks
@staticmethod
def _clean_title(raw: str) -> str:
title = raw.rstrip(" .:,;!?")
title = re.sub(r"\s*\(.*\)", "", title)
return title.strip()
@classmethod
def _process_book(
cls, book: "BookData"
) -> tuple["BookData", list["BookData"]] | None:
"""Process one book; returns (original, [found editions]) or None on failure."""
if not book.title:
return None
response: list["BookData"] = []
query = [
f"pica.tit={book.title}",
f"pica.vlg={book.publisher}",
]
swb_result = swb.getBooks(["pica.bib=20735", f"pica.ppn={book.ppn}"])[0]
dnb_results = swb.getBooks(query)
new_editions = find_newer_edition(swb_result, dnb_results)
if new_editions is not None:
for new_edition in new_editions:
new_edition.library_location = cat.get_location(new_edition.ppn)
try:
isbn = (
str(new_edition.isbn[0])
if isinstance(new_edition.isbn, list)
else str(new_edition.isbn)
)
new_edition.link = (
f"https://www.lehmanns.de/search/quick?mediatype_id=2&q={isbn}"
)
except (IndexError, TypeError):
isbn = None
new_edition.in_library = cat.in_library(new_edition.ppn)
response = new_editions
# client = SWB()
# response: list["BookData"] = []
# # First, search by title only
# results = client.getBooks([f"pica.title={title}", f"pica.vlg={book.publisher}"])
# lehmanns = LehmannsClient()
# results = lehmanns.search_by_title(title)
# for result in results:
# if "(eBook)" in result.title:
# result.title = result.title.replace("(eBook)", "").strip()
# swb_results = client.getBooks(
# [
# f"pica.tit={result.title}",
# f"pica.vlg={result.publisher.split(',')[0]}",
# ]
# )
# for swb in swb_results:
# if swb.isbn == result.isbn:
# result.ppn = swb.ppn
# result.signature = swb.signature
# response.append(result)
# if (result.edition_number < swb.edition_number) and (
# swb.year > result.year
# ):
# response.append(result)
if response == []:
return None
# Remove duplicates based on ppn
return (book, response)
@classmethod
def _worker(cls, items: list["BookData"], q: Queue) -> None:
"""Worker for one chunk; pushes ('result', ...), ('progress', 1), and ('done', None)."""
try:
for book in items:
try:
result = cls._process_book(book)
except Exception:
result = None
if result is not None:
q.put(("result", result))
q.put(("progress", 1))
finally:
q.put(("done", None))
# ---------- thread entry point ----------
def run(self):
total = len(self.entries)
self.total_entries_signal.emit(total)
# start timer for metrics
t0 = monotonic()
if total == 0:
log.debug("No entries to process.")
# emit metrics (zero work)
self.rateSignal.emit(0.0)
self.etaSignal.emit(0)
self.resultsSignal.emit([])
return
# Up to 4 workers; ~20 items per worker
num_workers = min(THREAD_COUNT, max(1, ceil(total / THREAD_MIN_ITEMS)))
chunks = self._split_evenly(self.entries, num_workers)
sizes = [len(ch) for ch in chunks]
q: Queue = Queue()
processed = 0
finished_workers = 0
with ThreadPoolExecutor(max_workers=len(chunks)) as ex:
futures = [ex.submit(self._worker, ch, q) for ch in chunks]
log.info(
f"Launched {len(futures)} worker thread(s) for {total} entries: {sizes} entries per thread."
)
for idx, sz in enumerate(sizes, 1):
log.debug(f"Thread {idx}: {sz} entries")
# Aggregate progress/results
while finished_workers < len(chunks):
try:
kind, payload = q.get(timeout=0.1)
except Empty:
continue
if kind == "progress":
processed += int(payload)
self.updateSignal.emit(processed, total)
self.updateProgress.emit(processed, total)
# ---- NEW: compute & emit metrics ----
elapsed = max(1e-9, monotonic() - t0)
rate = processed / elapsed # items per second
remaining = max(0, total - processed)
eta_sec = int(round(remaining / rate)) if rate > 0 else -1
self.rateSignal.emit(rate)
# clamp negative just in case
self.etaSignal.emit(max(0, eta_sec) if eta_sec >= 0 else -1)
# -------------------------------------
elif kind == "result":
self.results.append(payload)
elif kind == "done":
finished_workers += 1
# Final metrics on completion
elapsed_total = max(1e-9, monotonic() - t0)
final_rate = total / elapsed_total
self.rateSignal.emit(final_rate)
self.etaSignal.emit(0)
self.resultsSignal.emit(self.results)

View File

@@ -1,13 +1,15 @@
import sys
import time import time
import loguru
# from icecream import ic # from icecream import ic
from PySide6.QtCore import QThread from PySide6.QtCore import QThread
from PySide6.QtCore import Signal as Signal from PySide6.QtCore import Signal as Signal
from src.backend import Database
import loguru
import sys
from src import LOG_DIR from src import LOG_DIR
from src.backend import Database
log = loguru.logger log = loguru.logger
log.remove() log.remove()
log.add(sys.stdout, level="INFO") log.add(sys.stdout, level="INFO")
@@ -29,8 +31,8 @@ class AutoAdder(QThread):
self.app_id = app_id self.app_id = app_id
self.prof_id = prof_id self.prof_id = prof_id
# print("Launched AutoAdder") # #print("Launched AutoAdder")
# print(self.data, self.app_id, self.prof_id) # #print(self.data, self.app_id, self.prof_id)
def run(self): def run(self):
self.db = Database() self.db = Database()
@@ -46,7 +48,7 @@ class AutoAdder(QThread):
time.sleep(1) time.sleep(1)
except Exception as e: except Exception as e:
# print(e) # #print(e)
log.exception( log.exception(
f"The query failed with message {e} for signature {entry}" f"The query failed with message {e} for signature {entry}"
) )

View File

@@ -1,22 +1,10 @@
import time
# from icecream import ic # from icecream import ic
from PySide6.QtCore import QThread from PySide6.QtCore import QThread
from PySide6.QtCore import Signal as Signal from PySide6.QtCore import Signal as Signal
from src.backend.database import Database from src.backend.database import Database
from src import LOG_DIR
from src.logic.webrequest import BibTextTransformer, WebRequest from src.logic.webrequest import BibTextTransformer, WebRequest
from src.shared.logging import log
# from src.transformers import RDS_AVAIL_DATA
import loguru
import sys
log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
class AvailChecker(QThread): class AvailChecker(QThread):
@@ -24,7 +12,11 @@ class AvailChecker(QThread):
updateProgress = Signal(int, int) updateProgress = Signal(int, int)
def __init__( def __init__(
self, links: list = None, appnumber: int = None, parent=None, books=list[dict] self,
links: list[str] | None = None,
appnumber: int | None = None,
parent=None,
books: list[dict] | None = None,
): ):
if links is None: if links is None:
links = [] links = []
@@ -39,11 +31,13 @@ class AvailChecker(QThread):
) )
self.links = links self.links = links
self.appnumber = appnumber self.appnumber = appnumber
self.books = books self.books = books or []
log.info( log.info(
f"Started worker with appnumber: {self.appnumber} and links: {self.links} and {len(self.books)} books..." f"Started worker with appnumber: {self.appnumber} and links: {self.links} and {len(self.books)} books..."
) )
time.sleep(2) # Pre-create reusable request and transformer to avoid per-item overhead
self._request = WebRequest().set_apparat(self.appnumber)
self._rds_transformer = BibTextTransformer("RDS")
def run(self): def run(self):
self.db = Database() self.db = Database()
@@ -51,16 +45,18 @@ class AvailChecker(QThread):
count = 0 count = 0
for link in self.links: for link in self.links:
log.info("Processing entry: " + str(link)) log.info("Processing entry: " + str(link))
data = WebRequest().set_apparat(self.appnumber).get_ppn(link).get_data() data = self._request.get_ppn(link).get_data()
transformer = BibTextTransformer("RDS") rds = self._rds_transformer.get_data(data).return_data("rds_availability")
rds = transformer.get_data(data).return_data("rds_availability")
book_id = None book_id = None
if not rds or not rds.items:
log.warning(f"No RDS data found for link {link}")
continue
for item in rds.items: for item in rds.items:
sign = item.superlocation sign = item.superlocation
loc = item.location loc = item.location
# # print(item.location) # # #print(item.location)
if self.appnumber in sign or self.appnumber in loc: if str(self.appnumber) in sign or str(self.appnumber) in loc:
state = 1 state = 1
break break
for book in self.books: for book in self.books:
@@ -68,7 +64,7 @@ class AvailChecker(QThread):
book_id = book["id"] book_id = book["id"]
break break
log.info(f"State of {link}: " + str(state)) log.info(f"State of {link}: " + str(state))
# print("Updating availability of " + str(book_id) + " to " + str(state)) # #print("Updating availability of " + str(book_id) + " to " + str(state))
self.db.setAvailability(book_id, state) self.db.setAvailability(book_id, state)
count += 1 count += 1
self.updateProgress.emit(count, len(self.links)) self.updateProgress.emit(count, len(self.links))

631
src/logic/SRU.py Normal file
View File

@@ -0,0 +1,631 @@
import re
import xml.etree.ElementTree as ET
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, Iterable, List, Optional, Tuple, Union
import requests
from requests.adapters import HTTPAdapter
# centralized logging used via src.shared.logging
from src.logic.dataclass import BookData
from src.shared.logging import log
log # ensure imported logger is referenced
# -----------------------
# Dataclasses
# -----------------------
# --- MARC XML structures ---
@dataclass
class ControlField:
tag: str
value: str
@dataclass
class SubField:
code: str
value: str
@dataclass
class DataField:
tag: str
ind1: str = " "
ind2: str = " "
subfields: List[SubField] = field(default_factory=list)
@dataclass
class MarcRecord:
leader: str
controlfields: List[ControlField] = field(default_factory=list)
datafields: List[DataField] = field(default_factory=list)
# --- SRU record wrapper ---
@dataclass
class Record:
recordSchema: str
recordPacking: str
recordData: MarcRecord
recordPosition: int
@dataclass
class EchoedSearchRequest:
version: str
query: str
maximumRecords: int
recordPacking: str
recordSchema: str
@dataclass
class SearchRetrieveResponse:
version: str
numberOfRecords: int
records: List[Record] = field(default_factory=list)
echoedSearchRetrieveRequest: Optional[EchoedSearchRequest] = None
# -----------------------
# Parser
# -----------------------
ZS = "http://www.loc.gov/zing/srw/"
MARC = "http://www.loc.gov/MARC21/slim"
NS = {"zs": ZS, "marc": MARC}
def _text(elem: Optional[ET.Element]) -> str:
return (elem.text or "") if elem is not None else ""
def _req_text(parent: ET.Element, path: str) -> Optional[str]:
el = parent.find(path, NS)
if el is None or el.text is None:
return None
return el.text
def parse_marc_record(record_el: ET.Element) -> MarcRecord:
"""
record_el is the <marc:record> element (default ns MARC in your sample)
"""
# leader
leader_text = _req_text(record_el, "marc:leader") or ""
# controlfields
controlfields: List[ControlField] = []
for cf in record_el.findall("marc:controlfield", NS):
tag = cf.get("tag", "").strip()
controlfields.append(ControlField(tag=tag, value=_text(cf)))
# datafields
datafields: List[DataField] = []
for df in record_el.findall("marc:datafield", NS):
tag = df.get("tag", "").strip()
ind1 = df.get("ind1") or " "
ind2 = df.get("ind2") or " "
subfields: List[SubField] = []
for sf in df.findall("marc:subfield", NS):
code = sf.get("code", "")
subfields.append(SubField(code=code, value=_text(sf)))
datafields.append(DataField(tag=tag, ind1=ind1, ind2=ind2, subfields=subfields))
return MarcRecord(
leader=leader_text, controlfields=controlfields, datafields=datafields
)
def parse_record(zs_record_el: ET.Element) -> Record:
recordSchema = _req_text(zs_record_el, "zs:recordSchema") or ""
recordPacking = _req_text(zs_record_el, "zs:recordPacking") or ""
# recordData contains a MARC <record> with default MARC namespace in your sample
recordData_el = zs_record_el.find("zs:recordData", NS)
if recordData_el is None:
raise ValueError("Missing zs:recordData")
marc_record_el = recordData_el.find("marc:record", NS)
if marc_record_el is None:
# If the MARC record uses default ns (xmlns="...") ElementTree still needs the ns-qualified name
# We already searched with prefix; this covers both default and prefixed cases.
raise ValueError("Missing MARC21 record inside zs:recordData")
marc_record = parse_marc_record(marc_record_el)
recordPosition = int(_req_text(zs_record_el, "zs:recordPosition") or "0")
return Record(
recordSchema=recordSchema,
recordPacking=recordPacking,
recordData=marc_record,
recordPosition=recordPosition,
)
def parse_echoed_request(root: ET.Element) -> Optional[EchoedSearchRequest]:
el = root.find("zs:echoedSearchRetrieveRequest", NS)
if el is None:
return None
# Be permissive with missing fields
version = _text(el.find("zs:version", NS))
query = _text(el.find("zs:query", NS))
maximumRecords_text = _text(el.find("zs:maximumRecords", NS)) or "0"
recordPacking = _text(el.find("zs:recordPacking", NS))
recordSchema = _text(el.find("zs:recordSchema", NS))
try:
maximumRecords = int(maximumRecords_text)
except ValueError:
maximumRecords = 0
return EchoedSearchRequest(
version=version,
query=query,
maximumRecords=maximumRecords,
recordPacking=recordPacking,
recordSchema=recordSchema,
)
def parse_search_retrieve_response(
xml_str: Union[str, bytes],
) -> SearchRetrieveResponse:
root = ET.fromstring(xml_str)
# Root is zs:searchRetrieveResponse
version = _req_text(root, "zs:version")
numberOfRecords = int(_req_text(root, "zs:numberOfRecords") or "0")
records_parent = root.find("zs:records", NS)
records: List[Record] = []
if records_parent is not None:
for r in records_parent.findall("zs:record", NS):
records.append(parse_record(r))
echoed = parse_echoed_request(root)
return SearchRetrieveResponse(
version=version,
numberOfRecords=numberOfRecords,
records=records,
echoedSearchRetrieveRequest=echoed,
)
# --- Query helpers over MarcRecord ---
def iter_datafields(
rec: MarcRecord,
tag: Optional[str] = None,
ind1: Optional[str] = None,
ind2: Optional[str] = None,
) -> Iterable[DataField]:
"""Yield datafields, optionally filtered by tag/indicators."""
for df in rec.datafields:
if tag is not None and df.tag != tag:
continue
if ind1 is not None and df.ind1 != ind1:
continue
if ind2 is not None and df.ind2 != ind2:
continue
yield df
def subfield_values(
rec: MarcRecord,
tag: str,
code: str,
*,
ind1: Optional[str] = None,
ind2: Optional[str] = None,
) -> List[str]:
"""All values for subfield `code` in every `tag` field (respecting indicators)."""
out: List[str] = []
for df in iter_datafields(rec, tag, ind1, ind2):
out.extend(sf.value for sf in df.subfields if sf.code == code)
return out
def first_subfield_value(
rec: MarcRecord,
tag: str,
code: str,
*,
ind1: Optional[str] = None,
ind2: Optional[str] = None,
default: Optional[str] = None,
) -> Optional[str]:
"""First value for subfield `code` in `tag` (respecting indicators)."""
for df in iter_datafields(rec, tag, ind1, ind2):
for sf in df.subfields:
if sf.code == code:
return sf.value
return default
def find_datafields_with_subfields(
rec: MarcRecord,
tag: str,
*,
where_all: Optional[Dict[str, str]] = None,
where_any: Optional[Dict[str, str]] = None,
casefold: bool = False,
ind1: Optional[str] = None,
ind2: Optional[str] = None,
) -> List[DataField]:
"""
Return datafields of `tag` whose subfields match constraints:
- where_all: every (code -> exact value) must be present
- where_any: at least one (code -> exact value) present
Set `casefold=True` for case-insensitive comparison.
"""
where_all = where_all or {}
where_any = where_any or {}
matched: List[DataField] = []
for df in iter_datafields(rec, tag, ind1, ind2):
# Map code -> list of values (with optional casefold applied)
vals: Dict[str, List[str]] = {}
for sf in df.subfields:
v = sf.value.casefold() if casefold else sf.value
vals.setdefault(sf.code, []).append(v)
ok = True
for c, v in where_all.items():
vv = v.casefold() if casefold else v
if c not in vals or vv not in vals[c]:
ok = False
break
if ok and where_any:
any_ok = any(
(c in vals) and ((v.casefold() if casefold else v) in vals[c])
for c, v in where_any.items()
)
if not any_ok:
ok = False
if ok:
matched.append(df)
return matched
def controlfield_value(
rec: MarcRecord, tag: str, default: Optional[str] = None
) -> Optional[str]:
"""Get the first controlfield value by tag (e.g., '001', '005')."""
for cf in rec.controlfields:
if cf.tag == tag:
return cf.value
return default
def datafields_value(
data: List[DataField], code: str, default: Optional[str] = None
) -> Optional[str]:
"""Get the first value for a specific subfield code in a list of datafields."""
for df in data:
for sf in df.subfields:
if sf.code == code:
return sf.value
return default
def datafield_value(
df: DataField, code: str, default: Optional[str] = None
) -> Optional[str]:
"""Get the first value for a specific subfield code in a datafield."""
for sf in df.subfields:
if sf.code == code:
return sf.value
return default
def _smart_join_title(a: str, b: Optional[str]) -> str:
"""
Join 245 $a and $b with MARC-style punctuation.
If $b is present, join with ' : ' unless either side already supplies punctuation.
"""
a = a.strip()
if not b:
return a
b = b.strip()
if a.endswith((":", ";", "/")) or b.startswith((":", ";", "/")):
return f"{a} {b}"
return f"{a} : {b}"
def subfield_values_from_fields(
fields: Iterable[DataField],
code: str,
) -> List[str]:
"""All subfield values with given `code` across a list of DataField."""
return [sf.value for df in fields for sf in df.subfields if sf.code == code]
def first_subfield_value_from_fields(
fields: Iterable[DataField],
code: str,
default: Optional[str] = None,
) -> Optional[str]:
"""First subfield value with given `code` across a list of DataField."""
for df in fields:
for sf in df.subfields:
if sf.code == code:
return sf.value
return default
def subfield_value_pairs_from_fields(
fields: Iterable[DataField],
code: str,
) -> List[Tuple[DataField, str]]:
"""
Return (DataField, value) pairs for all subfields with `code`.
Useful if you need to know which field a value came from.
"""
out: List[Tuple[DataField, str]] = []
for df in fields:
for sf in df.subfields:
if sf.code == code:
out.append((df, sf.value))
return out
def book_from_marc(rec: MarcRecord) -> BookData:
# PPN from controlfield 001
ppn = controlfield_value(rec, "001")
# Title = 245 $a + 245 $b (if present)
t_a = first_subfield_value(rec, "245", "a")
t_b = first_subfield_value(rec, "245", "b")
title = _smart_join_title(t_a, t_b) if t_a else None
# Signature = 924 where $9 == "Frei 129" → take that field's $g
frei_fields = find_datafields_with_subfields(
rec, "924", where_all={"9": "Frei 129"}
)
signature = first_subfield_value_from_fields(frei_fields, "g")
# Year = 264 $c (prefer ind2="1" publication; fallback to any 264)
year = first_subfield_value(rec, "264", "c", ind2="1") or first_subfield_value(
rec, "264", "c"
)
isbn = subfield_values(rec, "020", "a")
mediatype = first_subfield_value(rec, "338", "a")
lang = subfield_values(rec, "041", "a")
authors = subfield_values(rec, "700", "a")
author = None
if authors:
author = "; ".join(authors)
return BookData(
ppn=ppn,
title=title,
signature=signature,
edition=first_subfield_value(rec, "250", "a") or "",
year=year,
pages=first_subfield_value(rec, "300", "a") or "",
publisher=first_subfield_value(rec, "264", "b") or "",
isbn=isbn,
language=lang,
link="",
author=author,
media_type=mediatype,
)
class SWBData(Enum):
URL = "https://sru.k10plus.de/opac-de-627!rec=1?version=1.1&operation=searchRetrieve&query={}&maximumRecords=100&recordSchema=marcxml"
ARGSCHEMA = "pica."
NAME = "SWB"
class DNBData(Enum):
URL = "https://services.dnb.de/sru/dnb?version=1.1&operation=searchRetrieve&query={}&maximumRecords=100&recordSchema=MARC21-xml"
ARGSCHEMA = ""
NAME = "DNB"
class SRUSite(Enum):
SWB = SWBData
DNB = DNBData
RVK_ALLOWED = r"[A-Z0-9.\-\/]" # conservative char set typically seen in RVK notations
def find_newer_edition(
swb_result: BookData, dnb_result: List[BookData]
) -> Optional[List[BookData]]:
"""
New edition if:
- year > swb.year OR
- edition_number > swb.edition_number
Additional guards & preferences:
- If both have signatures and they differ, skip (not the same work).
- For duplicates (same ppn): keep the one that has a signature, and
prefer a signature that matches swb_result.signature.
- If multiple remain: keep the single 'latest' by (year desc,
edition_number desc, best-signature-match desc, has-signature desc).
"""
def norm_sig(s: Optional[str]) -> str:
if not s:
return ""
# normalize: lowercase, collapse whitespace, keep alnum + a few separators
s = s.lower()
s = re.sub(r"\s+", " ", s).strip()
# remove obvious noise; adjust if your signature format differs
s = re.sub(r"[^a-z0-9\-_/\. ]+", "", s)
return s
def has_sig(b: BookData) -> bool:
return bool(getattr(b, "signature", None))
def sig_matches_swb(b: BookData) -> bool:
if not has_sig(b) or not has_sig(swb_result):
return False
return norm_sig(b.signature) == norm_sig(swb_result.signature)
def strictly_newer(b: BookData) -> bool:
by_year = (
b.year is not None
and swb_result.year is not None
and b.year > swb_result.year
)
by_edition = (
b.edition_number is not None
and swb_result.edition_number is not None
and b.edition_number > swb_result.edition_number
)
return by_year or by_edition
swb_sig_norm = norm_sig(getattr(swb_result, "signature", None))
# 1) Filter to same-work AND newer
candidates: List[BookData] = []
for b in dnb_result:
# Skip if both signatures exist and don't match (different work)
b_sig = getattr(b, "signature", None)
if b_sig and swb_result.signature:
if norm_sig(b_sig) != swb_sig_norm:
continue # not the same work
# Keep only if newer by rules
if strictly_newer(b):
candidates.append(b)
if not candidates:
return None
# 2) Dedupe by PPN, preferring signature (and matching signature if possible)
by_ppn: dict[Optional[str], BookData] = {}
for b in candidates:
key = getattr(b, "ppn", None)
prev = by_ppn.get(key)
if prev is None:
by_ppn[key] = b
continue
# Compute preference score for both
def ppn_pref_score(x: BookData) -> tuple[int, int]:
# (signature matches swb, has signature)
return (1 if sig_matches_swb(x) else 0, 1 if has_sig(x) else 0)
if ppn_pref_score(b) > ppn_pref_score(prev):
by_ppn[key] = b
deduped = list(by_ppn.values())
if not deduped:
return None
# 3) If multiple remain, keep only the latest one.
# Order: year desc, edition_number desc, signature-match desc, has-signature desc
def sort_key(b: BookData):
year = b.year if b.year is not None else -1
ed = b.edition_number if b.edition_number is not None else -1
sig_match = 1 if sig_matches_swb(b) else 0
sig_present = 1 if has_sig(b) else 0
return (year, ed, sig_match, sig_present)
best = max(deduped, key=sort_key)
return [best] if best else None
class Api:
def __init__(self, site: str, url: str, prefix: str):
self.site = site
self.url = url
self.prefix = prefix
# Reuse TCP connections across requests for better performance
self._session = requests.Session()
# Slightly larger connection pool for concurrent calls
adapter = HTTPAdapter(pool_connections=10, pool_maxsize=20)
self._session.mount("http://", adapter)
self._session.mount("https://", adapter)
def close(self):
try:
self._session.close()
except Exception:
pass
def __del__(self):
# Best-effort cleanup
self.close()
def get(self, query_args: Iterable[str]) -> List[Record]:
# if any query_arg ends with =, remove it
if self.site == "DNB":
args = [arg for arg in query_args if not arg.startswith("pica.")]
if args == []:
raise ValueError("DNB queries must include at least one search term")
query_args = args
# query_args = [f"{self.prefix}{arg}" for arg in query_args]
query = "+and+".join(query_args)
query = query.replace(" ", "%20").replace("&", "%26")
# query_args = [arg for arg in query_args if not arg.endswith("=")]
# query = "+and+".join(query_args)
# query = query.replace(" ", "%20").replace("&", "%26")
# insert the query into the url url is
url = self.url.format(query)
log.debug(url)
headers = {
"User-Agent": f"{self.site} SRU Client, <alexander.kirchner@ph-freiburg.de>",
"Accept": "application/xml",
"Accept-Charset": "latin1,utf-8;q=0.7,*;q=0.3",
}
# Use persistent session and set timeouts to avoid hanging
resp = self._session.get(url, headers=headers, timeout=(3.05, 60))
if resp.status_code != 200:
raise Exception(f"Error fetching data from SWB: {resp.status_code}")
# Parse using raw bytes (original behavior) to preserve encoding edge cases
sr = parse_search_retrieve_response(resp.content)
return sr.records
def getBooks(self, query_args: Iterable[str]) -> List[BookData]:
records: List[Record] = self.get(query_args)
# Avoid printing on hot paths; rely on logger if needed
log.debug(f"{self.site} found {len(records)} records for args={query_args}")
books: List[BookData] = []
# extract title from query_args if present
title = None
for arg in query_args:
if arg.startswith("pica.tit="):
title = arg.split("=")[1]
break
for rec in records:
book = book_from_marc(rec.recordData)
books.append(book)
if title:
books = [
b
for b in books
if b.title and b.title.lower().startswith(title.lower())
]
return books
def getLinkForBook(self, book: BookData) -> str:
# Not implemented: depends on catalog front-end; return empty string for now
return ""
class SWB(Api):
def __init__(self):
self.site = SWBData.NAME.value
self.url = SWBData.URL.value
self.prefix = SWBData.ARGSCHEMA.value
super().__init__(self.site, self.url, self.prefix)

View File

@@ -1,6 +1,35 @@
from .dataclass import ApparatData, BookData, Prof, Apparat, ELSA __all__ = [
"custom_sort",
"sort_semesters_list",
"APP_NRS",
"PROF_TITLES",
"SEMAP_MEDIA_ACCOUNTS",
"csv_to_list",
"ELSA",
"Apparat",
"ApparatData",
"BookData",
"Prof",
"Semester",
"SemapDocument",
"elsa_word_to_csv",
"pdf_to_semap",
"word_docx_to_csv",
"word_to_semap",
"ZoteroController",
"eml_to_semap",
]
from .c_sort import custom_sort, sort_semesters_list from .c_sort import custom_sort, sort_semesters_list
from .constants import APP_NRS, PROF_TITLES, SEMAP_MEDIA_ACCOUNTS from .constants import APP_NRS, PROF_TITLES, SEMAP_MEDIA_ACCOUNTS
from .csvparser import csv_to_list from .csvparser import csv_to_list
from .wordparser import elsa_word_to_csv, word_docx_to_csv, word_to_semap, SemapDocument from .dataclass import ELSA, Apparat, ApparatData, BookData, Prof
from .semester import Semester
from .wordparser import (
SemapDocument,
elsa_word_to_csv,
pdf_to_semap,
word_docx_to_csv,
word_to_semap,
)
from .xmlparser import eml_to_semap
from .zotero import ZoteroController from .zotero import ZoteroController

View File

@@ -1,36 +1,4 @@
def parse_semester(semester: str): def parse_semester(semester: str) -> tuple[int, int]:
"""
Parses the semester string into a sortable format.
Returns a tuple of (year, type), where type is 0 for SoSe and 1 for WiSe.
"""
if semester.startswith("SoSe"):
return int(semester.split()[1]), 0
elif semester.startswith("WiSe"):
year_part = semester.split()[1]
start_year, _ = map(int, year_part.split("/"))
return start_year, 1
else:
raise ValueError(f"Invalid semester format: {semester}")
def custom_sort(entries):
"""
Sorts the list of tuples based on the custom schema.
:param entries: List of tuples in the format (str, int, int).
:return: Sorted list of tuples.
"""
return sorted(
entries,
key=lambda entry: (
parse_semester(entry[0]), # Sort by semester parsed as (year, type)
entry[1], # Then by the second element of the tuple
entry[2], # Finally by the third element of the tuple
),
)
def parse_semester(semester: str):
""" """
Parses the semester string into a sortable format. Parses the semester string into a sortable format.
Returns a tuple of (year, type), where type is 0 for SoSe and 1 for WiSe. Returns a tuple of (year, type), where type is 0 for SoSe and 1 for WiSe.
@@ -48,6 +16,23 @@ def parse_semester(semester: str):
raise ValueError(f"Invalid semester format: {semester}") raise ValueError(f"Invalid semester format: {semester}")
def custom_sort(entries) -> list:
"""
Sorts the list of tuples based on the custom schema.
:param entries: List of tuples in the format (str, int, int).
:return: Sorted list of tuples.
"""
return sorted(
entries,
key=lambda entry: (
parse_semester(entry[0]), # Sort by semester parsed as (year, type)
entry[1], # Then by the second element of the tuple
entry[2], # Finally by the third element of the tuple
),
)
def sort_semesters_list(semesters: list) -> list: def sort_semesters_list(semesters: list) -> list:
""" """
Sorts a list of semester strings based on year and type. Sorts a list of semester strings based on year and type.
@@ -83,4 +68,4 @@ if __name__ == "__main__":
"SoSe 25", "SoSe 25",
] ]
print(sort_semesters_list(unsorted)) # print(sort_semesters_list(unsorted))

View File

@@ -30,184 +30,184 @@ PROF_TITLES = [
] ]
SEMAP_MEDIA_ACCOUNTS = { SEMAP_MEDIA_ACCOUNTS = {
"1": "1008000055", 1: "1008000055",
"2": "1008000188", 2: "1008000188",
"3": "1008000211", 3: "1008000211",
"4": "1008000344", 4: "1008000344",
"5": "1008000477", 5: "1008000477",
"6": "1008000500", 6: "1008000500",
"7": "1008000633", 7: "1008000633",
"8": "1008000766", 8: "1008000766",
"9": "1008000899", 9: "1008000899",
"10": "1008000922", 10: "1008000922",
"11": "1008001044", 11: "1008001044",
"12": "1008001177", 12: "1008001177",
"13": "1008001200", 13: "1008001200",
"14": "1008001333", 14: "1008001333",
"15": "1008001466", 15: "1008001466",
"16": "1008001599", 16: "1008001599",
"17": "1008001622", 17: "1008001622",
"18": "1008001755", 18: "1008001755",
"19": "1008001888", 19: "1008001888",
"20": "1008001911", 20: "1008001911",
"21": "1008002033", 21: "1008002033",
"22": "1008002166", 22: "1008002166",
"23": "1008002299", 23: "1008002299",
"24": "1008002322", 24: "1008002322",
"25": "1008002455", 25: "1008002455",
"26": "1008002588", 26: "1008002588",
"27": "1008002611", 27: "1008002611",
"28": "1008002744", 28: "1008002744",
"29": "1008002877", 29: "1008002877",
"30": "1008002900", 30: "1008002900",
"31": "1008003022", 31: "1008003022",
"32": "1008003155", 32: "1008003155",
"33": "1008003288", 33: "1008003288",
"34": "1008003311", 34: "1008003311",
"35": "1008003444", 35: "1008003444",
"36": "1008003577", 36: "1008003577",
"37": "1008003600", 37: "1008003600",
"38": "1008003733", 38: "1008003733",
"39": "1008003866", 39: "1008003866",
"40": "1008003999", 40: "1008003999",
"41": "1008004011", 41: "1008004011",
"42": "1008004144", 42: "1008004144",
"43": "1008004277", 43: "1008004277",
"44": "1008004300", 44: "1008004300",
"45": "1008004433", 45: "1008004433",
"46": "1008004566", 46: "1008004566",
"47": "1008004699", 47: "1008004699",
"48": "1008004722", 48: "1008004722",
"49": "1008004855", 49: "1008004855",
"50": "1008004988", 50: "1008004988",
"51": "1008005000", 51: "1008005000",
"52": "1008005133", 52: "1008005133",
"53": "1008005266", 53: "1008005266",
"54": "1008005399", 54: "1008005399",
"55": "1008005422", 55: "1008005422",
"56": "1008005555", 56: "1008005555",
"57": "1008005688", 57: "1008005688",
"58": "1008005711", 58: "1008005711",
"59": "1008005844", 59: "1008005844",
"60": "1008005977", 60: "1008005977",
"61": "1008006099", 61: "1008006099",
"62": "1008006122", 62: "1008006122",
"63": "1008006255", 63: "1008006255",
"64": "1008006388", 64: "1008006388",
"65": "1008006411", 65: "1008006411",
"66": "1008006544", 66: "1008006544",
"67": "1008006677", 67: "1008006677",
"68": "1008006700", 68: "1008006700",
"69": "1008006833", 69: "1008006833",
"70": "1008006966", 70: "1008006966",
"71": "1008007088", 71: "1008007088",
"72": "1008007111", 72: "1008007111",
"73": "1008007244", 73: "1008007244",
"74": "1008007377", 74: "1008007377",
"75": "1008007400", 75: "1008007400",
"76": "1008007533", 76: "1008007533",
"77": "1008007666", 77: "1008007666",
"78": "1008007799", 78: "1008007799",
"79": "1008007822", 79: "1008007822",
"80": "1008007955", 80: "1008007955",
"81": "1008008077", 81: "1008008077",
"82": "1008008100", 82: "1008008100",
"83": "1008008233", 83: "1008008233",
"84": "1008008366", 84: "1008008366",
"85": "1008008499", 85: "1008008499",
"86": "1008008522", 86: "1008008522",
"87": "1008008655", 87: "1008008655",
"88": "1008008788", 88: "1008008788",
"89": "1008008811", 89: "1008008811",
"90": "1008008944", 90: "1008008944",
"91": "1008009066", 91: "1008009066",
"92": "1008009199", 92: "1008009199",
"93": "1008009222", 93: "1008009222",
"94": "1008009355", 94: "1008009355",
"95": "1008009488", 95: "1008009488",
"96": "1008009511", 96: "1008009511",
"97": "1008009644", 97: "1008009644",
"98": "1008009777", 98: "1008009777",
"99": "1008009800", 99: "1008009800",
"100": "1008009933", 100: "1008009933",
"101": "1008010022", 101: "1008010022",
"102": "1008010155", 102: "1008010155",
"103": "1008010288", 103: "1008010288",
"104": "1008010311", 104: "1008010311",
"105": "1008010444", 105: "1008010444",
"106": "1008010577", 106: "1008010577",
"107": "1008010600", 107: "1008010600",
"108": "1008010733", 108: "1008010733",
"109": "1008010866", 109: "1008010866",
"110": "1008010999", 110: "1008010999",
"111": "1008011011", 111: "1008011011",
"112": "1008011144", 112: "1008011144",
"113": "1008011277", 113: "1008011277",
"114": "1008011300", 114: "1008011300",
"115": "1008011433", 115: "1008011433",
"116": "1008011566", 116: "1008011566",
"117": "1008011699", 117: "1008011699",
"118": "1008011722", 118: "1008011722",
"119": "1008011855", 119: "1008011855",
"120": "1008011988", 120: "1008011988",
"121": "1008012000", 121: "1008012000",
"122": "1008012133", 122: "1008012133",
"123": "1008012266", 123: "1008012266",
"124": "1008012399", 124: "1008012399",
"125": "1008012422", 125: "1008012422",
"126": "1008012555", 126: "1008012555",
"127": "1008012688", 127: "1008012688",
"128": "1008012711", 128: "1008012711",
"129": "1008012844", 129: "1008012844",
"130": "1008012977", 130: "1008012977",
"131": "1008013099", 131: "1008013099",
"132": "1008013122", 132: "1008013122",
"133": "1008013255", 133: "1008013255",
"134": "1008013388", 134: "1008013388",
"135": "1008013411", 135: "1008013411",
"136": "1008013544", 136: "1008013544",
"137": "1008013677", 137: "1008013677",
"138": "1008013700", 138: "1008013700",
"139": "1008013833", 139: "1008013833",
"140": "1008013966", 140: "1008013966",
"141": "1008014088", 141: "1008014088",
"142": "1008014111", 142: "1008014111",
"143": "1008014244", 143: "1008014244",
"144": "1008014377", 144: "1008014377",
"145": "1008014400", 145: "1008014400",
"146": "1008014533", 146: "1008014533",
"147": "1008014666", 147: "1008014666",
"148": "1008014799", 148: "1008014799",
"149": "1008014822", 149: "1008014822",
"150": "1008014955", 150: "1008014955",
"151": "1008015077", 151: "1008015077",
"152": "1008015100", 152: "1008015100",
"153": "1008015233", 153: "1008015233",
"154": "1008015366", 154: "1008015366",
"155": "1008015499", 155: "1008015499",
"156": "1008015522", 156: "1008015522",
"157": "1008015655", 157: "1008015655",
"158": "1008015788", 158: "1008015788",
"159": "1008015811", 159: "1008015811",
"160": "1008015944", 160: "1008015944",
"161": "1008016066", 161: "1008016066",
"162": "1008016199", 162: "1008016199",
"163": "1008016222", 163: "1008016222",
"164": "1008016355", 164: "1008016355",
"165": "1008016488", 165: "1008016488",
"166": "1008016511", 166: "1008016511",
"167": "1008016644", 167: "1008016644",
"168": "1008016777", 168: "1008016777",
"169": "1008016800", 169: "1008016800",
"170": "1008016933", 170: "1008016933",
"171": "1008017055", 171: "1008017055",
"172": "1008017188", 172: "1008017188",
"173": "1008017211", 173: "1008017211",
"174": "1008017344", 174: "1008017344",
"175": "1008017477", 175: "1008017477",
"176": "1008017500", 176: "1008017500",
"177": "1008017633", 177: "1008017633",
"178": "1008017766", 178: "1008017766",
"179": "1008017899", 179: "1008017899",
"180": "1008017922", 180: "1008017922",
} }

View File

@@ -1,13 +1,13 @@
import csv import csv
import chardet from charset_normalizer import detect
def csv_to_list(path: str) -> list[str]: def csv_to_list(path: str) -> list[str]:
""" """
Extracts the data from a csv file and returns it as a pandas dataframe Extracts the data from a csv file and returns it as a pandas dataframe
""" """
encoding = chardet.detect(open(path, "rb").read())["encoding"] encoding = detect(open(path, "rb").read())["encoding"]
with open(path, newline="", encoding=encoding) as csvfile: with open(path, newline="", encoding=encoding) as csvfile:
# if decoder fails to map, assign "" # if decoder fails to map, assign ""
reader = csv.reader(csvfile, delimiter=";", quotechar="|") reader = csv.reader(csvfile, delimiter=";", quotechar="|")
@@ -20,4 +20,4 @@ def csv_to_list(path: str) -> list[str]:
if __name__ == "__main__": if __name__ == "__main__":
text = csv_to_list("C:/Users/aky547/Desktop/semap/71.csv") text = csv_to_list("C:/Users/aky547/Desktop/semap/71.csv")
# remove linebreaks # remove linebreaks
# print(text) # #print(text)

View File

@@ -1,8 +1,13 @@
from dataclasses import dataclass, field
from enum import Enum
import json import json
from typing import Union, Any, Optional 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 @dataclass
class Prof: class Prof:
@@ -32,7 +37,7 @@ class Prof:
self._title = value self._title = value
# add function that sets the data from a tuple # add function that sets the data from a tuple
def from_tuple(self, data: tuple[Union[str, int], ...]): def from_tuple(self, data: tuple[Union[str, int], ...]) -> "Prof":
setattr(self, "id", data[0]) setattr(self, "id", data[0])
setattr(self, "_title", data[1]) setattr(self, "_title", data[1])
setattr(self, "firstname", data[2]) setattr(self, "firstname", data[2])
@@ -67,21 +72,63 @@ class BookData:
language: Union[str, list[str], None] = field(default_factory=list) language: Union[str, list[str], None] = field(default_factory=list)
publisher: str | None = None publisher: str | None = None
place: str | None = None place: str | None = None
year: str | None = None year: int | None = None
pages: str | None = None pages: str | None = None
library_location: int | None = None library_location: str | None = None
in_apparat: bool | None = False in_apparat: bool | None = False
adis_idn: str | None = None 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
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": def from_dict(self, data: dict) -> "BookData":
for key, value in data.items(): for key, value in data.items():
setattr(self, key, value) setattr(self, key, value)
return self 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 @property
def to_dict(self) -> str: def to_dict(self) -> str:
"""Convert the dataclass to a dictionary.""" """Convert the dataclass to a dictionary."""
return json.dumps(self.__dict__, ensure_ascii=False) 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: def from_dataclass(self, dataclass: Optional[Any]) -> None:
if dataclass is None: if dataclass is None:
@@ -89,10 +136,44 @@ class BookData:
for key, value in dataclass.__dict__.items(): for key, value in dataclass.__dict__.items():
setattr(self, key, value) 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": def from_string(self, data: str) -> "BookData":
ndata = json.loads(data) ndata = json.loads(data)
return BookData(**ndata) 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 @dataclass
class MailData: class MailData:
@@ -141,6 +222,7 @@ class Subjects(Enum):
for i in cls: for i in cls:
if i.name == name: if i.name == name:
return i.id - 1 return i.id - 1
return None
@dataclass @dataclass
@@ -204,3 +286,124 @@ class ELSA:
class ApparatData: class ApparatData:
prof: Prof = field(default_factory=Prof) prof: Prof = field(default_factory=Prof)
apparat: Apparat = field(default_factory=Apparat) 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 []

312
src/logic/lehmannsapi.py Normal file
View File

@@ -0,0 +1,312 @@
from __future__ import annotations
import re
from dataclasses import asdict, dataclass, field
from typing import Iterable, List, Optional
from urllib.parse import quote_plus, urljoin
import httpx
from bs4 import BeautifulSoup
from src.logic.dataclass import BookData
BASE = "https://www.lehmanns.de"
SEARCH_URL = "https://www.lehmanns.de/search/quick?mediatype_id=&q="
@dataclass
class LehmannsSearchResult:
title: str
url: str
# Core fields from the listing card
year: Optional[int] = None
edition: Optional[int] = None
publisher: Optional[str] = None
isbn13: Optional[str] = None
# Extras from the listing card
description: Optional[str] = None
authors: list[str] = field(default_factory=list)
media_type: Optional[str] = None
book_format: Optional[str] = None
price_eur: Optional[float] = None
currency: str = "EUR"
image: Optional[str] = None
# From detail page:
pages: Optional[str] = None # "<N> Seiten"
buyable: bool = True # set in enrich_pages (detail page)
unavailable_hint: Optional[str] = (
None # e.g. "Titel ist leider vergriffen; keine Neuauflage"
)
def to_dict(self) -> dict:
return asdict(self)
class LehmannsClient:
"""Scrapes quick-search results, then enriches (and filters) via product pages."""
def __init__(self, timeout: float = 20.0):
self.client = httpx.Client(
headers={
"User-Agent": (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/124.0 Safari/537.36"
),
"Accept-Language": "de-DE,de;q=0.9,en;q=0.8",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
},
timeout=timeout,
follow_redirects=True,
)
def close(self):
self.client.close()
def __enter__(self):
return self
def __exit__(self, *exc):
self.close()
# ------------------- Search (listing) -------------------
def build_search_url(self, title: str) -> str:
# spaces -> '+'
return SEARCH_URL + quote_plus(title)
def search_by_title(
self,
title: str,
limit: Optional[int] = None,
strict: bool = False,
only_latest: bool = True,
) -> List[BookData]:
"""
Parse the listing page only (no availability check here).
Use enrich_pages(...) afterwards to fetch detail pages, add 'pages',
and drop unbuyable items.
"""
url = self.build_search_url(title=title)
html = self._get(url)
if not html:
return []
results = self._parse_results(html)
self.enrich_pages(results)
results = [BookData().from_LehmannsSearchResult(r) for r in results]
if strict:
# filter results to only those with exact title match (case-insensitive)
title_lower = title.lower()
results = [r for r in results if r.title and r.title.lower() == title_lower]
# results = [r for r in results if r.buyable]
return results
if limit is not None:
results = results[: max(0, limit)]
if only_latest and len(results) > 1:
# keep only the latest edition (highest edition number)
results.sort(key=lambda r: (r.edition_number or 0), reverse=True)
results = [results[0]]
return results
# ------------------- Detail enrichment & filtering -------------------
def enrich_pages(
self, results: Iterable[LehmannsSearchResult], drop_unbuyable: bool = True
) -> List[LehmannsSearchResult]:
"""
Fetch each result.url, extract:
- pages: from <span class="book-meta meta-seiten" itemprop="numberOfPages">...</span>
- availability: from <li class="availability-3">...</li>
* if it contains "Titel ist leider vergriffen", mark buyable=False
* if it also contains "keine Neuauflage", set unavailable_hint accordingly
If drop_unbuyable=True, exclude non-buyable results from the returned list.
"""
enriched: List[LehmannsSearchResult] = []
for r in results:
try:
html = self._get(r.url)
if not html:
# Can't verify; keep as-is when not dropping, else skip
if not drop_unbuyable:
enriched.append(r)
continue
soup = BeautifulSoup(html, "html.parser") # type: ignore
# Pages
pages_node = soup.select_one( # type: ignore
"span.book-meta.meta-seiten[itemprop='numberOfPages'], "
"span.book-meta.meta-seiten[itemprop='numberofpages'], "
".meta-seiten [itemprop='numberOfPages'], "
".meta-seiten[itemprop='numberOfPages'], "
".book-meta.meta-seiten"
)
if pages_node:
text = pages_node.get_text(" ", strip=True)
m = re.search(r"\d+", text)
if m:
r.pages = f"{m.group(0)} Seiten"
# Availability via li.availability-3
avail_li = soup.select_one("li.availability-3") # type: ignore
if avail_li:
avail_text = " ".join(
avail_li.get_text(" ", strip=True).split()
).lower()
if "titel ist leider vergriffen" in avail_text:
r.buyable = False
if "keine neuauflage" in avail_text:
r.unavailable_hint = (
"Titel ist leider vergriffen; keine Neuauflage"
)
else:
r.unavailable_hint = "Titel ist leider vergriffen"
# Append or drop
if (not drop_unbuyable) or r.buyable:
enriched.append(r)
except Exception:
# On any per-item error, keep the record if not dropping; else skip
if not drop_unbuyable:
enriched.append(r)
continue
return enriched
# ------------------- Internals -------------------
def _get(self, url: str) -> Optional[str]:
try:
r = self.client.get(url)
r.encoding = "utf-8"
if r.status_code == 200 and "text/html" in (
r.headers.get("content-type") or ""
):
return r.text
except httpx.HTTPError:
pass
return None
def _parse_results(self, html: str) -> List[LehmannsSearchResult]:
soup = BeautifulSoup(html, "html.parser")
results: list[LehmannsSearchResult] = []
for block in soup.select("div.info-block"):
a = block.select_one(".title a[href]")
if not a:
continue
url = urljoin(BASE, a["href"].strip())
base_title = (block.select_one(".title [itemprop='name']") or a).get_text( # type: ignore
strip=True
)
# Alternative headline => extend title
alt_tag = block.select_one(".description[itemprop='alternativeHeadline']") # type: ignore
alternative_headline = alt_tag.get_text(strip=True) if alt_tag else None
title = (
f"{base_title} : {alternative_headline}"
if alternative_headline
else base_title
)
description = alternative_headline
# Authors from .author
authors: list[str] = []
author_div = block.select_one("div.author") # type: ignore
if author_div:
t = author_div.get_text(" ", strip=True)
t = re.sub(r"^\s*von\s+", "", t, flags=re.I)
for part in re.split(r"\s*;\s*|\s*&\s*|\s+und\s+", t):
name = " ".join(part.split())
if name:
authors.append(name)
# Media + format
media_type = None
book_format = None
type_text = block.select_one(".type") # type: ignore
if type_text:
t = type_text.get_text(" ", strip=True)
m = re.search(r"\b(Buch|eBook|Hörbuch)\b", t)
if m:
media_type = m.group(1)
fm = re.search(r"\(([^)]+)\)", t)
if fm:
book_format = fm.group(1).strip().upper()
# Year
year = None
y = block.select_one("[itemprop='copyrightYear']") # type: ignore
if y:
try:
year = int(y.get_text(strip=True))
except ValueError:
pass
# Edition
edition = None
ed = block.select_one("[itemprop='bookEdition']") # type: ignore
if ed:
m = re.search(r"\d+", ed.get_text(strip=True))
if m:
edition = int(m.group())
# Publisher
publisher = None
pub = block.select_one( # type: ignore
".publisherprop [itemprop='name']"
) or block.select_one(".publisher [itemprop='name']") # type: ignore
if pub:
publisher = pub.get_text(strip=True)
# ISBN-13
isbn13 = None
isbn_tag = block.select_one(".isbn [itemprop='isbn'], [itemprop='isbn']") # type: ignore
if isbn_tag:
digits = re.sub(r"[^0-9Xx]", "", isbn_tag.get_text(strip=True))
m = re.search(r"(97[89]\d{10})", digits)
if m:
isbn13 = m.group(1)
# Price (best effort)
price_eur = None
txt = block.get_text(" ", strip=True)
mprice = re.search(r"(\d{1,3}(?:\.\d{3})*,\d{2})\s*€", txt)
if not mprice and block.parent:
sib = block.parent.get_text(" ", strip=True)
mprice = re.search(r"(\d{1,3}(?:\.\d{3})*,\d{2})\s*€", sib)
if mprice:
num = mprice.group(1).replace(".", "").replace(",", ".")
try:
price_eur = float(num)
except ValueError:
pass
# Image (best-effort)
image = None
left_img = block.find_previous("img") # type: ignore
if left_img and left_img.get("src"):
image = urljoin(BASE, left_img["src"])
results.append(
LehmannsSearchResult(
title=title,
url=url,
description=description,
authors=authors,
media_type=media_type,
book_format=book_format,
year=year,
edition=edition,
publisher=publisher,
isbn13=isbn13,
price_eur=price_eur,
image=image,
)
)
return results

View File

@@ -1,10 +1,12 @@
from openai import OpenAI
from src import settings
import json import json
from typing import Any
from openai import OpenAI
from src import settings
def init_client() -> OpenAI:
def init_client():
"""Initialize the OpenAI client with the API key and model from settings.""" """Initialize the OpenAI client with the API key and model from settings."""
global client, model, api_key global client, model, api_key
if not settings.openAI.api_key: if not settings.openAI.api_key:
@@ -16,9 +18,11 @@ def init_client():
api_key = settings.openAI.api_key api_key = settings.openAI.api_key
client = OpenAI(api_key=api_key) client = OpenAI(api_key=api_key)
return client return client
def run_shortener(title:str, length:int):
def run_shortener(title: str, length: int) -> list[dict[str, Any]]:
client = init_client() client = init_client()
response = client.responses.create( response = client.responses.create( # type: ignore
model=model, model=model,
instructions="""you are a sentence shortener. The next message will contain the string to shorten and the length limit. instructions="""you are a sentence shortener. The next message will contain the string to shorten and the length limit.
You need to shorten the string to be under the length limit, while keeping as much detail as possible. The result may NOT be longer than the length limit. You need to shorten the string to be under the length limit, while keeping as much detail as possible. The result may NOT be longer than the length limit.
@@ -27,26 +31,27 @@ based on that, please reply only the shortened string. Give me 5 choices. if the
) )
answers = response.output_text answers = response.output_text
return eval(answers) # type: ignore return eval(answers) # type: ignore
#answers are strings in json format, so we need to convert them to a list of dicts # answers are strings in json format, so we need to convert them to a list of dicts
def name_tester(name: str): def name_tester(name: str) -> dict:
client = init_client() client = init_client()
response = client.responses.create( response = client.responses.create( # type: ignore
model = model, model=model,
instructions="""you are a name tester, You are given a name and will have to split the name into first name, last name, and if present the title. Return the name in a json format with the keys "title", "first_name", "last_name". If no title is present, set title to none. Do NOt return the answer in a codeblock, use a pure json string. Assume the names are in the usual german naming scheme""", instructions="""you are a name tester, You are given a name and will have to split the name into first name, last name, and if present the title. Return the name in a json format with the keys "title", "first_name", "last_name". If no title is present, set title to none. Do NOt return the answer in a codeblock, use a pure json string. Assume the names are in the usual german naming scheme""",
input = f'{{"name":"{name}"}}' input=f'{{"name":"{name}"}}',
) )
answers = response.output_text answers = response.output_text
return json.loads(answers) return json.loads(answers)
def semester_converter(semester:str):
def semester_converter(semester: str) -> str:
client = init_client() client = init_client()
response = client.responses.create( response = client.responses.create( # type: ignore
model = model, model=model,
instructions="""you are a semester converter. You will be given a string. Convert this into a string like this: SoSe YY or WiSe YY/YY+1. Do not return the answer in a codeblock, use a pure string.""", instructions="""you are a semester converter. You will be given a string. Convert this into a string like this: SoSe YY or WiSe YY/YY+1. Do not return the answer in a codeblock, use a pure string.""",
input = semester input=semester,
) )
answers = response.output_text answers = response.output_text

View File

@@ -1,10 +1,9 @@
# add depend path to system path # add depend path to system path
import pandas as pd
from pdfquery import PDFQuery from pdfquery import PDFQuery
def pdf_to_csv(path: str) -> pd.DataFrame: def pdf_to_csv(path: str) -> str:
""" """
Extracts the data from a pdf file and returns it as a pandas dataframe Extracts the data from a pdf file and returns it as a pandas dataframe
""" """
@@ -21,4 +20,4 @@ if __name__ == "__main__":
text = pdf_to_csv("54_pdf.pdf") text = pdf_to_csv("54_pdf.pdf")
# remove linebreaks # remove linebreaks
text = text.replace("\n", "") text = text.replace("\n", "")
print(text) # print(text)

View File

@@ -15,20 +15,13 @@ Key points
""" """
from __future__ import annotations from __future__ import annotations
import datetime import datetime
import re import re
from dataclasses import dataclass
import loguru from src.shared.logging import log
import sys
from src import LOG_DIR
log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
# @dataclass
class Semester: class Semester:
"""Represents a German university semester (WiSe or SoSe).""" """Represents a German university semester (WiSe or SoSe)."""
@@ -123,21 +116,22 @@ class Semester:
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Comparison helpers # Comparison helpers
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def isPastSemester(self, other: "Semester") -> bool: def isPastSemester(self, current: "Semester") -> bool:
if self.year < other.year: log.debug(f"Comparing {self} < {current}")
if self.year < current.year:
return True return True
if self.year == other.year: if self.year == current.year:
return ( return (
self.semester == "WiSe" and other.semester == "SoSe" self.semester == "WiSe" and current.semester == "SoSe"
) # WiSe before next SoSe ) # WiSe before next SoSe
return False return False
def isFutureSemester(self, other: "Semester") -> bool: def isFutureSemester(self, current: "Semester") -> bool:
if self.year > other.year: if self.year > current.year:
return True return True
if self.year == other.year: if self.year == current.year:
return ( return (
self.semester == "SoSe" and other.semester == "WiSe" self.semester == "SoSe" and current.semester == "WiSe"
) # SoSe after WiSe of same year ) # SoSe after WiSe of same year
return False return False
@@ -235,8 +229,20 @@ if __name__ == "__main__":
s_start = Semester(6, "SoSe") # SoSe 6 s_start = Semester(6, "SoSe") # SoSe 6
s_end = Semester(25, "WiSe") # WiSe 25/26 s_end = Semester(25, "WiSe") # WiSe 25/26
chain = Semester.generate_missing(s_start, s_end) chain = Semester.generate_missing(s_start, s_end)
print("generate_missing:", [str(s) for s in chain]) # print("generate_missing:", [str(s) for s in chain])
# Parsing demo --------------------------------------------------------- # Parsing demo ---------------------------------------------------------
for label in ["SoSe 6", "WiSe 6/7", "wise 23/24", "WiSe 9"]: examples = [
print("from_string:", label, "", Semester.from_string(label)) "SoSe 6",
"WiSe 6/7",
"WiSe 6",
"SoSe 23",
"WiSe 23/24",
"WiSe 24",
"WiSe 99/00",
"SoSe 00",
"WiSe 100/101", # test large year
]
for ex in examples:
parsed = Semester.from_string(ex)
print(f"'{ex}'{parsed} ({parsed.year=}, {parsed.semester=})")

View File

@@ -13,7 +13,7 @@ class Settings:
default_apps: bool = True default_apps: bool = True
custom_applications: list[dict] = field(default_factory=list) custom_applications: list[dict] = field(default_factory=list)
def save_settings(self): def save_settings(self) -> None:
"""Save the settings to the config file.""" """Save the settings to the config file."""
with open("config.yaml", "w") as f: with open("config.yaml", "w") as f:
yaml.dump(self.__dict__, f) yaml.dump(self.__dict__, f)

View File

@@ -1,26 +1,19 @@
from typing import Any, Optional, Union
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
# import sleep_and_retry decorator to retry requests # import sleep_and_retry decorator to retry requests
from ratelimit import limits, sleep_and_retry from ratelimit import limits, sleep_and_retry
from typing import Union, Any, Optional
from src.logic.dataclass import BookData
from src.logic.dataclass import BookData
from src.shared.logging import log
from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
import loguru
import sys
from src import LOG_DIR
log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO") # logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
API_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndexrecord/{}/" API_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndexrecord/{}/"
PPN_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND" PPN_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND"
BASE = "https://rds.ibs-bw.de" BASE = "https://rds.ibs-bw.de"
@@ -58,14 +51,14 @@ class WebRequest:
log.info("Using any book") log.info("Using any book")
return self return self
def set_apparat(self, apparat: int): def set_apparat(self, apparat: int) -> "WebRequest":
self.apparat = apparat self.apparat = apparat
if int(self.apparat) < 10: if int(self.apparat) < 10:
self.apparat = f"0{self.apparat}" self.apparat = f"0{self.apparat}"
log.info(f"Set apparat to {self.apparat}") log.info(f"Set apparat to {self.apparat}")
return self return self
def get_ppn(self, signature: str): def get_ppn(self, signature: str) -> "WebRequest":
self.signature = signature self.signature = signature
if "+" in signature: if "+" in signature:
signature = signature.replace("+", "%2B") signature = signature.replace("+", "%2B")
@@ -80,6 +73,12 @@ class WebRequest:
response = requests.get(PPN_URL.format(searchterm), timeout=self.timeout) response = requests.get(PPN_URL.format(searchterm), timeout=self.timeout)
return response.text return response.text
@sleep_and_retry
@limits(calls=RATE_LIMIT, period=RATE_PERIOD)
def search_ppn(self, ppn: str) -> str:
response = requests.get(API_URL.format(ppn), timeout=self.timeout)
return response.text
def get_book_links(self, searchterm: str) -> list[str]: def get_book_links(self, searchterm: str) -> list[str]:
response: str = self.search_book(searchterm) # type:ignore response: str = self.search_book(searchterm) # type:ignore
soup = BeautifulSoup(response, "html.parser") soup = BeautifulSoup(response, "html.parser")
@@ -91,7 +90,7 @@ class WebRequest:
@sleep_and_retry @sleep_and_retry
@limits(calls=RATE_LIMIT, period=RATE_PERIOD) @limits(calls=RATE_LIMIT, period=RATE_PERIOD)
def search(self, link: str): def search(self, link: str) -> Optional[str]:
try: try:
response = requests.get(link, timeout=self.timeout) response = requests.get(link, timeout=self.timeout)
return response.text return response.text
@@ -99,7 +98,7 @@ class WebRequest:
log.error(f"Request failed: {e}") log.error(f"Request failed: {e}")
return None return None
def get_data(self) -> Union[list[str], None]: def get_data(self) -> Optional[list[str]]:
links = self.get_book_links(self.ppn) links = self.get_book_links(self.ppn)
log.debug(f"Links: {links}") log.debug(f"Links: {links}")
return_data: list[str] = [] return_data: list[str] = []
@@ -111,21 +110,8 @@ class WebRequest:
locations = soup.find_all("div", class_="col-xs-12 rds-dl RDS_LOCATION") locations = soup.find_all("div", class_="col-xs-12 rds-dl RDS_LOCATION")
if locations: if locations:
for location in locations: for location in locations:
item_location = location.find( if "1. OG Semesterapparat" in location.text:
"div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel" log.success("Found Semesterapparat, adding entry")
).text.strip()
log.debug(f"Item location: {item_location}")
if self.use_any:
pre_tag = soup.find_all("pre")
if pre_tag:
for tag in pre_tag:
data = tag.text.strip()
return_data.append(data)
return return_data
else:
log.error("No <pre> tag found")
raise ValueError("No <pre> tag found")
elif f"Semesterapparat-{self.apparat}" in item_location:
pre_tag = soup.find_all("pre") pre_tag = soup.find_all("pre")
return_data = [] return_data = []
if pre_tag: if pre_tag:
@@ -137,14 +123,40 @@ class WebRequest:
log.error("No <pre> tag found") log.error("No <pre> tag found")
return return_data return return_data
else: else:
log.error( item_location = location.find(
f"Signature {self.signature} not found in {item_location}" "div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel"
) ).text.strip()
# return_data = [] log.debug(f"Item location: {item_location}")
if self.use_any:
pre_tag = soup.find_all("pre")
if pre_tag:
for tag in pre_tag:
data = tag.text.strip()
return_data.append(data)
return return_data
else:
log.error("No <pre> tag found")
raise ValueError("No <pre> tag found")
elif f"Semesterapparat-{self.apparat}" in item_location:
pre_tag = soup.find_all("pre")
return_data = []
if pre_tag:
for tag in pre_tag:
data = tag.text.strip()
return_data.append(data)
return return_data
else:
log.error("No <pre> tag found")
return return_data
else:
log.error(
f"Signature {self.signature} not found in {item_location}"
)
# return_data = []
return return_data return return_data
def get_data_elsa(self): def get_data_elsa(self) -> Optional[list[str]]:
links = self.get_book_links(self.ppn) links = self.get_book_links(self.ppn)
for link in links: for link in links:
result = self.search(link) result = self.search(link)
@@ -185,12 +197,12 @@ class BibTextTransformer:
self.data = None self.data = None
# self.bookdata = BookData(**self.data) # self.bookdata = BookData(**self.data)
def use_signature(self, signature: str): def use_signature(self, signature: str) -> "BibTextTransformer":
"""use the exact signature to search for the book""" """use the exact signature to search for the book"""
self.signature = signature self.signature = signature
return self return self
def get_data(self, data: Union[list[str]] = None) -> "BibTextTransformer": def get_data(self, data: Optional[list[str]] = None) -> "BibTextTransformer":
RIS_IDENT = "TY -" RIS_IDENT = "TY -"
ARRAY_IDENT = "[kid]" ARRAY_IDENT = "[kid]"
COinS_IDENT = "ctx_ver" COinS_IDENT = "ctx_ver"

View File

@@ -1,131 +1,13 @@
import sys
import zipfile import zipfile
from dataclasses import dataclass from typing import Any, Optional
from typing import Any, Union
import loguru import fitz # PyMuPDF
import pandas as pd import pandas as pd
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from docx import Document from docx import Document
from src import LOG_DIR from src.logic.dataclass import Book, SemapDocument
from src.backend import Semester from src.shared.logging import log
from src.logic.openai import name_tester, run_shortener, semester_converter
log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@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:
log.warning("Title is too long")
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 ", Dauer" in self.semester:
self.semester = self.semester.split(",")[0]
self.eternal = True
self.semester = Semester().from_string(self.semester)
else:
log.warning("Semester {} is not valid", self.semester)
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 []
def word_docx_to_csv(path: str) -> list[pd.DataFrame]: def word_docx_to_csv(path: str) -> list[pd.DataFrame]:
@@ -141,8 +23,8 @@ def word_docx_to_csv(path: str) -> list[pd.DataFrame]:
text = text.replace("\n", "") text = text.replace("\n", "")
row_data.append(text) row_data.append(text)
if text == "Ihr Fach:": # if text == "Ihr Fach:":
row_data.append(get_fach(path)) # row_data.append(get_fach(path))
data.append(row_data) data.append(row_data)
df = pd.DataFrame(data) df = pd.DataFrame(data)
df.columns = df.iloc[0] df.columns = df.iloc[0]
@@ -153,7 +35,7 @@ def word_docx_to_csv(path: str) -> list[pd.DataFrame]:
return m_data return m_data
def get_fach(path: str) -> str: def get_fach(path: str) -> Optional[str]:
document = zipfile.ZipFile(path) document = zipfile.ZipFile(path)
xml_data = document.read("word/document.xml") xml_data = document.read("word/document.xml")
document.close() document.close()
@@ -161,17 +43,18 @@ def get_fach(path: str) -> str:
soup = BeautifulSoup(xml_data, "xml") soup = BeautifulSoup(xml_data, "xml")
# text we need is in <w:p w14:paraId="12456A32" ... > -> w:r -> w:t # text we need is in <w:p w14:paraId="12456A32" ... > -> w:r -> w:t
paragraphs = soup.find_all("w:p") paragraphs = soup.find_all("w:p")
names = []
for para in paragraphs: for para in paragraphs:
para_id = para.get("w14:paraId") para_id = para.get("w14:paraId")
if para_id == "12456A32": if para_id == "12456A32":
# get the data in the w:t # get the data in the w:t
for run in para.find_all("w:r"): for run in para.find_all("w:r"):
data = run.find("w:t") data = run.find("w:t")
return data.contents[0] if data and data.contents:
return data.contents[0]
return None
def makeDict(): def makeDict() -> dict[str, Optional[str]]:
return { return {
"work_author": None, "work_author": None,
"section_author": None, "section_author": None,
@@ -189,8 +72,8 @@ def makeDict():
} }
def tuple_to_dict(tlist: tuple, type: str) -> dict: def tuple_to_dict(tlist: tuple, type: str) -> list[dict[str, Optional[str]]]:
ret = [] ret: list[dict[str, Optional[str]]] = []
for line in tlist: for line in tlist:
data = makeDict() data = makeDict()
if type == "Monografien": if type == "Monografien":
@@ -230,7 +113,7 @@ def tuple_to_dict(tlist: tuple, type: str) -> dict:
return ret return ret
def elsa_word_to_csv(path: str): def elsa_word_to_csv(path: str) -> tuple[list[dict[str, Optional[str]]], str]:
doc = Document(path) doc = Document(path)
# # print all lines in doc # # print all lines in doc
doctype = [para.text for para in doc.paragraphs if para.text != ""][-1] doctype = [para.text for para in doc.paragraphs if para.text != ""][-1]
@@ -265,14 +148,14 @@ def elsa_word_to_csv(path: str):
return tuple_to_dict(data, doctype), doctype return tuple_to_dict(data, doctype), doctype
def word_to_semap(word_path: str) -> SemapDocument: def word_to_semap(word_path: str, ai: bool = True) -> SemapDocument:
log.info("Parsing Word Document {}", word_path) log.info("Parsing Word Document {}", word_path)
semap = SemapDocument() semap = SemapDocument()
df = word_docx_to_csv(word_path) df = word_docx_to_csv(word_path)
apparatdata = df[0] apparatdata = df[0]
apparatdata = apparatdata.to_dict() apparatdata = apparatdata.to_dict()
keys = list(apparatdata.keys()) keys = list(apparatdata.keys())
print(apparatdata, keys) # print(apparatdata, keys)
appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys) - 1, 2)} appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys) - 1, 2)}
semap.phoneNumber = appdata["Telefon:"] semap.phoneNumber = appdata["Telefon:"]
@@ -286,8 +169,9 @@ def word_to_semap(word_path: str) -> SemapDocument:
appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys), 2)} appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys), 2)}
semap.title = appdata["Veranstaltung:"] semap.title = appdata["Veranstaltung:"]
semap.semester = appdata["Semester:"] semap.semester = appdata["Semester:"]
semap.renameSemester if ai:
semap.nameSetter semap.renameSemester
semap.nameSetter
books = df[2] books = df[2]
booklist = [] booklist = []
@@ -308,8 +192,182 @@ def word_to_semap(word_path: str) -> SemapDocument:
return semap return semap
if __name__ == "__main__": def pdf_to_semap(pdf_path: str, ai: bool = True) -> SemapDocument:
else_df = elsa_word_to_csv( """
"C:/Users/aky547/Desktop/ELSA_Bestellung Scann Der Westen und der Rest.docx" Parse a Semesterapparat PDF like the sample you provided and return a SemapDocument.
- No external programs, only PyMuPDF.
- Robust to multi-line field values (e.g., hyphenated emails) and multi-line table cells.
- Works across multiple pages; headers only need to exist on the first page.
"""
doc = fitz.open(pdf_path)
semap = SemapDocument()
# ---------- helpers ----------
def _join_tokens(tokens: list[str]) -> str:
"""Join tokens, preserving hyphen/URL joins across line wraps."""
parts = []
for tok in tokens:
if parts and (
parts[-1].endswith("-")
or parts[-1].endswith("/")
or parts[-1].endswith(":")
):
parts[-1] = parts[-1] + tok # no space after '-', '/' or ':'
else:
parts.append(tok)
return " ".join(parts).strip()
def _extract_row_values_multiline(
page, labels: list[str], y_window: float = 24
) -> dict[str, str]:
"""For a row of inline labels (e.g., Name/Fach/Telefon/Mail), grab text to the right of each label."""
rects = []
for lab in labels:
hits = page.search_for(lab)
if hits:
rects.append((lab, hits[0]))
if not rects:
return {}
rects.sort(key=lambda t: t[1].x0)
words = page.get_text("words")
out = {}
for i, (lab, r) in enumerate(rects):
x0 = r.x1 + 1
x1 = rects[i + 1][1].x0 - 1 if i + 1 < len(rects) else page.rect.width - 5
y0 = r.y0 - 3
y1 = r.y0 + y_window
toks = [w for w in words if x0 <= w[0] <= x1 and y0 <= w[1] <= y1]
toks.sort(key=lambda w: (w[1], w[0])) # line, then x
out[lab] = _join_tokens([w[4] for w in toks])
return out
def _compute_columns_from_headers(page0):
"""Find column headers (once) and derive column centers + header baseline."""
headers = [
("Autorenname(n):", "Autorenname(n):Nachname, Vorname"),
("Jahr/Auflage", "Jahr/Auflage"),
("Titel", "Titel"),
("Ort und Verlag", "Ort und Verlag"),
("Standnummer", "Standnummer"),
("Interne Vermerke", "Interne Vermerke"),
]
found = []
for label, canon in headers:
rects = [
r for r in page0.search_for(label) if r.y0 > 200
] # skip top-of-form duplicates
if rects:
found.append((canon, rects[0]))
found.sort(key=lambda t: t[1].x0)
cols = [(canon, r.x0, r.x1, (r.x0 + r.x1) / 2.0) for canon, r in found]
header_y = min(r.y0 for _, r in found) if found else 0
return cols, header_y
def _extract_table_rows_from_page(
page, cols, header_y, y_top_margin=5, y_bottom_margin=40, y_tol=26.0
):
"""
Group words into logical rows (tolerant to wrapped lines), then map each word
to the nearest column by x-center and join tokens per column.
"""
words = [
w
for w in page.get_text("words")
if w[1] > header_y + y_top_margin
and w[3] < page.rect.height - y_bottom_margin
]
# group into row bands by y (tolerance big enough to capture wrapped lines, but below next row gap)
rows = []
for w in sorted(words, key=lambda w: w[1]):
y = w[1]
for row in rows:
if abs(row["y_mean"] - y) <= y_tol:
row["ys"].append(y)
row["y_mean"] = sum(row["ys"]) / len(row["ys"])
row["words"].append(w)
break
else:
rows.append({"y_mean": y, "ys": [y], "words": [w]})
# map to columns + join
joined_rows = []
for row in rows:
rowdict = {canon: "" for canon, *_ in cols}
words_by_col = {canon: [] for canon, *_ in cols}
for w in sorted(row["words"], key=lambda w: (w[1], w[0])):
xmid = (w[0] + w[2]) / 2.0
canon = min(cols, key=lambda c: abs(xmid - c[3]))[0]
words_by_col[canon].append(w[4])
for canon, toks in words_by_col.items():
rowdict[canon] = _join_tokens(toks)
if any(v for v in rowdict.values()):
joined_rows.append(rowdict)
return joined_rows
# ---------- top-of-form fields ----------
p0 = doc[0]
row1 = _extract_row_values_multiline(
p0,
["Ihr Name und Titel:", "Ihr Fach:", "Telefon:", "Mailadresse:"],
y_window=22,
) )
print(else_df) row2 = _extract_row_values_multiline(
p0, ["Veranstaltung:", "Semester:"], y_window=20
)
name_title = row1.get("Ihr Name und Titel:", "") or ""
semap.subject = row1.get("Ihr Fach:", None)
semap.phoneNumber = row1.get("Telefon:", None) # keep as-is (string like "682-308")
semap.mail = row1.get("Mailadresse:", None)
semap.personName = ",".join(name_title.split(",")[:-1]) if name_title else None
semap.personTitle = (
",".join(name_title.split(",")[-1:]).strip() if name_title else None
)
semap.title = row2.get("Veranstaltung:", None)
semap.semester = row2.get("Semester:", None)
# ---------- table extraction (all pages) ----------
cols, header_y = _compute_columns_from_headers(p0)
all_rows: list[dict[str, Any]] = []
for pn in range(len(doc)):
all_rows.extend(_extract_table_rows_from_page(doc[pn], cols, header_y))
# drop the sub-header line "Nachname, Vorname" etc.
filtered = []
for r in all_rows:
if r.get("Autorenname(n):Nachname, Vorname", "").strip() in (
"",
"Nachname, Vorname",
):
# skip if it's just the sub-header line
if all(not r[c] for c in r if c != "Autorenname(n):Nachname, Vorname"):
continue
filtered.append(r)
# build Book objects (same filters as your word parser)
booklist: list[Book] = []
for row in filtered:
b = Book()
b.from_dict(row)
if b.is_empty:
continue
if not b.has_signature:
continue
booklist.append(b)
semap.books = booklist
# keep parity with your post-processing
if ai:
_ = semap.renameSemester
_ = semap.nameSetter
return semap
if __name__ == "__main__":
else_df = pdf_to_semap("C:/Users/aky547/Dokumente/testsemap.pdf")
# print(else_df)

67
src/logic/xmlparser.py Normal file
View File

@@ -0,0 +1,67 @@
import xml.etree.ElementTree as ET
from src.logic.dataclass import Apparat, BookData, SemapDocument, XMLMailSubmission
from src.logic.semester import Semester
def parse_xml_submission(xml_string: str) -> XMLMailSubmission:
"""
Parse an XML string representing a mail submission and return an XMLMailSubmission object.
"""
submission = XMLMailSubmission()
root = ET.fromstring(xml_string)
static_data = root.find("static")
static_info = {child.tag: child.text for child in static_data}
books = root.find("books")
books_info = []
for book in books:
book_details = {detail.tag: detail.text for detail in book}
book = BookData(
author=book_details.get("authorname"),
year=book_details.get("year").split("/")[0]
if "/" in book_details.get("year")
else book_details.get("year"),
edition=book_details.get("year").split("/")[1]
if "/" in book_details.get("year")
else None,
title=book_details.get("title"),
signature=book_details.get("signature"),
)
books_info.append(book)
# Extract static data
submission.name = static_info.get("name")
submission.lastname = static_info.get("lastname")
submission.title = static_info.get("title")
submission.telno = int(static_info.get("telno"))
submission.email = static_info.get("mail")
submission.app_name = static_info.get("apparatsname")
submission.subject = static_info.get("subject")
sem_year = static_info.get("semester").split()[1]
sem_term = static_info.get("semester").split()[0]
submission.semester = Semester(semester=sem_term, year=int(sem_year))
submission.books = books_info
# Extract book information
# book_info = []
# for book in books:
# book_details = {detail.tag: detail.text for detail in book}
# book_info.append(book_details)
return submission
def eml_parser(path: str) -> XMLMailSubmission:
with open(path, "r", encoding="utf-8") as file:
xml_content = file.read().split("\n\n", 1)[1] # Skip headers
print("EML content loaded, parsing XML...")
print(xml_content)
return parse_xml_submission(xml_content)
def eml_to_semap(xml_mail: XMLMailSubmission) -> SemapDocument:
submission = eml_parser(xml_mail)
semap_doc = SemapDocument(
# prof=Prof(name=submission.name, lastname=submission.lastname, email=submission.email),
apparat=Apparat(name=submission.app_name, subject=submission.subject),
semester=submission.semester,
books=submission.books,
)
return semap_doc

View File

@@ -1,7 +1,10 @@
from pyzotero import zotero
from dataclasses import dataclass from dataclasses import dataclass
from src.logic.webrequest import WebRequest, BibTextTransformer from typing import Optional
from pyzotero import zotero
from src import settings from src import settings
from src.logic.webrequest import BibTextTransformer, WebRequest
@dataclass @dataclass
@@ -10,11 +13,11 @@ class Creator:
lastName: str = None lastName: str = None
creatorType: str = "author" creatorType: str = "author"
def from_dict(self, data: dict): def from_dict(self, data: dict) -> None:
for key, value in data.items(): for key, value in data.items():
setattr(self, key, value) setattr(self, key, value)
def from_string(self, data: str): def from_string(self, data: str) -> "Creator":
if "," in data: if "," in data:
self.firstName = data.split(",")[1] self.firstName = data.split(",")[1]
self.lastName = data.split(",")[0] self.lastName = data.split(",")[0]
@@ -54,7 +57,7 @@ class Book:
rights: str = None rights: str = None
extra: str = None extra: str = None
def to_dict(self): def to_dict(self) -> dict:
ret = {} ret = {}
for key, value in self.__dict__.items(): for key, value in self.__dict__.items():
if value: if value:
@@ -93,14 +96,14 @@ class BookSection:
collections = list collections = list
relations = dict relations = dict
def to_dict(self): def to_dict(self) -> dict:
ret = {} ret = {}
for key, value in self.__dict__.items(): for key, value in self.__dict__.items():
if value: if value:
ret[key] = value ret[key] = value
return ret return ret
def assign(self, book): def assign(self, book) -> None:
for key, value in book.__dict__.items(): for key, value in book.__dict__.items():
if key in self.__dict__.keys(): if key in self.__dict__.keys():
try: try:
@@ -140,14 +143,14 @@ class JournalArticle:
collections = list collections = list
relations = dict relations = dict
def to_dict(self): def to_dict(self) -> dict:
ret = {} ret = {}
for key, value in self.__dict__.items(): for key, value in self.__dict__.items():
if value: if value:
ret[key] = value ret[key] = value
return ret return ret
def assign(self, book: dict): def assign(self, book: dict) -> None:
for key, value in book.__dict__.items(): for key, value in book.__dict__.items():
if key in self.__dict__.keys(): if key in self.__dict__.keys():
try: try:
@@ -162,15 +165,15 @@ class ZoteroController:
def __init__(self): def __init__(self):
if self.zoterocfg.library_id is None: if self.zoterocfg.library_id is None:
return return
self.zot = zotero.Zotero( self.zot = zotero.Zotero( # type: ignore
self.zoterocfg.library_id, self.zoterocfg.library_id,
self.zoterocfg.library_type, self.zoterocfg.library_type,
self.zoterocfg.api_key, self.zoterocfg.api_key,
) )
def get_books(self): def get_books(self) -> list:
ret = [] ret = []
items = self.zot.top() items = self.zot.top() # type: ignore
for item in items: for item in items:
if item["data"]["itemType"] == "book": if item["data"]["itemType"] == "book":
ret.append(item) ret.append(item)
@@ -178,7 +181,7 @@ class ZoteroController:
# create item in zotero # create item in zotero
# item is a part of a book # item is a part of a book
def __get_data(self, isbn): def __get_data(self, isbn) -> dict:
web = WebRequest() web = WebRequest()
web.get_ppn(isbn) web.get_ppn(isbn)
data = web.get_data_elsa() data = web.get_data_elsa()
@@ -187,8 +190,8 @@ class ZoteroController:
book = bib.return_data() book = bib.return_data()
return book return book
# # print(zot.item_template("bookSection")) # # #print(zot.item_template("bookSection"))
def createBook(self, isbn): def createBook(self, isbn) -> Book:
book = self.__get_data(isbn) book = self.__get_data(isbn)
bookdata = Book() bookdata = Book()
@@ -207,23 +210,23 @@ class ZoteroController:
bookdata.creators = authors bookdata.creators = authors
return bookdata return bookdata
def createItem(self, item): def createItem(self, item) -> Optional[str]:
resp = self.zot.create_items([item]) resp = self.zot.create_items([item]) # type: ignore
if "successful" in resp.keys(): if "successful" in resp.keys():
# print(resp["successful"]["0"]["key"]) # #print(resp["successful"]["0"]["key"])
return resp["successful"]["0"]["key"] return resp["successful"]["0"]["key"]
else: else:
return None return None
def deleteItem(self, key): def deleteItem(self, key) -> None:
items = self.zot.items() items = self.zot.items()
for item in items: for item in items:
if item["key"] == key: if item["key"] == key:
self.zot.delete_item(item) self.zot.delete_item(item) # type: ignore
# print(item) # #print(item)
break break
def createHGSection(self, book: Book, data: dict): def createHGSection(self, book: Book, data: dict) -> Optional[str]:
chapter = BookSection() chapter = BookSection()
chapter.assign(book) chapter.assign(book)
chapter.pages = data["pages"] chapter.pages = data["pages"]
@@ -241,11 +244,11 @@ class ZoteroController:
] ]
chapter.creators += authors chapter.creators += authors
# print(chapter.to_dict()) # #print(chapter.to_dict())
return self.createItem(chapter.to_dict()) return self.createItem(chapter.to_dict())
pass pass
def createBookSection(self, book: Book, data: dict): def createBookSection(self, book: Book, data: dict) -> Optional[str]:
chapter = BookSection() chapter = BookSection()
chapter.assign(book) chapter.assign(book)
chapter.pages = data["pages"] chapter.pages = data["pages"]
@@ -256,8 +259,8 @@ class ZoteroController:
return self.createItem(chapter.to_dict()) return self.createItem(chapter.to_dict())
# chapter.creators # chapter.creators
def createJournalArticle(self, journal, article): def createJournalArticle(self, journal, article) -> Optional[str]:
# print(type(article)) # #print(type(article))
journalarticle = JournalArticle() journalarticle = JournalArticle()
journalarticle.assign(journal) journalarticle.assign(journal)
journalarticle.itemType = "journalArticle" journalarticle.itemType = "journalArticle"
@@ -273,12 +276,12 @@ class ZoteroController:
journalarticle.issue = article["issue"] journalarticle.issue = article["issue"]
journalarticle.url = article["isbn"] journalarticle.url = article["isbn"]
# print(journalarticle.to_dict()) # #print(journalarticle.to_dict())
return self.createItem(journalarticle.to_dict()) return self.createItem(journalarticle.to_dict())
def get_citation(self, item): def get_citation(self, item) -> str:
title = self.zot.item( title = self.zot.item( # type: ignore
item, item,
content="bib", content="bib",
style="deutsche-gesellschaft-fur-psychologie", style="deutsche-gesellschaft-fur-psychologie",
@@ -319,16 +322,16 @@ if __name__ == "__main__":
# if isinstance(publishers, str): # if isinstance(publishers, str):
# publishers = [publishers] # publishers = [publishers]
# for publisher in publishers: # for publisher in publishers:
# # print(publisher) # # #print(publisher)
# creator = Creator().from_string(publisher) # creator = Creator().from_string(publisher)
# creator.creatorType = "editor" # creator.creatorType = "editor"
# authors.append(creator.__dict__) # authors.append(creator.__dict__)
# chapter.creators = authors # chapter.creators = authors
# chapter.publisher = book.publisher # chapter.publisher = book.publisher
# # print(chapter.to_dict()) # # #print(chapter.to_dict())
# createBookSection(chapter.to_dict()) # createBookSection(chapter.to_dict())
# get_citation("9ZXH8DDE") # get_citation("9ZXH8DDE")
# # # print() # # # #print()
# # print(get_books()) # # #print(get_books())
# # print(zot.item_creator_types("bookSection")) # # #print(zot.item_creator_types("bookSection"))

25
src/shared/logging.py Normal file
View File

@@ -0,0 +1,25 @@
import sys
import loguru
from src import LOG_DIR
log = loguru.logger
_configured = False
def configure(level: str = "INFO", to_stdout: bool = True, rotate_bytes: str = "1 MB"):
global _configured
if _configured:
return log
log.remove()
if to_stdout:
log.add(sys.stdout, level=level)
# application rolling log
log.add(
f"{LOG_DIR}/application.log",
rotation=rotate_bytes,
retention="10 days",
)
_configured = True
return log

BIN
src/sounds/ding.mp3 Normal file

Binary file not shown.

BIN
src/sounds/error.mp3 Normal file

Binary file not shown.

View File

@@ -10,8 +10,9 @@ import hashlib
from PySide6 import QtCore, QtWidgets from PySide6 import QtCore, QtWidgets
from src.backend.database import Database
from src.backend.admin_console import AdminCommands from src.backend.admin_console import AdminCommands
from src.backend.database import Database
class Ui_Dialog(object): class Ui_Dialog(object):
def setupUi(self, Dialog): def setupUi(self, Dialog):
@@ -64,13 +65,11 @@ class Ui_Dialog(object):
def login(self): def login(self):
username = self.lineEdit.text() username = self.lineEdit.text()
password = self.lineEdit_2.text() password = self.lineEdit_2.text()
print(type(username), password) # print(type(username), password)
# Assuming 'Database' is a class to interact with your database # Assuming 'Database' is a class to interact with your database
db = Database() db = Database()
hashed_password = hashlib.sha256( hashed_password = hashlib.sha256(password.encode()).hexdigest()
password.encode()
).hexdigest()
if len(db.getUsers()) == 0: if len(db.getUsers()) == 0:
AdminCommands().create_admin() AdminCommands().create_admin()
self.lresult = 1 # Indicate successful login self.lresult = 1 # Indicate successful login

View File

@@ -6,17 +6,18 @@
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
import subprocess
import tempfile
import os import os
import re import re
import subprocess
import tempfile
from omegaconf import OmegaConf from omegaconf import OmegaConf
from PySide6 import QtCore, QtWidgets
config = OmegaConf.load("config.yaml") config = OmegaConf.load("config.yaml")
class Ui_eMailPreview(object):
class Ui_eMailPreview(object):
def setupUi( def setupUi(
self, self,
eMailPreview, eMailPreview,
@@ -31,7 +32,10 @@ class Ui_eMailPreview(object):
self.buttonBox = QtWidgets.QDialogButtonBox(eMailPreview) self.buttonBox = QtWidgets.QDialogButtonBox(eMailPreview)
self.buttonBox.setGeometry(QtCore.QRect(310, 630, 341, 32)) self.buttonBox.setGeometry(QtCore.QRect(310, 630, 341, 32))
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setObjectName("buttonBox")
self.gridLayoutWidget = QtWidgets.QWidget(eMailPreview) self.gridLayoutWidget = QtWidgets.QWidget(eMailPreview)
self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 661, 621)) self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 661, 621))
@@ -46,7 +50,11 @@ class Ui_eMailPreview(object):
self.prof_name.setObjectName("prof_name") self.prof_name.setObjectName("prof_name")
self.gridLayout.addWidget(self.prof_name, 2, 2, 1, 1) self.gridLayout.addWidget(self.prof_name, 2, 2, 1, 1)
self.label_3 = QtWidgets.QLabel(self.gridLayoutWidget) self.label_3 = QtWidgets.QLabel(self.gridLayoutWidget)
self.label_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop) self.label_3.setAlignment(
QtCore.Qt.AlignmentFlag.AlignLeading
| QtCore.Qt.AlignmentFlag.AlignLeft
| QtCore.Qt.AlignmentFlag.AlignTop
)
self.label_3.setObjectName("label_3") self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 5, 0, 1, 1) self.gridLayout.addWidget(self.label_3, 5, 0, 1, 1)
self.mail_name = QtWidgets.QLineEdit(self.gridLayoutWidget) self.mail_name = QtWidgets.QLineEdit(self.gridLayoutWidget)
@@ -81,7 +89,12 @@ class Ui_eMailPreview(object):
self.gender_non = QtWidgets.QRadioButton(self.gridLayoutWidget) self.gender_non = QtWidgets.QRadioButton(self.gridLayoutWidget)
self.gender_non.setObjectName("gender_non") self.gender_non.setObjectName("gender_non")
self.horizontalLayout_3.addWidget(self.gender_non) self.horizontalLayout_3.addWidget(self.gender_non)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_3.addItem(spacerItem) self.horizontalLayout_3.addItem(spacerItem)
self.gridLayout.addLayout(self.horizontalLayout_3, 4, 2, 1, 1) self.gridLayout.addLayout(self.horizontalLayout_3, 4, 2, 1, 1)
self.label_6 = QtWidgets.QLabel(self.gridLayoutWidget) self.label_6 = QtWidgets.QLabel(self.gridLayoutWidget)
@@ -89,8 +102,8 @@ class Ui_eMailPreview(object):
self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1) self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1)
self.retranslateUi(eMailPreview) self.retranslateUi(eMailPreview)
self.buttonBox.accepted.connect(eMailPreview.accept) # type: ignore self.buttonBox.accepted.connect(eMailPreview.accept) # type: ignore
self.buttonBox.rejected.connect(eMailPreview.reject) # type: ignore self.buttonBox.rejected.connect(eMailPreview.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(eMailPreview) QtCore.QMetaObject.connectSlotsByName(eMailPreview)
self._appid = app_id self._appid = app_id
self._appname = app_name self._appname = app_name
@@ -128,7 +141,6 @@ class Ui_eMailPreview(object):
elif self.gender_non.isChecked(): elif self.gender_non.isChecked():
return "Guten Tag" return "Guten Tag"
def set_mail(self): def set_mail(self):
email_template = self.comboBox.currentText() email_template = self.comboBox.currentText()
if email_template == "": if email_template == "":
@@ -145,14 +157,19 @@ class Ui_eMailPreview(object):
mail_html = mail_template.split("<html>")[1] mail_html = mail_template.split("<html>")[1]
mail_html = "<html>" + mail_html mail_html = "<html>" + mail_html
mail_html = mail_html.format( mail_html = mail_html.format(
Profname=self.prof_name.text().split(" ")[1], Appname=self._appname, AppNr=self._appid, AppSubject = self._subject,greeting = self.get_greeting() Profname=self.prof_name.text().split(" ")[1],
Appname=self._appname,
AppNr=self._appid,
AppSubject=self._subject,
greeting=self.get_greeting(),
) )
self.mail_body.setHtml(mail_html) self.mail_body.setHtml(mail_html)
def load_mail_templates(self): def load_mail_templates(self):
mail_templates = os.listdir("mail_vorlagen") mail_templates = os.listdir("mail_vorlagen")
mail_templates = [f for f in mail_templates if f.endswith(".eml")] mail_templates = [f for f in mail_templates if f.endswith(".eml")]
print(mail_templates) # print(mail_templates)
self.comboBox.addItems(mail_templates) self.comboBox.addItems(mail_templates)
def save_mail(self): def save_mail(self):
@@ -168,16 +185,17 @@ class Ui_eMailPreview(object):
) as f: ) as f:
f.write(mail) f.write(mail)
self.mail_path = f.name self.mail_path = f.name
print(self.mail_path) # print(self.mail_path)
# open the file using thunderbird # open the file using thunderbird
subprocess.Popen([f"{self.mail_path}"]) subprocess.Popen([f"{self.mail_path}"])
# delete the file # delete the file
# os.remove(self.mail_path) # os.remove(self.mail_path)
def launch(): def launch():
app = QtWidgets.QApplication([]) app = QtWidgets.QApplication([])
eMailPreview = QtWidgets.QDialog() eMailPreview = QtWidgets.QDialog()
ui = Ui_eMailPreview() ui = Ui_eMailPreview()
ui.setupUi(eMailPreview, "1","Test","Biologie","Kirchner, Alexander") ui.setupUi(eMailPreview, "1", "Test", "Biologie", "Kirchner, Alexander")
eMailPreview.show() eMailPreview.show()
app.exec() app.exec()

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
</TS>

View File

@@ -110,7 +110,7 @@ class Ui_Form(object):
self.progressBar.setValue(value) self.progressBar.setValue(value)
def thread_quit(self): def thread_quit(self):
print("Terminating thread") # print("Terminating thread")
self.thread.terminate() self.thread.terminate()
self.thread.quit() self.thread.quit()
self.thread.deleteLater() self.thread.deleteLater()
@@ -144,7 +144,7 @@ class Ui_Form(object):
def determine_progress(self, signal): def determine_progress(self, signal):
# check length of listWidget # check length of listWidget
length = self.listWidget.count() length = self.listWidget.count()
print(f"Length of listWidget: {length}") # print(f"Length of listWidget: {length}")
if length == 0: if length == 0:
logger.log_info("AutoAdder finished") logger.log_info("AutoAdder finished")
self.buttonBox.accepted.emit() self.buttonBox.accepted.emit()

View File

@@ -169,7 +169,7 @@ class Ui_Dialog(object):
name = application.application name = application.application
file_type = application.extensions file_type = application.extensions
display_name = application.name display_name = application.name
print(name, file_type, display_name) # # print(name, file_type, display_name) #
# create new item # create new item
item = QtWidgets.QTreeWidgetItem(self.treeWidget) item = QtWidgets.QTreeWidgetItem(self.treeWidget)
item.setText(0, display_name) item.setText(0, display_name)

View File

@@ -12,20 +12,23 @@ __all__ = [
"ElsaAddEntry", "ElsaAddEntry",
"ApparatExtendDialog", "ApparatExtendDialog",
"DocumentPrintDialog", "DocumentPrintDialog",
"NewEditionDialog",
"Settings", "Settings",
"DeleteDialog",
] ]
from .about import About
from .app_ext import ApparatExtendDialog
from .bookdata import BookDataUI from .bookdata import BookDataUI
from .deletedialog import DeleteDialog
from .docuprint import DocumentPrintDialog
from .elsa_add_entry import ElsaAddEntry
from .elsa_gen_confirm import ElsaGenConfirm
from .login import LoginDialog from .login import LoginDialog
from .mail import Mail_Dialog from .mail import Mail_Dialog
from .mailTemplate import MailTemplateDialog from .mailTemplate import MailTemplateDialog
from .medienadder import MedienAdder from .medienadder import MedienAdder
from .newEdition import NewEditionDialog
from .parsed_titles import ParsedTitles from .parsed_titles import ParsedTitles
from .popup_confirm import ConfirmDialog as popus_confirm from .popup_confirm import ConfirmDialog as popus_confirm
from .reminder import ReminderDialog from .reminder import ReminderDialog
from .about import About
from .elsa_gen_confirm import ElsaGenConfirm
from .elsa_add_entry import ElsaAddEntry
from .app_ext import ApparatExtendDialog
from .docuprint import DocumentPrintDialog
from .settings import Settings from .settings import Settings

View File

@@ -1,7 +1,9 @@
from .dialog_sources.Ui_about import Ui_about
from PySide6 import QtWidgets
import PySide6 import PySide6
from src import Icon, __version__, __author__ from PySide6 import QtWidgets
from src import Icon, __author__, __version__
from .dialog_sources.about_ui import Ui_about
class About(QtWidgets.QDialog, Ui_about): class About(QtWidgets.QDialog, Ui_about):

View File

@@ -1,7 +1,9 @@
from PySide6 import QtWidgets from PySide6 import QtWidgets
from .dialog_sources.Ui_apparat_extend import Ui_Dialog
from src import Icon from src import Icon
from .dialog_sources.apparat_extend_ui import Ui_Dialog
class ApparatExtendDialog(QtWidgets.QDialog, Ui_Dialog): class ApparatExtendDialog(QtWidgets.QDialog, Ui_Dialog):
def __init__( def __init__(

View File

@@ -2,7 +2,7 @@ from PySide6 import QtWidgets
from src.logic.dataclass import BookData from src.logic.dataclass import BookData
from .dialog_sources.Ui_edit_bookdata import Ui_Dialog from .dialog_sources.edit_bookdata_ui import Ui_Dialog
class BookDataUI(QtWidgets.QDialog, Ui_Dialog): class BookDataUI(QtWidgets.QDialog, Ui_Dialog):

View File

@@ -0,0 +1,129 @@
from typing import Any
from PySide6 import QtCore, QtWidgets
from src import Icon
from src.backend.database import Database
from .dialog_sources.deletedialog_ui import Ui_Dialog
class DeleteDialog(QtWidgets.QDialog, Ui_Dialog):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowTitle("Medien löschen")
self.setWindowIcon(Icon("trash").icon)
self.reset_btn.clicked.connect(self.reset_selection)
self.cancel_btn.clicked.connect(self.close)
self.delete_btn.clicked.connect(self.delete_selected)
self.db = Database()
self.books = self.setBooks()
self.lineEdit.textChanged.connect(self.populate_books)
self.populate_books()
def delete_selected(self):
to_delete = []
for row in range(self.tableWidget.rowCount()):
checkbox = self.tableWidget.cellWidget(row, 0)
if checkbox is not None and checkbox.isChecked():
book_id_item = self.tableWidget.item(row, 6)
if book_id_item is not None:
book_id = int(book_id_item.text())
to_delete.append(book_id)
if to_delete:
self.db.deleteBooks(to_delete)
self.accept()
def reset_selection(self):
for row in range(self.tableWidget.rowCount()):
checkbox = self.tableWidget.cellWidget(row, 0)
if checkbox is not None:
checkbox.setChecked(False)
def setBooks(self) -> list[dict[str, Any]]:
result: list[dict[str, Any]] = []
books = self.db.getAllBooks()
for book in books:
title = book["bookdata"].title
signature = book["bookdata"].signature
edition = book["bookdata"].edition
appnr = self.db.getApparatNrByBookId(book["id"])
result.append(
{
"id": book["id"],
"appnr": appnr,
"title": title,
"signature": signature,
"edition": edition,
}
)
return result
def populate_books(self):
searchterm = self.lineEdit.text().lower()
self.tableWidget.setRowCount(0)
for book in self.books:
checkbox = QtWidgets.QCheckBox()
app_nr = book["appnr"]
title = book["title"]
signature = book["signature"]
edition = book["edition"] if book["edition"] else ""
if searchterm in title.lower() or searchterm in signature.lower():
self.tableWidget.insertRow(self.tableWidget.rowCount())
self.tableWidget.setCellWidget(
self.tableWidget.rowCount() - 1, 0, checkbox
)
self.tableWidget.setItem(
self.tableWidget.rowCount() - 1,
1,
QtWidgets.QTableWidgetItem(str(app_nr)),
)
self.tableWidget.setItem(
self.tableWidget.rowCount() - 1,
2,
QtWidgets.QTableWidgetItem(signature),
)
self.tableWidget.setItem(
self.tableWidget.rowCount() - 1,
3,
QtWidgets.QTableWidgetItem(title),
)
self.tableWidget.setItem(
self.tableWidget.rowCount() - 1,
4,
QtWidgets.QTableWidgetItem(edition),
)
self.tableWidget.setItem(
self.tableWidget.rowCount() - 1,
5,
QtWidgets.QTableWidgetItem(""),
)
self.tableWidget.setItem(
self.tableWidget.rowCount() - 1,
6,
QtWidgets.QTableWidgetItem(str(book["id"])),
)
else:
continue
# set column signature to be 10px wider than the longest entry
self.tableWidget.setColumnWidth(1, 100)
self.tableWidget.setColumnWidth(2, 150)
self.tableWidget.setColumnWidth(3, 150)
self.tableWidget.setColumnWidth(4, 100)
self.tableWidget.setColumnWidth(0, 50)
# horizontal header 0 should be centered
self.tableWidget.horizontalHeader().setDefaultAlignment(
QtCore.Qt.AlignmentFlag.AlignCenter
)
def launch():
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication([])
dialog = DeleteDialog()
dialog.exec()

View File

@@ -1,38 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\about.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_about(object):
def setupUi(self, about):
about.setObjectName("about")
about.resize(301, 313)
self.verticalLayout = QtWidgets.QVBoxLayout(about)
self.verticalLayout.setObjectName("verticalLayout")
self.version = QtWidgets.QLabel(parent=about)
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(True)
self.version.setFont(font)
self.version.setScaledContents(False)
self.version.setObjectName("version")
self.verticalLayout.addWidget(self.version)
self.description = QtWidgets.QTextEdit(parent=about)
self.description.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.description.setReadOnly(True)
self.description.setObjectName("description")
self.verticalLayout.addWidget(self.description)
self.retranslateUi(about)
QtCore.QMetaObject.connectSlotsByName(about)
def retranslateUi(self, about):
_translate = QtCore.QCoreApplication.translate
about.setWindowTitle(_translate("about", "Dialog"))
self.version.setText(_translate("about", "SemesterapparatsManagement"))

View File

@@ -1,22 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\app_status.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(300, 500)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))

View File

@@ -1,83 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\apparat_extend.ui'
#
# Created by: PySide6 UI code generator 6.7.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(388, 103)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth())
Dialog.setSizePolicy(sizePolicy)
Dialog.setMinimumSize(QtCore.QSize(388, 103))
Dialog.setMaximumSize(QtCore.QSize(388, 103))
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setGeometry(QtCore.QRect(290, 30, 81, 241))
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Vertical)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Abort
| QtWidgets.QDialogButtonBox.StandardButton.Save
)
self.buttonBox.setObjectName("buttonBox")
self.label = QtWidgets.QLabel(parent=Dialog)
self.label.setGeometry(QtCore.QRect(10, 0, 281, 31))
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setPointSize(10)
self.label.setFont(font)
self.label.setObjectName("label")
self.frame = QtWidgets.QFrame(parent=Dialog)
self.frame.setGeometry(QtCore.QRect(10, 30, 241, 41))
self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.frame.setObjectName("frame")
self.line = QtWidgets.QFrame(parent=self.frame)
self.line.setGeometry(QtCore.QRect(120, 0, 3, 61))
self.line.setFrameShape(QtWidgets.QFrame.Shape.VLine)
self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
self.line.setObjectName("line")
self.rad_sommer = QtWidgets.QRadioButton(parent=self.frame)
self.rad_sommer.setGeometry(QtCore.QRect(10, 10, 82, 21))
self.rad_sommer.setObjectName("rad_sommer")
self.rad_winter = QtWidgets.QRadioButton(parent=self.frame)
self.rad_winter.setGeometry(QtCore.QRect(140, 10, 82, 21))
self.rad_winter.setObjectName("rad_winter")
self.sem_year = QtWidgets.QLineEdit(parent=Dialog)
self.sem_year.setGeometry(QtCore.QRect(10, 70, 121, 20))
self.sem_year.setObjectName("sem_year")
self.dauerapp = QtWidgets.QCheckBox(parent=Dialog)
self.dauerapp.setGeometry(QtCore.QRect(150, 70, 111, 21))
self.dauerapp.setObjectName("dauerapp")
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.label.setText(
_translate("Dialog", "Bis wann soll der Apparat verlängert werden?")
)
self.rad_sommer.setText(_translate("Dialog", "Sommer"))
self.rad_winter.setText(_translate("Dialog", "Winter"))
self.sem_year.setPlaceholderText(_translate("Dialog", "2023"))
self.dauerapp.setText(_translate("Dialog", "Dauerapparat"))

View File

@@ -1,37 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\confirm_extend.ui'
#
# Created by: PySide6 UI code generator 6.7.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtWidgets
class Ui_extend_confirm(object):
def setupUi(self, extend_confirm):
extend_confirm.setObjectName("extend_confirm")
extend_confirm.resize(380, 97)
self.horizontalLayout = QtWidgets.QHBoxLayout(extend_confirm)
self.horizontalLayout.setObjectName("horizontalLayout")
self.textEdit = QtWidgets.QTextEdit(parent=extend_confirm)
self.textEdit.setObjectName("textEdit")
self.horizontalLayout.addWidget(self.textEdit)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=extend_confirm)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Vertical)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setObjectName("buttonBox")
self.horizontalLayout.addWidget(self.buttonBox)
self.retranslateUi(extend_confirm)
self.buttonBox.accepted.connect(extend_confirm.accept) # type: ignore
self.buttonBox.rejected.connect(extend_confirm.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(extend_confirm)
def retranslateUi(self, extend_confirm):
_translate = QtCore.QCoreApplication.translate
extend_confirm.setWindowTitle(_translate("extend_confirm", "Dialog"))

View File

@@ -1,127 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\edit_bookdata.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(448, 572)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setGeometry(QtCore.QRect(260, 530, 161, 32))
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setObjectName("buttonBox")
self.gridLayoutWidget = QtWidgets.QWidget(parent=Dialog)
self.gridLayoutWidget.setGeometry(QtCore.QRect(0, 0, 441, 531))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout.setSizeConstraint(
QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint
)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.label_10 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_10.setObjectName("label_10")
self.gridLayout.addWidget(self.label_10, 10, 1, 1, 1)
self.label = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 1, 1, 1)
self.label_9 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_9.setObjectName("label_9")
self.gridLayout.addWidget(self.label_9, 9, 1, 1, 1)
self.label_8 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 8, 1, 1, 1)
self.label_12 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_12.setObjectName("label_12")
self.gridLayout.addWidget(self.label_12, 6, 1, 1, 1)
self.line_edition = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_edition.setObjectName("line_edition")
self.gridLayout.addWidget(self.line_edition, 2, 2, 1, 1)
self.label_3 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 2, 1, 1, 1)
self.label_4 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 3, 1, 1, 1)
self.line_link = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_link.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.ArrowCursor))
self.line_link.setReadOnly(True)
self.line_link.setObjectName("line_link")
self.gridLayout.addWidget(self.line_link, 6, 2, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 4, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 7, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 5, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=self.gridLayoutWidget)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 1, 1, 1, 1)
spacerItem = QtWidgets.QSpacerItem(
5,
20,
QtWidgets.QSizePolicy.Policy.Fixed,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.gridLayout.addItem(spacerItem, 8, 0, 1, 1)
self.line_title = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_title.setObjectName("line_title")
self.gridLayout.addWidget(self.line_title, 0, 2, 1, 1)
self.line_signature = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_signature.setObjectName("line_signature")
self.gridLayout.addWidget(self.line_signature, 1, 2, 1, 1)
self.line_author = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_author.setObjectName("line_author")
self.gridLayout.addWidget(self.line_author, 3, 2, 1, 1)
self.line_lang = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_lang.setObjectName("line_lang")
self.gridLayout.addWidget(self.line_lang, 8, 2, 1, 1)
self.line_ppn = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_ppn.setObjectName("line_ppn")
self.gridLayout.addWidget(self.line_ppn, 5, 2, 1, 1)
self.line_isbn = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_isbn.setObjectName("line_isbn")
self.gridLayout.addWidget(self.line_isbn, 7, 2, 1, 1)
self.line_year = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_year.setObjectName("line_year")
self.gridLayout.addWidget(self.line_year, 9, 2, 1, 1)
self.line_pages = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_pages.setObjectName("line_pages")
self.gridLayout.addWidget(self.line_pages, 10, 2, 1, 1)
self.line_publisher = QtWidgets.QLineEdit(parent=self.gridLayoutWidget)
self.line_publisher.setObjectName("line_publisher")
self.gridLayout.addWidget(self.line_publisher, 4, 2, 1, 1)
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.label_10.setText(_translate("Dialog", "Seiten"))
self.label.setText(_translate("Dialog", "Titel"))
self.label_9.setText(_translate("Dialog", "Jahr"))
self.label_8.setText(_translate("Dialog", "Sprache"))
self.label_12.setText(_translate("Dialog", "Link"))
self.label_3.setText(_translate("Dialog", "Auflage"))
self.label_4.setText(_translate("Dialog", "Autor"))
self.label_5.setText(_translate("Dialog", "Herausgeber"))
self.label_7.setText(_translate("Dialog", "ISBN(s)"))
self.label_6.setText(_translate("Dialog", "PPN"))
self.label_2.setText(_translate("Dialog", "Signatur"))

View File

@@ -1,470 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_add_table_entry.ui'
#
# Created by: PySide6 UI code generator 6.7.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(529, 482)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.groupBox = QtWidgets.QGroupBox(parent=Dialog)
self.groupBox.setFlat(True)
self.groupBox.setCheckable(False)
self.groupBox.setObjectName("groupBox")
self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_4.setObjectName("gridLayout_4")
spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.gridLayout_4.addItem(spacerItem, 0, 3, 1, 1)
self.btn_mono = QtWidgets.QRadioButton(parent=self.groupBox)
self.btn_mono.setChecked(False)
self.btn_mono.setObjectName("btn_mono")
self.gridLayout_4.addWidget(self.btn_mono, 0, 0, 1, 1)
self.btn_zs = QtWidgets.QRadioButton(parent=self.groupBox)
self.btn_zs.setObjectName("btn_zs")
self.gridLayout_4.addWidget(self.btn_zs, 0, 2, 1, 1)
self.btn_hg = QtWidgets.QRadioButton(parent=self.groupBox)
self.btn_hg.setObjectName("btn_hg")
self.gridLayout_4.addWidget(self.btn_hg, 0, 1, 1, 1)
self.verticalLayout.addWidget(self.groupBox)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_2 = QtWidgets.QLabel(parent=Dialog)
self.label_2.setObjectName("label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.searchIdent = QtWidgets.QLineEdit(parent=Dialog)
self.searchIdent.setObjectName("searchIdent")
self.horizontalLayout_2.addWidget(self.searchIdent)
self.btn_search = QtWidgets.QPushButton(parent=Dialog)
self.btn_search.setObjectName("btn_search")
self.horizontalLayout_2.addWidget(self.btn_search)
spacerItem1 = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_2.addItem(spacerItem1)
self.make_quote = QtWidgets.QPushButton(parent=Dialog)
self.make_quote.setObjectName("make_quote")
self.horizontalLayout_2.addWidget(self.make_quote)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.stackedWidget = QtWidgets.QStackedWidget(parent=Dialog)
self.stackedWidget.setObjectName("stackedWidget")
self.mono = QtWidgets.QWidget()
self.mono.setObjectName("mono")
self.gridLayout_2 = QtWidgets.QGridLayout(self.mono)
self.gridLayout_2.setObjectName("gridLayout_2")
self.label = QtWidgets.QLabel(parent=self.mono)
self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
self.book_author = QtWidgets.QLineEdit(parent=self.mono)
self.book_author.setObjectName("book_author")
self.gridLayout_2.addWidget(self.book_author, 0, 1, 1, 1)
self.label_3 = QtWidgets.QLabel(parent=self.mono)
self.label_3.setObjectName("label_3")
self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1)
self.book_year = QtWidgets.QLineEdit(parent=self.mono)
self.book_year.setObjectName("book_year")
self.gridLayout_2.addWidget(self.book_year, 1, 1, 1, 1)
self.label_4 = QtWidgets.QLabel(parent=self.mono)
self.label_4.setObjectName("label_4")
self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
self.book_edition = QtWidgets.QLineEdit(parent=self.mono)
self.book_edition.setObjectName("book_edition")
self.gridLayout_2.addWidget(self.book_edition, 2, 1, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=self.mono)
self.label_5.setObjectName("label_5")
self.gridLayout_2.addWidget(self.label_5, 3, 0, 1, 1)
self.book_title = QtWidgets.QLineEdit(parent=self.mono)
self.book_title.setObjectName("book_title")
self.gridLayout_2.addWidget(self.book_title, 3, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=self.mono)
self.label_6.setObjectName("label_6")
self.gridLayout_2.addWidget(self.label_6, 4, 0, 1, 1)
self.book_place = QtWidgets.QLineEdit(parent=self.mono)
self.book_place.setObjectName("book_place")
self.gridLayout_2.addWidget(self.book_place, 4, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(parent=self.mono)
self.label_7.setObjectName("label_7")
self.gridLayout_2.addWidget(self.label_7, 5, 0, 1, 1)
self.book_publisher = QtWidgets.QLineEdit(parent=self.mono)
self.book_publisher.setObjectName("book_publisher")
self.gridLayout_2.addWidget(self.book_publisher, 5, 1, 1, 1)
self.label_8 = QtWidgets.QLabel(parent=self.mono)
self.label_8.setObjectName("label_8")
self.gridLayout_2.addWidget(self.label_8, 6, 0, 1, 1)
self.book_signature = QtWidgets.QLineEdit(parent=self.mono)
self.book_signature.setObjectName("book_signature")
self.gridLayout_2.addWidget(self.book_signature, 6, 1, 1, 1)
self.label_9 = QtWidgets.QLabel(parent=self.mono)
self.label_9.setObjectName("label_9")
self.gridLayout_2.addWidget(self.label_9, 7, 0, 1, 1)
self.book_pages = QtWidgets.QLineEdit(parent=self.mono)
self.book_pages.setObjectName("book_pages")
self.gridLayout_2.addWidget(self.book_pages, 7, 1, 1, 1)
self.page_warn_2 = QtWidgets.QToolButton(parent=self.mono)
self.page_warn_2.setText("")
self.page_warn_2.setAutoRaise(True)
self.page_warn_2.setObjectName("page_warn_2")
self.gridLayout_2.addWidget(self.page_warn_2, 7, 2, 1, 1)
self.label_29 = QtWidgets.QLabel(parent=self.mono)
self.label_29.setObjectName("label_29")
self.gridLayout_2.addWidget(self.label_29, 8, 0, 1, 1)
self.book_isbn = QtWidgets.QLineEdit(parent=self.mono)
self.book_isbn.setObjectName("book_isbn")
self.gridLayout_2.addWidget(self.book_isbn, 8, 1, 1, 1)
self.stackedWidget.addWidget(self.mono)
self.hg = QtWidgets.QWidget()
self.hg.setObjectName("hg")
self.gridLayout_3 = QtWidgets.QGridLayout(self.hg)
self.gridLayout_3.setObjectName("gridLayout_3")
self.hg_editor = QtWidgets.QLineEdit(parent=self.hg)
self.hg_editor.setObjectName("hg_editor")
self.gridLayout_3.addWidget(self.hg_editor, 4, 1, 1, 1)
self.label_26 = QtWidgets.QLabel(parent=self.hg)
self.label_26.setObjectName("label_26")
self.gridLayout_3.addWidget(self.label_26, 7, 0, 1, 1)
self.hg_edition = QtWidgets.QLineEdit(parent=self.hg)
self.hg_edition.setObjectName("hg_edition")
self.gridLayout_3.addWidget(self.hg_edition, 2, 1, 1, 1)
self.label_20 = QtWidgets.QLabel(parent=self.hg)
self.label_20.setObjectName("label_20")
self.gridLayout_3.addWidget(self.label_20, 1, 0, 1, 1)
self.label_24 = QtWidgets.QLabel(parent=self.hg)
self.label_24.setObjectName("label_24")
self.gridLayout_3.addWidget(self.label_24, 3, 0, 1, 1)
self.label_27 = QtWidgets.QLabel(parent=self.hg)
self.label_27.setObjectName("label_27")
self.gridLayout_3.addWidget(self.label_27, 8, 0, 1, 1)
self.label_28 = QtWidgets.QLabel(parent=self.hg)
self.label_28.setObjectName("label_28")
self.gridLayout_3.addWidget(self.label_28, 9, 0, 1, 1)
self.label_23 = QtWidgets.QLabel(parent=self.hg)
self.label_23.setObjectName("label_23")
self.gridLayout_3.addWidget(self.label_23, 5, 0, 1, 1)
self.label_21 = QtWidgets.QLabel(parent=self.hg)
self.label_21.setObjectName("label_21")
self.gridLayout_3.addWidget(self.label_21, 2, 0, 1, 1)
self.hg_pages = QtWidgets.QLineEdit(parent=self.hg)
self.hg_pages.setObjectName("hg_pages")
self.gridLayout_3.addWidget(self.hg_pages, 8, 1, 1, 1)
self.label_19 = QtWidgets.QLabel(parent=self.hg)
self.label_19.setObjectName("label_19")
self.gridLayout_3.addWidget(self.label_19, 0, 0, 1, 1)
self.hg_signature = QtWidgets.QLineEdit(parent=self.hg)
self.hg_signature.setObjectName("hg_signature")
self.gridLayout_3.addWidget(self.hg_signature, 9, 1, 1, 1)
self.label_30 = QtWidgets.QLabel(parent=self.hg)
self.label_30.setObjectName("label_30")
self.gridLayout_3.addWidget(self.label_30, 10, 0, 1, 1)
self.label_25 = QtWidgets.QLabel(parent=self.hg)
self.label_25.setObjectName("label_25")
self.gridLayout_3.addWidget(self.label_25, 6, 0, 1, 1)
self.hg_year = QtWidgets.QLineEdit(parent=self.hg)
self.hg_year.setObjectName("hg_year")
self.gridLayout_3.addWidget(self.hg_year, 1, 1, 1, 1)
self.label_22 = QtWidgets.QLabel(parent=self.hg)
self.label_22.setObjectName("label_22")
self.gridLayout_3.addWidget(self.label_22, 4, 0, 1, 1)
self.hg_title = QtWidgets.QLineEdit(parent=self.hg)
self.hg_title.setObjectName("hg_title")
self.gridLayout_3.addWidget(self.hg_title, 5, 1, 1, 1)
self.hg_chaptertitle = QtWidgets.QLineEdit(parent=self.hg)
self.hg_chaptertitle.setObjectName("hg_chaptertitle")
self.gridLayout_3.addWidget(self.hg_chaptertitle, 3, 1, 1, 1)
self.hg_author = QtWidgets.QLineEdit(parent=self.hg)
self.hg_author.setObjectName("hg_author")
self.gridLayout_3.addWidget(self.hg_author, 0, 1, 1, 1)
self.hg_isbn = QtWidgets.QLineEdit(parent=self.hg)
self.hg_isbn.setObjectName("hg_isbn")
self.gridLayout_3.addWidget(self.hg_isbn, 10, 1, 1, 1)
self.hg_publisher = QtWidgets.QLineEdit(parent=self.hg)
self.hg_publisher.setObjectName("hg_publisher")
self.gridLayout_3.addWidget(self.hg_publisher, 7, 1, 1, 1)
self.hg_place = QtWidgets.QLineEdit(parent=self.hg)
self.hg_place.setObjectName("hg_place")
self.gridLayout_3.addWidget(self.hg_place, 6, 1, 1, 1)
self.page_warn_3 = QtWidgets.QToolButton(parent=self.hg)
self.page_warn_3.setText("")
self.page_warn_3.setAutoRaise(True)
self.page_warn_3.setObjectName("page_warn_3")
self.gridLayout_3.addWidget(self.page_warn_3, 8, 2, 1, 1)
self.stackedWidget.addWidget(self.hg)
self.zs = QtWidgets.QWidget()
self.zs.setObjectName("zs")
self.gridLayout = QtWidgets.QGridLayout(self.zs)
self.gridLayout.setObjectName("gridLayout")
self.label_10 = QtWidgets.QLabel(parent=self.zs)
self.label_10.setObjectName("label_10")
self.gridLayout.addWidget(self.label_10, 0, 0, 1, 1)
self.zs_publisher = QtWidgets.QLineEdit(parent=self.zs)
self.zs_publisher.setObjectName("zs_publisher")
self.gridLayout.addWidget(self.zs_publisher, 6, 1, 1, 1)
self.zs_place = QtWidgets.QLineEdit(parent=self.zs)
self.zs_place.setObjectName("zs_place")
self.gridLayout.addWidget(self.zs_place, 5, 1, 1, 1)
self.label_14 = QtWidgets.QLabel(parent=self.zs)
self.label_14.setObjectName("label_14")
self.gridLayout.addWidget(self.label_14, 4, 0, 1, 1)
self.label_11 = QtWidgets.QLabel(parent=self.zs)
self.label_11.setObjectName("label_11")
self.gridLayout.addWidget(self.label_11, 1, 0, 1, 1)
self.zs_year = QtWidgets.QLineEdit(parent=self.zs)
self.zs_year.setObjectName("zs_year")
self.gridLayout.addWidget(self.zs_year, 1, 1, 1, 1)
self.label_17 = QtWidgets.QLabel(parent=self.zs)
self.label_17.setObjectName("label_17")
self.gridLayout.addWidget(self.label_17, 7, 0, 1, 1)
self.label_16 = QtWidgets.QLabel(parent=self.zs)
self.label_16.setObjectName("label_16")
self.gridLayout.addWidget(self.label_16, 6, 0, 1, 1)
self.zs_issue = QtWidgets.QLineEdit(parent=self.zs)
self.zs_issue.setObjectName("zs_issue")
self.gridLayout.addWidget(self.zs_issue, 2, 1, 1, 1)
self.zs_chapter_title = QtWidgets.QLineEdit(parent=self.zs)
self.zs_chapter_title.setObjectName("zs_chapter_title")
self.gridLayout.addWidget(self.zs_chapter_title, 3, 1, 1, 1)
self.zs_isbn = QtWidgets.QLineEdit(parent=self.zs)
self.zs_isbn.setObjectName("zs_isbn")
self.gridLayout.addWidget(self.zs_isbn, 9, 1, 1, 1)
self.label_12 = QtWidgets.QLabel(parent=self.zs)
self.label_12.setObjectName("label_12")
self.gridLayout.addWidget(self.label_12, 2, 0, 1, 1)
self.label_31 = QtWidgets.QLabel(parent=self.zs)
self.label_31.setObjectName("label_31")
self.gridLayout.addWidget(self.label_31, 9, 0, 1, 1)
self.label_15 = QtWidgets.QLabel(parent=self.zs)
self.label_15.setObjectName("label_15")
self.gridLayout.addWidget(self.label_15, 5, 0, 1, 1)
self.zs_signature = QtWidgets.QLineEdit(parent=self.zs)
self.zs_signature.setObjectName("zs_signature")
self.gridLayout.addWidget(self.zs_signature, 8, 1, 1, 1)
self.zs_pages = QtWidgets.QLineEdit(parent=self.zs)
self.zs_pages.setObjectName("zs_pages")
self.gridLayout.addWidget(self.zs_pages, 7, 1, 1, 1)
self.label_13 = QtWidgets.QLabel(parent=self.zs)
self.label_13.setObjectName("label_13")
self.gridLayout.addWidget(self.label_13, 3, 0, 1, 1)
self.label_18 = QtWidgets.QLabel(parent=self.zs)
self.label_18.setObjectName("label_18")
self.gridLayout.addWidget(self.label_18, 8, 0, 1, 1)
self.zs_author = QtWidgets.QLineEdit(parent=self.zs)
self.zs_author.setObjectName("zs_author")
self.gridLayout.addWidget(self.zs_author, 0, 1, 1, 1)
self.zs_title = QtWidgets.QLineEdit(parent=self.zs)
self.zs_title.setObjectName("zs_title")
self.gridLayout.addWidget(self.zs_title, 4, 1, 1, 1)
self.page_warn = QtWidgets.QToolButton(parent=self.zs)
self.page_warn.setText("")
self.page_warn.setAutoRaise(True)
self.page_warn.setObjectName("page_warn")
self.gridLayout.addWidget(self.page_warn, 7, 2, 1, 1)
self.stackedWidget.addWidget(self.zs)
self.page = QtWidgets.QWidget()
self.page.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.page.setObjectName("page")
self.gridLayout_5 = QtWidgets.QGridLayout(self.page)
self.gridLayout_5.setObjectName("gridLayout_5")
self.label_32 = QtWidgets.QLabel(parent=self.page)
self.label_32.setObjectName("label_32")
self.gridLayout_5.addWidget(self.label_32, 0, 0, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.gridLayout_5.addItem(spacerItem2, 7, 0, 1, 1)
self.file_desc_edit = QtWidgets.QTextEdit(parent=self.page)
self.file_desc_edit.setReadOnly(True)
self.file_desc_edit.setObjectName("file_desc_edit")
self.gridLayout_5.addWidget(self.file_desc_edit, 6, 0, 1, 1)
self.label_34 = QtWidgets.QLabel(parent=self.page)
self.label_34.setObjectName("label_34")
self.gridLayout_5.addWidget(self.label_34, 3, 0, 1, 1)
self.filename_edit = QtWidgets.QTextEdit(parent=self.page)
self.filename_edit.setReadOnly(True)
self.filename_edit.setObjectName("filename_edit")
self.gridLayout_5.addWidget(self.filename_edit, 1, 0, 1, 1)
self.label_33 = QtWidgets.QLabel(parent=self.page)
self.label_33.setObjectName("label_33")
self.gridLayout_5.addWidget(self.label_33, 5, 0, 1, 1)
self.ilias_filename = QtWidgets.QTextEdit(parent=self.page)
self.ilias_filename.setReadOnly(True)
self.ilias_filename.setObjectName("ilias_filename")
self.gridLayout_5.addWidget(self.ilias_filename, 4, 0, 1, 1)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
spacerItem3 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_2.addItem(spacerItem3)
self.copy_filename = QtWidgets.QToolButton(parent=self.page)
self.copy_filename.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.copy_filename.setAutoFillBackground(False)
self.copy_filename.setObjectName("copy_filename")
self.verticalLayout_2.addWidget(self.copy_filename)
self.filename_edit_label = QtWidgets.QLabel(parent=self.page)
self.filename_edit_label.setText("")
self.filename_edit_label.setObjectName("filename_edit_label")
self.verticalLayout_2.addWidget(self.filename_edit_label)
spacerItem4 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_2.addItem(spacerItem4)
self.gridLayout_5.addLayout(self.verticalLayout_2, 1, 1, 1, 1)
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
spacerItem5 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_3.addItem(spacerItem5)
self.copy_ilias_filename = QtWidgets.QToolButton(parent=self.page)
self.copy_ilias_filename.setObjectName("copy_ilias_filename")
self.verticalLayout_3.addWidget(self.copy_ilias_filename)
self.ilias_filename_label = QtWidgets.QLabel(parent=self.page)
self.ilias_filename_label.setText("")
self.ilias_filename_label.setObjectName("ilias_filename_label")
self.verticalLayout_3.addWidget(self.ilias_filename_label)
spacerItem6 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_3.addItem(spacerItem6)
self.gridLayout_5.addLayout(self.verticalLayout_3, 4, 1, 1, 1)
self.verticalLayout_4 = QtWidgets.QVBoxLayout()
self.verticalLayout_4.setObjectName("verticalLayout_4")
spacerItem7 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_4.addItem(spacerItem7)
self.copy_qoute = QtWidgets.QToolButton(parent=self.page)
self.copy_qoute.setObjectName("copy_qoute")
self.verticalLayout_4.addWidget(self.copy_qoute)
self.file_desc_edit_label = QtWidgets.QLabel(parent=self.page)
self.file_desc_edit_label.setText("")
self.file_desc_edit_label.setObjectName("file_desc_edit_label")
self.verticalLayout_4.addWidget(self.file_desc_edit_label)
spacerItem8 = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout_4.addItem(spacerItem8)
self.gridLayout_5.addLayout(self.verticalLayout_4, 6, 1, 1, 1)
self.stackedWidget.addWidget(self.page)
self.verticalLayout.addWidget(self.stackedWidget)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Discard
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setObjectName("buttonBox")
self.horizontalLayout.addWidget(self.buttonBox)
self.retryButton = QtWidgets.QPushButton(parent=Dialog)
self.retryButton.setObjectName("retryButton")
self.horizontalLayout.addWidget(self.retryButton)
self.verticalLayout.addLayout(self.horizontalLayout)
self.retranslateUi(Dialog)
self.stackedWidget.setCurrentIndex(3)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.groupBox.setTitle(_translate("Dialog", "Medientyp?"))
self.btn_mono.setText(_translate("Dialog", "Monografie"))
self.btn_zs.setText(_translate("Dialog", "Zeitschrift"))
self.btn_hg.setText(_translate("Dialog", "Herausgeberwerk"))
self.label_2.setText(_translate("Dialog", "Identifikator"))
self.btn_search.setText(_translate("Dialog", "Suchen"))
self.make_quote.setToolTip(
_translate("Dialog", "Zuerst die Seitenzahl anpassen")
)
self.make_quote.setText(_translate("Dialog", "Zitat erstellen"))
self.label.setText(_translate("Dialog", "Autor(en)\n Nachname, Vorname"))
self.book_author.setToolTip(
_translate("Dialog", "Bei mehreren Autoren mit ; trennen")
)
self.label_3.setText(_translate("Dialog", "Jahr"))
self.label_4.setText(_translate("Dialog", "Auflage"))
self.label_5.setText(_translate("Dialog", "Titel"))
self.label_6.setText(_translate("Dialog", "Ort"))
self.label_7.setText(_translate("Dialog", "Verlag"))
self.label_8.setText(_translate("Dialog", "Signatur"))
self.label_9.setText(_translate("Dialog", "Seiten"))
self.book_pages.setPlaceholderText(
_translate("Dialog", "Seitenanzahl des Mediums, zum zitieren ändern!")
)
self.label_29.setText(_translate("Dialog", "ISBN"))
self.hg_editor.setToolTip(
_translate("Dialog", "Bei mehreren Autoren mit ; trennen")
)
self.label_26.setText(_translate("Dialog", "Verlag"))
self.label_20.setText(_translate("Dialog", "Jahr"))
self.label_24.setText(_translate("Dialog", "Beitragstitel"))
self.label_27.setText(_translate("Dialog", "Seiten"))
self.label_28.setText(_translate("Dialog", "Signatur"))
self.label_23.setText(_translate("Dialog", "Titel des Werkes"))
self.label_21.setText(_translate("Dialog", "Auflage"))
self.label_19.setText(_translate("Dialog", "Autor(en)\nNachname, Vorname"))
self.label_30.setText(_translate("Dialog", "ISBN"))
self.label_25.setText(_translate("Dialog", "Ort"))
self.label_22.setText(
_translate("Dialog", "Herausgebername(n)\nNachname, Vorname")
)
self.hg_author.setToolTip(
_translate("Dialog", "Bei mehreren Autoren mit ; trennen")
)
self.label_10.setText(_translate("Dialog", "Autor(en)\nNachname, Vorname"))
self.label_14.setText(_translate("Dialog", "Name der Zeitschrift"))
self.label_11.setText(_translate("Dialog", "Jahr"))
self.label_17.setText(_translate("Dialog", "Seiten"))
self.label_16.setText(_translate("Dialog", "Verlag"))
self.label_12.setText(_translate("Dialog", "Heft"))
self.label_31.setText(_translate("Dialog", "ISSN"))
self.label_15.setText(_translate("Dialog", "Ort"))
self.label_13.setText(_translate("Dialog", "Artikeltitel"))
self.label_18.setText(_translate("Dialog", "Signatur"))
self.zs_author.setToolTip(
_translate("Dialog", "Bei mehreren Autoren mit ; trennen")
)
self.label_32.setText(_translate("Dialog", "Dateiname"))
self.label_34.setText(_translate("Dialog", "ILIAS Name"))
self.label_33.setText(_translate("Dialog", "ILIAS Dateibeschreibung"))
self.copy_filename.setText(_translate("Dialog", "Kopieren"))
self.copy_ilias_filename.setText(_translate("Dialog", "Kopieren"))
self.copy_qoute.setText(_translate("Dialog", "Kopieren"))
self.retryButton.setText(_translate("Dialog", "Wiederholen"))

View File

@@ -1,83 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_generate_citation.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(564, 517)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.select_type = QtWidgets.QFrame(parent=Dialog)
self.select_type.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.select_type.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.select_type.setObjectName("select_type")
self.verticalLayout = QtWidgets.QVBoxLayout(self.select_type)
self.verticalLayout.setObjectName("verticalLayout")
self.radio_mono = QtWidgets.QRadioButton(parent=self.select_type)
self.radio_mono.setObjectName("radio_mono")
self.verticalLayout.addWidget(self.radio_mono)
self.radio_zs = QtWidgets.QRadioButton(parent=self.select_type)
self.radio_zs.setObjectName("radio_zs")
self.verticalLayout.addWidget(self.radio_zs)
self.radio_hg = QtWidgets.QRadioButton(parent=self.select_type)
self.radio_hg.setObjectName("radio_hg")
self.verticalLayout.addWidget(self.radio_hg)
spacerItem = QtWidgets.QSpacerItem(
20,
40,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.verticalLayout.addItem(spacerItem)
self.verticalLayout_2.addWidget(self.select_type)
self.check = QtWidgets.QGroupBox(parent=Dialog)
font = QtGui.QFont()
font.setBold(True)
self.check.setFont(font)
self.check.setObjectName("check")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.check)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.citation_style_result = QtWidgets.QStackedWidget(parent=self.check)
font = QtGui.QFont()
font.setBold(False)
self.citation_style_result.setFont(font)
self.citation_style_result.setObjectName("citation_style_result")
self.monografie = QtWidgets.QWidget()
self.monografie.setObjectName("monografie")
self.citation_style_result.addWidget(self.monografie)
self.zsaufsatz = QtWidgets.QWidget()
self.zsaufsatz.setObjectName("zsaufsatz")
self.citation_style_result.addWidget(self.zsaufsatz)
self.herausgeberwerk = QtWidgets.QWidget()
self.herausgeberwerk.setObjectName("herausgeberwerk")
self.citation_style_result.addWidget(self.herausgeberwerk)
self.verticalLayout_3.addWidget(self.citation_style_result)
self.pushButton = QtWidgets.QPushButton(parent=self.check)
self.pushButton.setObjectName("pushButton")
self.verticalLayout_3.addWidget(
self.pushButton, 0, QtCore.Qt.AlignmentFlag.AlignRight
)
self.verticalLayout_2.addWidget(self.check)
self.verticalLayout_2.setStretch(0, 20)
self.verticalLayout_2.setStretch(1, 80)
self.retranslateUi(Dialog)
self.citation_style_result.setCurrentIndex(2)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.radio_mono.setText(_translate("Dialog", "Monografie"))
self.radio_zs.setText(_translate("Dialog", "Zeitschriftenaufsatz"))
self.radio_hg.setText(_translate("Dialog", "Herausgeberwerk"))
self.check.setTitle(_translate("Dialog", "Daten"))
self.pushButton.setText(_translate("Dialog", "Bestätigen"))

View File

@@ -1,129 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_generator_confirm.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(530, 210)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed
)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth())
Dialog.setSizePolicy(sizePolicy)
Dialog.setMaximumSize(QtCore.QSize(530, 210))
self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog)
self.horizontalLayout.setObjectName("horizontalLayout")
self.groupBox = QtWidgets.QGroupBox(parent=Dialog)
font = QtGui.QFont()
font.setBold(True)
self.groupBox.setFont(font)
self.groupBox.setObjectName("groupBox")
self.gridLayout = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.label.setFont(font)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
self.bookauthor = QtWidgets.QLineEdit(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.bookauthor.setFont(font)
self.bookauthor.setObjectName("bookauthor")
self.gridLayout.addWidget(self.bookauthor, 5, 1, 1, 1)
self.book_title = QtWidgets.QLineEdit(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.book_title.setFont(font)
self.book_title.setObjectName("book_title")
self.gridLayout.addWidget(self.book_title, 3, 1, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 5, 0, 1, 1)
self.pages = QtWidgets.QLineEdit(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.pages.setFont(font)
self.pages.setObjectName("pages")
self.gridLayout.addWidget(self.pages, 4, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
self.label_3 = QtWidgets.QLabel(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 3, 0, 1, 1)
self.label_4 = QtWidgets.QLabel(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.label_4.setFont(font)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 4, 0, 1, 1)
self.chapter_title = QtWidgets.QLineEdit(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.chapter_title.setFont(font)
self.chapter_title.setObjectName("chapter_title")
self.gridLayout.addWidget(self.chapter_title, 1, 1, 1, 1)
self.chapter_authors = QtWidgets.QLineEdit(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.chapter_authors.setFont(font)
self.chapter_authors.setObjectName("chapter_authors")
self.gridLayout.addWidget(self.chapter_authors, 2, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=self.groupBox)
font = QtGui.QFont()
font.setBold(False)
self.label_6.setFont(font)
self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 0, 1, 1, 1)
self.horizontalLayout.addWidget(self.groupBox)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Vertical)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setCenterButtons(False)
self.buttonBox.setObjectName("buttonBox")
self.horizontalLayout.addWidget(self.buttonBox)
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.groupBox.setTitle(_translate("Dialog", "Angaben korrekt?"))
self.label.setText(_translate("Dialog", "Kapiteltitel"))
self.label_5.setText(_translate("Dialog", "Herausgebername"))
self.label_2.setText(_translate("Dialog", "Autor(en)"))
self.label_3.setText(_translate("Dialog", "Buchtitel"))
self.label_4.setText(_translate("Dialog", "Seite(n)"))
self.label_6.setText(
_translate(
"Dialog", "Hier können fehlerhafte / fehlende Daten geändert werden"
)
)

View File

@@ -1,53 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\login.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(218, 190)
icon = QtGui.QIcon()
icon.addPixmap(
QtGui.QPixmap(":/icons/resources/1f510.svg"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
Dialog.setWindowIcon(icon)
self.label = QtWidgets.QLabel(parent=Dialog)
self.label.setGeometry(QtCore.QRect(20, 40, 71, 21))
self.label.setObjectName("label")
self.lineEdit = QtWidgets.QLineEdit(parent=Dialog)
self.lineEdit.setGeometry(QtCore.QRect(80, 40, 113, 21))
self.lineEdit.setObjectName("lineEdit")
self.label_2 = QtWidgets.QLabel(parent=Dialog)
self.label_2.setGeometry(QtCore.QRect(20, 80, 71, 21))
self.label_2.setObjectName("label_2")
self.lineEdit_2 = QtWidgets.QLineEdit(parent=Dialog)
self.lineEdit_2.setGeometry(QtCore.QRect(80, 80, 113, 21))
self.lineEdit_2.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhSensitiveData)
self.lineEdit_2.setClearButtonEnabled(True)
self.lineEdit_2.setObjectName("lineEdit_2")
self.login_button = QtWidgets.QPushButton(parent=Dialog)
self.login_button.setGeometry(QtCore.QRect(30, 140, 76, 32))
self.login_button.setObjectName("login_button")
self.cancel_button = QtWidgets.QPushButton(parent=Dialog)
self.cancel_button.setGeometry(QtCore.QRect(120, 140, 76, 32))
self.cancel_button.setObjectName("cancel_button")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Login"))
self.label.setText(_translate("Dialog", "Username"))
self.label_2.setText(_translate("Dialog", "Password"))
self.login_button.setText(_translate("Dialog", "Login"))
self.cancel_button.setText(_translate("Dialog", "Cancel"))

View File

@@ -1,137 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\mail_preview.ui'
#
# Created by: PySide6 UI code generator 6.7.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_eMailPreview(object):
def setupUi(self, eMailPreview):
eMailPreview.setObjectName("eMailPreview")
eMailPreview.resize(700, 668)
icon = QtGui.QIcon()
icon.addPixmap(
QtGui.QPixmap(
"c:\\Users\\aky547\\GitHub\\SemesterapparatsManager\\src\\ui\\dialogs\\dialog_sources\\../../../../../../icons/email.svg"
),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
eMailPreview.setWindowIcon(icon)
self.gridLayout_2 = QtWidgets.QGridLayout(eMailPreview)
self.gridLayout_2.setObjectName("gridLayout_2")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.prof_name = QtWidgets.QLineEdit(parent=eMailPreview)
self.prof_name.setObjectName("prof_name")
self.gridLayout.addWidget(self.prof_name, 2, 2, 1, 1)
self.newTemplate = QtWidgets.QPushButton(parent=eMailPreview)
self.newTemplate.setAutoFillBackground(False)
self.newTemplate.setText("")
self.newTemplate.setIconSize(QtCore.QSize(24, 24))
self.newTemplate.setAutoDefault(True)
self.newTemplate.setDefault(False)
self.newTemplate.setFlat(False)
self.newTemplate.setObjectName("newTemplate")
self.gridLayout.addWidget(self.newTemplate, 0, 3, 1, 1)
self.comboBox = QtWidgets.QComboBox(parent=eMailPreview)
self.comboBox.setObjectName("comboBox")
self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1)
self.mail_header = QtWidgets.QLineEdit(parent=eMailPreview)
self.mail_header.setObjectName("mail_header")
self.gridLayout.addWidget(self.mail_header, 3, 2, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=eMailPreview)
self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1)
self.mail_body = QtWidgets.QTextEdit(parent=eMailPreview)
self.mail_body.setObjectName("mail_body")
self.gridLayout.addWidget(self.mail_body, 5, 2, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=eMailPreview)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
self.mail_name = QtWidgets.QLineEdit(parent=eMailPreview)
self.mail_name.setObjectName("mail_name")
self.gridLayout.addWidget(self.mail_name, 1, 2, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=eMailPreview)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 0, 0, 1, 1)
self.label_4 = QtWidgets.QLabel(parent=eMailPreview)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.gender_male = QtWidgets.QRadioButton(parent=eMailPreview)
self.gender_male.setObjectName("gender_male")
self.horizontalLayout_3.addWidget(self.gender_male)
self.gender_female = QtWidgets.QRadioButton(parent=eMailPreview)
self.gender_female.setObjectName("gender_female")
self.horizontalLayout_3.addWidget(self.gender_female)
self.gender_non = QtWidgets.QRadioButton(parent=eMailPreview)
self.gender_non.setObjectName("gender_non")
self.horizontalLayout_3.addWidget(self.gender_non)
spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_3.addItem(spacerItem)
self.gridLayout.addLayout(self.horizontalLayout_3, 4, 2, 1, 1)
self.label_3 = QtWidgets.QLabel(parent=eMailPreview)
self.label_3.setAlignment(
QtCore.Qt.AlignmentFlag.AlignLeading
| QtCore.Qt.AlignmentFlag.AlignLeft
| QtCore.Qt.AlignmentFlag.AlignTop
)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 5, 0, 1, 1)
self.label = QtWidgets.QLabel(parent=eMailPreview)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem1 = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_2.addItem(spacerItem1)
self.btn_okay = QtWidgets.QPushButton(parent=eMailPreview)
self.btn_okay.setStatusTip("")
self.btn_okay.setObjectName("btn_okay")
self.horizontalLayout_2.addWidget(self.btn_okay)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=eMailPreview)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
)
self.buttonBox.setCenterButtons(True)
self.buttonBox.setObjectName("buttonBox")
self.horizontalLayout_2.addWidget(self.buttonBox)
self.gridLayout.addLayout(self.horizontalLayout_2, 6, 2, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
self.retranslateUi(eMailPreview)
self.buttonBox.accepted.connect(eMailPreview.accept) # type: ignore
self.buttonBox.rejected.connect(eMailPreview.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(eMailPreview)
def retranslateUi(self, eMailPreview):
_translate = QtCore.QCoreApplication.translate
eMailPreview.setWindowTitle(_translate("eMailPreview", "eMail Voransicht"))
self.label_6.setText(_translate("eMailPreview", "Anrede"))
self.label_2.setText(_translate("eMailPreview", "Prof"))
self.label_5.setText(_translate("eMailPreview", "Art"))
self.label_4.setText(_translate("eMailPreview", "Betreff"))
self.gender_male.setText(_translate("eMailPreview", "M"))
self.gender_female.setText(_translate("eMailPreview", "W"))
self.gender_non.setText(_translate("eMailPreview", "Divers"))
self.label_3.setText(_translate("eMailPreview", "Mail"))
self.label.setText(_translate("eMailPreview", "eMail"))
self.btn_okay.setWhatsThis(_translate("eMailPreview", "test"))
self.btn_okay.setText(_translate("eMailPreview", "Senden"))

View File

@@ -1,385 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\medianadder.ui'
#
# Created by: PySide6 UI code generator 6.7.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(620, 481)
icon = QtGui.QIcon()
icon.addPixmap(
QtGui.QPixmap(":/icons/resources/2795.svg"),
QtGui.QIcon.Mode.Normal,
QtGui.QIcon.State.Off,
)
Dialog.setWindowIcon(icon)
self.label = QtWidgets.QLabel(parent=Dialog)
self.label.setGeometry(QtCore.QRect(20, 10, 47, 21))
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(parent=Dialog)
self.label_2.setGeometry(QtCore.QRect(20, 40, 47, 21))
self.label_2.setObjectName("label_2")
self.comboBox = QtWidgets.QComboBox(parent=Dialog)
self.comboBox.setGeometry(QtCore.QRect(70, 40, 69, 22))
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.lineEdit = QtWidgets.QLineEdit(parent=Dialog)
self.lineEdit.setGeometry(QtCore.QRect(70, 10, 113, 20))
self.lineEdit.setObjectName("lineEdit")
self.label_3 = QtWidgets.QLabel(parent=Dialog)
self.label_3.setGeometry(QtCore.QRect(20, 90, 47, 21))
self.label_3.setObjectName("label_3")
self.widget = QtWidgets.QWidget(parent=Dialog)
self.widget.setGeometry(QtCore.QRect(330, 90, 281, 381))
self.widget.setObjectName("widget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.tableWidget = QtWidgets.QTableWidget(parent=self.widget)
self.tableWidget.setEnabled(True)
self.tableWidget.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.tableWidget.setAutoFillBackground(False)
self.tableWidget.setLineWidth(0)
self.tableWidget.setMidLineWidth(0)
self.tableWidget.setVerticalScrollBarPolicy(
QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
self.tableWidget.setHorizontalScrollBarPolicy(
QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
self.tableWidget.setSizeAdjustPolicy(
QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents
)
self.tableWidget.setEditTriggers(
QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers
)
self.tableWidget.setAlternatingRowColors(True)
self.tableWidget.setSelectionMode(
QtWidgets.QAbstractItemView.SelectionMode.NoSelection
)
self.tableWidget.setTextElideMode(QtCore.Qt.TextElideMode.ElideMiddle)
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(4)
self.tableWidget.setRowCount(11)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(4, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(5, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(6, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(7, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(8, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(9, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setVerticalHeaderItem(10, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(0, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(0, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(0, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(0, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(1, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(1, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(1, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(1, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(2, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(2, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(2, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(2, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(3, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(3, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(3, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(3, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(4, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(4, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(4, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(4, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(5, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(5, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(5, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(5, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(6, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(6, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(6, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(6, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(7, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(7, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(7, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(7, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(8, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(8, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(8, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(8, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(9, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(9, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(9, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(9, 3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(10, 0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(10, 1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(10, 2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget.setItem(10, 3, item)
self.tableWidget.horizontalHeader().setDefaultSectionSize(45)
self.horizontalLayout.addWidget(self.tableWidget)
self.listWidget = QtWidgets.QListWidget(parent=Dialog)
self.listWidget.setGeometry(QtCore.QRect(10, 110, 281, 321))
self.listWidget.setContextMenuPolicy(
QtCore.Qt.ContextMenuPolicy.CustomContextMenu
)
self.listWidget.setObjectName("listWidget")
self.label_4 = QtWidgets.QLabel(parent=Dialog)
self.label_4.setGeometry(QtCore.QRect(330, 50, 181, 21))
self.label_4.setObjectName("label_4")
self.label_5 = QtWidgets.QLabel(parent=Dialog)
self.label_5.setGeometry(QtCore.QRect(200, 90, 41, 21))
self.label_5.setObjectName("label_5")
self.list_amount = QtWidgets.QLabel(parent=Dialog)
self.list_amount.setGeometry(QtCore.QRect(240, 90, 47, 21))
self.list_amount.setObjectName("list_amount")
self.horizontalLayoutWidget = QtWidgets.QWidget(parent=Dialog)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 440, 160, 31))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.btn_save = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget)
self.btn_save.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.btn_save.setObjectName("btn_save")
self.horizontalLayout_2.addWidget(self.btn_save)
self.btn_cancel = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget)
self.btn_cancel.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.btn_cancel.setObjectName("btn_cancel")
self.horizontalLayout_2.addWidget(self.btn_cancel)
self.check_use_any_book = QtWidgets.QCheckBox(parent=Dialog)
self.check_use_any_book.setGeometry(QtCore.QRect(20, 70, 141, 20))
self.check_use_any_book.setObjectName("check_use_any_book")
self.check_use_exact_signature = QtWidgets.QCheckBox(parent=Dialog)
self.check_use_exact_signature.setGeometry(QtCore.QRect(165, 70, 121, 20))
self.check_use_exact_signature.setObjectName("check_use_exact_signature")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.lineEdit, self.comboBox)
Dialog.setTabOrder(self.comboBox, self.listWidget)
Dialog.setTabOrder(self.listWidget, self.tableWidget)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Medien"))
self.label.setText(_translate("Dialog", "Signatur"))
self.label_2.setText(_translate("Dialog", "Modus"))
self.comboBox.setItemText(0, _translate("Dialog", "ARRAY"))
self.comboBox.setItemText(1, _translate("Dialog", "BibTeX"))
self.comboBox.setItemText(2, _translate("Dialog", "COinS"))
self.comboBox.setItemText(3, _translate("Dialog", "RIS"))
self.lineEdit.setPlaceholderText(_translate("Dialog", "Signatur / ISBN"))
self.label_3.setText(_translate("Dialog", "Queue"))
item = self.tableWidget.verticalHeaderItem(0)
item.setText(_translate("Dialog", "PPN"))
item = self.tableWidget.verticalHeaderItem(1)
item.setText(_translate("Dialog", "Signatur"))
item = self.tableWidget.verticalHeaderItem(2)
item.setText(_translate("Dialog", "Autor"))
item = self.tableWidget.verticalHeaderItem(3)
item.setText(_translate("Dialog", "ISBN"))
item = self.tableWidget.verticalHeaderItem(4)
item.setText(_translate("Dialog", "Jahr"))
item = self.tableWidget.verticalHeaderItem(5)
item.setText(_translate("Dialog", "Auflage"))
item = self.tableWidget.verticalHeaderItem(6)
item.setText(_translate("Dialog", "Sprache"))
item = self.tableWidget.verticalHeaderItem(7)
item.setText(_translate("Dialog", "Herausgeber"))
item = self.tableWidget.verticalHeaderItem(8)
item.setText(_translate("Dialog", "Seiten"))
item = self.tableWidget.verticalHeaderItem(9)
item.setText(_translate("Dialog", "Titel"))
item = self.tableWidget.verticalHeaderItem(10)
item.setText(_translate("Dialog", "Link"))
item = self.tableWidget.horizontalHeaderItem(0)
item.setText(_translate("Dialog", "Array"))
item = self.tableWidget.horizontalHeaderItem(1)
item.setText(_translate("Dialog", "BibTeX"))
item = self.tableWidget.horizontalHeaderItem(2)
item.setText(_translate("Dialog", "COinS"))
item = self.tableWidget.horizontalHeaderItem(3)
item.setText(_translate("Dialog", "RIS"))
__sortingEnabled = self.tableWidget.isSortingEnabled()
self.tableWidget.setSortingEnabled(False)
item = self.tableWidget.item(0, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(0, 1)
item.setText(_translate("Dialog", "0"))
item = self.tableWidget.item(0, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(0, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(1, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(1, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(1, 2)
item.setText(_translate("Dialog", "0"))
item = self.tableWidget.item(1, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(2, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(2, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(2, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(2, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(3, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(3, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(3, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(3, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(4, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(4, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(4, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(4, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(5, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(5, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(5, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(5, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(6, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(6, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(6, 2)
item.setText(_translate("Dialog", "0"))
item = self.tableWidget.item(6, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(7, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(7, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(7, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(7, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(8, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(8, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(8, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(8, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(9, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(9, 1)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(9, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(9, 3)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(10, 0)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(10, 1)
item.setText(_translate("Dialog", "0"))
item = self.tableWidget.item(10, 2)
item.setText(_translate("Dialog", "1"))
item = self.tableWidget.item(10, 3)
item.setText(_translate("Dialog", "1"))
self.tableWidget.setSortingEnabled(__sortingEnabled)
self.label_4.setText(_translate("Dialog", "Belegbare Felder per Anbieter"))
self.label_5.setText(_translate("Dialog", "Anzahl:"))
self.list_amount.setText(_translate("Dialog", "0"))
self.btn_save.setText(_translate("Dialog", "Ok"))
self.btn_cancel.setText(_translate("Dialog", "Abbrechen"))
self.check_use_any_book.setToolTip(
_translate(
"Dialog",
"Verwendet ein zufälliges Buch des Datensatzes, nützlich wenn das Buch noch nicht im Apparat ist",
)
)
self.check_use_any_book.setText(_translate("Dialog", "Jedes Buch verwenden"))
self.check_use_exact_signature.setToolTip(
_translate(
"Dialog", "Verwendet die eingegebene Signatur für die Suche von Daten"
)
)
self.check_use_exact_signature.setText(_translate("Dialog", "Exakte Signatur"))

View File

@@ -1,181 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\newMailTemplateDesigner.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(689, 572)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.bold = QtWidgets.QPushButton(parent=Dialog)
self.bold.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.bold.setCheckable(True)
self.bold.setObjectName("bold")
self.horizontalLayout_2.addWidget(self.bold)
self.italic = QtWidgets.QPushButton(parent=Dialog)
self.italic.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.italic.setCheckable(True)
self.italic.setObjectName("italic")
self.horizontalLayout_2.addWidget(self.italic)
self.underlined = QtWidgets.QPushButton(parent=Dialog)
self.underlined.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.underlined.setCheckable(True)
self.underlined.setObjectName("underlined")
self.horizontalLayout_2.addWidget(self.underlined)
self.fontBox = QtWidgets.QFontComboBox(parent=Dialog)
self.fontBox.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.fontBox.setObjectName("fontBox")
self.horizontalLayout_2.addWidget(self.fontBox)
self.fontSize = QtWidgets.QComboBox(parent=Dialog)
self.fontSize.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.fontSize.setObjectName("fontSize")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.horizontalLayout_2.addWidget(self.fontSize)
spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_2.addItem(spacerItem)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.verticalLayout.addLayout(self.horizontalLayout_4)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(parent=Dialog)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.placeholder_list = QtWidgets.QComboBox(parent=Dialog)
self.placeholder_list.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.placeholder_list.setSizeAdjustPolicy(
QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToContents
)
self.placeholder_list.setObjectName("placeholder_list")
self.placeholder_list.addItem("")
self.placeholder_list.addItem("")
self.placeholder_list.addItem("")
self.placeholder_list.addItem("")
self.placeholder_list.addItem("")
self.placeholder_list.addItem("")
self.gridLayout.addWidget(self.placeholder_list, 1, 0, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=Dialog)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
self.lineEdit = QtWidgets.QLineEdit(parent=Dialog)
self.lineEdit.setEnabled(True)
self.lineEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.lineEdit.setFrame(False)
self.lineEdit.setReadOnly(True)
self.lineEdit.setObjectName("lineEdit")
self.gridLayout.addWidget(self.lineEdit, 1, 1, 1, 1)
self.insertPlaceholder = QtWidgets.QPushButton(parent=Dialog)
self.insertPlaceholder.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.insertPlaceholder.setObjectName("insertPlaceholder")
self.gridLayout.addWidget(self.insertPlaceholder, 1, 2, 1, 1)
self.verticalLayout.addLayout(self.gridLayout)
self.label_3 = QtWidgets.QLabel(parent=Dialog)
self.label_3.setObjectName("label_3")
self.verticalLayout.addWidget(self.label_3)
self.subject = QtWidgets.QLineEdit(parent=Dialog)
self.subject.setObjectName("subject")
self.verticalLayout.addWidget(self.subject)
self.templateEdit = QtWidgets.QTextEdit(parent=Dialog)
self.templateEdit.setObjectName("templateEdit")
self.verticalLayout.addWidget(self.templateEdit)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.testTemplate = QtWidgets.QPushButton(parent=Dialog)
self.testTemplate.setObjectName("testTemplate")
self.horizontalLayout_3.addWidget(self.testTemplate)
spacerItem1 = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout_3.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Discard
| QtWidgets.QDialogButtonBox.StandardButton.Save
)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout_2.addWidget(self.buttonBox)
self.retranslateUi(Dialog)
self.fontSize.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.subject, self.templateEdit)
Dialog.setTabOrder(self.templateEdit, self.testTemplate)
Dialog.setTabOrder(self.testTemplate, self.insertPlaceholder)
Dialog.setTabOrder(self.insertPlaceholder, self.lineEdit)
Dialog.setTabOrder(self.lineEdit, self.fontSize)
Dialog.setTabOrder(self.fontSize, self.placeholder_list)
Dialog.setTabOrder(self.placeholder_list, self.fontBox)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.bold.setText(_translate("Dialog", "Fett"))
self.italic.setText(_translate("Dialog", "Kursiv"))
self.underlined.setText(_translate("Dialog", "Unterstrichen"))
self.fontSize.setItemText(0, _translate("Dialog", "8"))
self.fontSize.setItemText(1, _translate("Dialog", "9"))
self.fontSize.setItemText(2, _translate("Dialog", "11"))
self.fontSize.setItemText(3, _translate("Dialog", "12"))
self.fontSize.setItemText(4, _translate("Dialog", "14"))
self.fontSize.setItemText(5, _translate("Dialog", "16"))
self.fontSize.setItemText(6, _translate("Dialog", "18"))
self.fontSize.setItemText(7, _translate("Dialog", "20"))
self.fontSize.setItemText(8, _translate("Dialog", "22"))
self.fontSize.setItemText(9, _translate("Dialog", "24"))
self.fontSize.setItemText(10, _translate("Dialog", "26"))
self.fontSize.setItemText(11, _translate("Dialog", "28"))
self.fontSize.setItemText(12, _translate("Dialog", "36"))
self.fontSize.setItemText(13, _translate("Dialog", "48"))
self.fontSize.setItemText(14, _translate("Dialog", "76"))
self.label.setText(_translate("Dialog", "Platzhalter"))
self.placeholder_list.setItemText(0, _translate("Dialog", "«Anrede»"))
self.placeholder_list.setItemText(1, _translate("Dialog", "«ApparatsName»"))
self.placeholder_list.setItemText(2, _translate("Dialog", "«ApparatsFach»"))
self.placeholder_list.setItemText(3, _translate("Dialog", "«ApparatsNummer»"))
self.placeholder_list.setItemText(4, _translate("Dialog", "«DozentName»"))
self.placeholder_list.setItemText(5, _translate("Dialog", "«Signatur»"))
self.label_2.setText(_translate("Dialog", "Beschreibung"))
self.insertPlaceholder.setText(
_translate("Dialog", "An aktiver Position einfügen")
)
self.label_3.setText(_translate("Dialog", "Betreff"))
self.testTemplate.setText(_translate("Dialog", "Template testen"))

View File

@@ -1,91 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\parsed_titles.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(402, 316)
self.frame = QtWidgets.QFrame(parent=Form)
self.frame.setGeometry(QtCore.QRect(10, 10, 381, 41))
self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.frame.setObjectName("frame")
self.horizontalLayoutWidget = QtWidgets.QWidget(parent=self.frame)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 381, 41))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.count = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.count.setFont(font)
self.count.setTextFormat(QtCore.Qt.TextFormat.PlainText)
self.count.setObjectName("count")
self.horizontalLayout.addWidget(self.count)
self.label_2 = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
self.horizontalLayout.addItem(spacerItem)
self.frame_2 = QtWidgets.QFrame(parent=Form)
self.frame_2.setGeometry(QtCore.QRect(10, 80, 381, 201))
self.frame_2.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.frame_2.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.frame_2.setObjectName("frame_2")
self.horizontalLayoutWidget_2 = QtWidgets.QWidget(parent=self.frame_2)
self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(0, 10, 381, 191))
self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.listWidget = QtWidgets.QListWidget(parent=self.horizontalLayoutWidget_2)
self.listWidget.setObjectName("listWidget")
self.horizontalLayout_2.addWidget(self.listWidget)
self.listWidget_done = QtWidgets.QListWidget(
parent=self.horizontalLayoutWidget_2
)
self.listWidget_done.setObjectName("listWidget_done")
self.horizontalLayout_2.addWidget(self.listWidget_done)
self.progressBar = QtWidgets.QProgressBar(parent=Form)
self.progressBar.setGeometry(QtCore.QRect(10, 60, 381, 23))
self.progressBar.setProperty("value", 24)
self.progressBar.setObjectName("progressBar")
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Form)
self.buttonBox.setGeometry(QtCore.QRect(230, 290, 156, 23))
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setObjectName("buttonBox")
self.toolButton = QtWidgets.QToolButton(parent=Form)
self.toolButton.setGeometry(QtCore.QRect(20, 290, 25, 19))
self.toolButton.setObjectName("toolButton")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label.setText(_translate("Form", "Es wurden"))
self.count.setText(_translate("Form", "0"))
self.label_2.setText(_translate("Form", "Signaturen gefunden."))
self.toolButton.setText(_translate("Form", "..."))

View File

@@ -1,46 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\reminder.ui'
#
# Created by: PySide6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtWidgets
class Ui_Erinnerung(object):
def setupUi(self, Erinnerung):
Erinnerung.setObjectName("Erinnerung")
Erinnerung.resize(358, 308)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Erinnerung)
self.buttonBox.setGeometry(QtCore.QRect(190, 270, 161, 32))
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setObjectName("buttonBox")
self.message_box = QtWidgets.QTextEdit(parent=Erinnerung)
self.message_box.setGeometry(QtCore.QRect(10, 60, 341, 201))
self.message_box.setObjectName("message_box")
self.label = QtWidgets.QLabel(parent=Erinnerung)
self.label.setGeometry(QtCore.QRect(10, 30, 61, 21))
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(parent=Erinnerung)
self.label_2.setGeometry(QtCore.QRect(120, 30, 81, 21))
self.label_2.setObjectName("label_2")
self.dateEdit = QtWidgets.QDateEdit(parent=Erinnerung)
self.dateEdit.setGeometry(QtCore.QRect(210, 30, 141, 22))
self.dateEdit.setObjectName("dateEdit")
self.retranslateUi(Erinnerung)
self.buttonBox.accepted.connect(Erinnerung.accept) # type: ignore
self.buttonBox.rejected.connect(Erinnerung.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Erinnerung)
def retranslateUi(self, Erinnerung):
_translate = QtCore.QCoreApplication.translate
Erinnerung.setWindowTitle(_translate("Erinnerung", "Dialog"))
self.label.setText(_translate("Erinnerung", "Nachricht:"))
self.label_2.setText(_translate("Erinnerung", "Erinnerung am:"))

View File

@@ -1,341 +0,0 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\settings.ui'
#
# Created by: PySide6 UI code generator 6.7.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PySide6 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.setWindowModality(QtCore.Qt.WindowModality.NonModal)
Dialog.resize(651, 679)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth())
Dialog.setSizePolicy(sizePolicy)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.toolBox = QtWidgets.QToolBox(parent=Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.toolBox.sizePolicy().hasHeightForWidth())
self.toolBox.setSizePolicy(sizePolicy)
self.toolBox.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhNone)
self.toolBox.setObjectName("toolBox")
self.page_1 = QtWidgets.QWidget()
self.page_1.setGeometry(QtCore.QRect(0, 0, 633, 511))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.page_1.sizePolicy().hasHeightForWidth())
self.page_1.setSizePolicy(sizePolicy)
self.page_1.setObjectName("page_1")
self.gridLayout_3 = QtWidgets.QGridLayout(self.page_1)
self.gridLayout_3.setObjectName("gridLayout_3")
self.db_name = QtWidgets.QLineEdit(parent=self.page_1)
self.db_name.setObjectName("db_name")
self.gridLayout_3.addWidget(self.db_name, 0, 1, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=self.page_1)
self.label_5.setObjectName("label_5")
self.gridLayout_3.addWidget(self.label_5, 0, 0, 1, 1)
self.db_path = QtWidgets.QLineEdit(parent=self.page_1)
self.db_path.setEnabled(False)
self.db_path.setObjectName("db_path")
self.gridLayout_3.addWidget(self.db_path, 1, 1, 1, 1)
self.label_12 = QtWidgets.QLabel(parent=self.page_1)
self.label_12.setObjectName("label_12")
self.gridLayout_3.addWidget(self.label_12, 2, 0, 1, 1)
self.label_11 = QtWidgets.QLabel(parent=self.page_1)
self.label_11.setObjectName("label_11")
self.gridLayout_3.addWidget(self.label_11, 1, 0, 1, 1)
self.tb_set_save_path = QtWidgets.QToolButton(parent=self.page_1)
self.tb_set_save_path.setObjectName("tb_set_save_path")
self.gridLayout_3.addWidget(self.tb_set_save_path, 2, 2, 1, 1)
self.tb_select_db = QtWidgets.QToolButton(parent=self.page_1)
self.tb_select_db.setObjectName("tb_select_db")
self.gridLayout_3.addWidget(self.tb_select_db, 0, 2, 1, 1)
self.save_path = QtWidgets.QLineEdit(parent=self.page_1)
self.save_path.setObjectName("save_path")
self.gridLayout_3.addWidget(self.save_path, 2, 1, 1, 1)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.gridLayout_3.addItem(spacerItem, 3, 1, 1, 1)
self.toolBox.addItem(self.page_1, "")
self.page_2 = QtWidgets.QWidget()
self.page_2.setGeometry(QtCore.QRect(0, 0, 633, 511))
self.page_2.setObjectName("page_2")
self.gridLayout = QtWidgets.QGridLayout(self.page_2)
self.gridLayout.setObjectName("gridLayout")
self.zotero_library_type = QtWidgets.QLineEdit(parent=self.page_2)
self.zotero_library_type.setObjectName("zotero_library_type")
self.gridLayout.addWidget(self.zotero_library_type, 2, 2, 1, 1)
self.zotero_library_id = QtWidgets.QLineEdit(parent=self.page_2)
self.zotero_library_id.setObjectName("zotero_library_id")
self.gridLayout.addWidget(self.zotero_library_id, 1, 2, 1, 1)
self.label_4 = QtWidgets.QLabel(parent=self.page_2)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 2, 0, 1, 1)
self.label_3 = QtWidgets.QLabel(parent=self.page_2)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1)
self.zotero_api_key = QtWidgets.QLineEdit(parent=self.page_2)
self.zotero_api_key.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhHiddenText|QtCore.Qt.InputMethodHint.ImhSensitiveData)
self.zotero_api_key.setObjectName("zotero_api_key")
self.gridLayout.addWidget(self.zotero_api_key, 0, 2, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=self.page_2)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
self.toggle_api_visibility = QtWidgets.QToolButton(parent=self.page_2)
self.toggle_api_visibility.setText("")
self.toggle_api_visibility.setObjectName("toggle_api_visibility")
self.gridLayout.addWidget(self.toggle_api_visibility, 0, 3, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.gridLayout.addItem(spacerItem1, 3, 2, 1, 1)
self.toolBox.addItem(self.page_2, "")
self.page_3 = QtWidgets.QWidget()
self.page_3.setGeometry(QtCore.QRect(0, 0, 633, 511))
self.page_3.setObjectName("page_3")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.page_3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.email_settings = QtWidgets.QTabWidget(parent=self.page_3)
self.email_settings.setObjectName("email_settings")
self.email_settingsPage1_2 = QtWidgets.QWidget()
self.email_settingsPage1_2.setObjectName("email_settingsPage1_2")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.email_settingsPage1_2)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.gridLayout_2 = QtWidgets.QGridLayout()
self.gridLayout_2.setObjectName("gridLayout_2")
self.use_username_smtp_login = QtWidgets.QCheckBox(parent=self.email_settingsPage1_2)
self.use_username_smtp_login.setTristate(False)
self.use_username_smtp_login.setObjectName("use_username_smtp_login")
self.gridLayout_2.addWidget(self.use_username_smtp_login, 4, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=self.email_settingsPage1_2)
self.label_6.setObjectName("label_6")
self.gridLayout_2.addWidget(self.label_6, 1, 0, 1, 1)
self.smtp_port = QtWidgets.QLineEdit(parent=self.email_settingsPage1_2)
self.smtp_port.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhDigitsOnly|QtCore.Qt.InputMethodHint.ImhPreferNumbers)
self.smtp_port.setClearButtonEnabled(True)
self.smtp_port.setObjectName("smtp_port")
self.gridLayout_2.addWidget(self.smtp_port, 1, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(parent=self.email_settingsPage1_2)
self.label_7.setObjectName("label_7")
self.gridLayout_2.addWidget(self.label_7, 2, 0, 1, 1)
self.sender_email = QtWidgets.QLineEdit(parent=self.email_settingsPage1_2)
self.sender_email.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhEmailCharactersOnly)
self.sender_email.setClearButtonEnabled(True)
self.sender_email.setObjectName("sender_email")
self.gridLayout_2.addWidget(self.sender_email, 2, 1, 1, 1)
self.mail_username = QtWidgets.QLineEdit(parent=self.email_settingsPage1_2)
self.mail_username.setClearButtonEnabled(True)
self.mail_username.setObjectName("mail_username")
self.gridLayout_2.addWidget(self.mail_username, 3, 1, 1, 1)
self.label_9 = QtWidgets.QLabel(parent=self.email_settingsPage1_2)
self.label_9.setText("")
self.label_9.setObjectName("label_9")
self.gridLayout_2.addWidget(self.label_9, 7, 0, 1, 1)
self.password = QtWidgets.QLineEdit(parent=self.email_settingsPage1_2)
self.password.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhHiddenText|QtCore.Qt.InputMethodHint.ImhSensitiveData)
self.password.setClearButtonEnabled(True)
self.password.setObjectName("password")
self.gridLayout_2.addWidget(self.password, 5, 1, 1, 1)
self.smtp_address = QtWidgets.QLineEdit(parent=self.email_settingsPage1_2)
self.smtp_address.setClearButtonEnabled(True)
self.smtp_address.setObjectName("smtp_address")
self.gridLayout_2.addWidget(self.smtp_address, 0, 1, 1, 1)
self.label = QtWidgets.QLabel(parent=self.email_settingsPage1_2)
self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
self.label_10 = QtWidgets.QLabel(parent=self.email_settingsPage1_2)
self.label_10.setObjectName("label_10")
self.gridLayout_2.addWidget(self.label_10, 5, 0, 1, 1)
self.togglePassword = QtWidgets.QPushButton(parent=self.email_settingsPage1_2)
self.togglePassword.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.togglePassword.setText("")
self.togglePassword.setObjectName("togglePassword")
self.gridLayout_2.addWidget(self.togglePassword, 5, 2, 1, 1)
self.label_8 = QtWidgets.QLabel(parent=self.email_settingsPage1_2)
self.label_8.setObjectName("label_8")
self.gridLayout_2.addWidget(self.label_8, 3, 0, 1, 1)
self.label_13 = QtWidgets.QLabel(parent=self.email_settingsPage1_2)
self.label_13.setObjectName("label_13")
self.gridLayout_2.addWidget(self.label_13, 6, 0, 1, 1)
self.printermail = QtWidgets.QLineEdit(parent=self.email_settingsPage1_2)
self.printermail.setObjectName("printermail")
self.gridLayout_2.addWidget(self.printermail, 6, 1, 1, 1)
self.horizontalLayout_4.addLayout(self.gridLayout_2)
self.email_settings.addTab(self.email_settingsPage1_2, "")
self.email_settingsPage2_2 = QtWidgets.QWidget()
self.email_settingsPage2_2.setObjectName("email_settingsPage2_2")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.email_settingsPage2_2)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(spacerItem2)
self.bold = QtWidgets.QPushButton(parent=self.email_settingsPage2_2)
self.bold.setCheckable(True)
self.bold.setObjectName("bold")
self.horizontalLayout_3.addWidget(self.bold)
self.italic = QtWidgets.QPushButton(parent=self.email_settingsPage2_2)
self.italic.setCheckable(True)
self.italic.setObjectName("italic")
self.horizontalLayout_3.addWidget(self.italic)
self.underscore = QtWidgets.QPushButton(parent=self.email_settingsPage2_2)
self.underscore.setCheckable(True)
self.underscore.setObjectName("underscore")
self.horizontalLayout_3.addWidget(self.underscore)
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(spacerItem3)
self.verticalLayout_2.addLayout(self.horizontalLayout_3)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.fontComboBox = QtWidgets.QFontComboBox(parent=self.email_settingsPage2_2)
self.fontComboBox.setObjectName("fontComboBox")
self.horizontalLayout.addWidget(self.fontComboBox)
self.font_size = QtWidgets.QComboBox(parent=self.email_settingsPage2_2)
self.font_size.setObjectName("font_size")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.font_size.addItem("")
self.horizontalLayout.addWidget(self.font_size)
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem4)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.verticalLayout_3.addLayout(self.verticalLayout_2)
self.editSignature = QtWidgets.QTextEdit(parent=self.email_settingsPage2_2)
self.editSignature.setObjectName("editSignature")
self.verticalLayout_3.addWidget(self.editSignature)
self.debug = QtWidgets.QPushButton(parent=self.email_settingsPage2_2)
self.debug.setObjectName("debug")
self.verticalLayout_3.addWidget(self.debug)
self.email_settings.addTab(self.email_settingsPage2_2, "")
self.horizontalLayout_2.addWidget(self.email_settings)
self.toolBox.addItem(self.page_3, "")
self.page_4 = QtWidgets.QWidget()
self.page_4.setGeometry(QtCore.QRect(0, 0, 633, 511))
self.page_4.setObjectName("page_4")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.page_4)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.groupBox = QtWidgets.QGroupBox(parent=self.page_4)
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(True)
self.groupBox.setFont(font)
self.groupBox.setObjectName("groupBox")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.scrollArea_3 = QtWidgets.QScrollArea(parent=self.groupBox)
self.scrollArea_3.setWidgetResizable(True)
self.scrollArea_3.setObjectName("scrollArea_3")
self.scrollAreaWidgetContents_3 = QtWidgets.QWidget()
self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 593, 201))
self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3")
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_3)
self.verticalLayout_7.setObjectName("verticalLayout_7")
self.gridLayout_4 = QtWidgets.QGridLayout()
self.gridLayout_4.setObjectName("gridLayout_4")
self.verticalLayout_7.addLayout(self.gridLayout_4)
self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3)
self.verticalLayout_5.addWidget(self.scrollArea_3)
self.verticalLayout_4.addWidget(self.groupBox)
self.scrollArea_2 = QtWidgets.QScrollArea(parent=self.page_4)
self.scrollArea_2.setWidgetResizable(True)
self.scrollArea_2.setObjectName("scrollArea_2")
self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 613, 241))
self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_2)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.vertical_icons = QtWidgets.QVBoxLayout()
self.vertical_icons.setObjectName("vertical_icons")
self.verticalLayout_6.addLayout(self.vertical_icons)
self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2)
self.verticalLayout_4.addWidget(self.scrollArea_2)
self.toolBox.addItem(self.page_4, "")
self.verticalLayout.addWidget(self.toolBox)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.label_5.setBuddy(self.db_name)
self.label_12.setBuddy(self.save_path)
self.label_11.setBuddy(self.db_path)
self.retranslateUi(Dialog)
self.toolBox.setCurrentIndex(2)
self.email_settings.setCurrentIndex(0)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.db_name.setText(_translate("Dialog", "sap.db"))
self.label_5.setToolTip(_translate("Dialog", "<html><head/><body><p>Name der Datenbank, welche verwendet werden soll. <span style=\" font-weight:600;\">Muss</span> auf .db enden</p></body></html>"))
self.label_5.setText(_translate("Dialog", "Datenbankname"))
self.label_12.setToolTip(_translate("Dialog", "Pfad, an dem heruntergeladene Dateien gespeichert werden sollen"))
self.label_12.setText(_translate("Dialog", "Temporäre Dateien"))
self.label_11.setText(_translate("Dialog", "Datenbankpfad"))
self.tb_set_save_path.setText(_translate("Dialog", "..."))
self.tb_select_db.setText(_translate("Dialog", "..."))
self.toolBox.setItemText(self.toolBox.indexOf(self.page_1), _translate("Dialog", "Datenbank"))
self.label_4.setText(_translate("Dialog", "Bibliothekstyp"))
self.label_3.setText(_translate("Dialog", "Bibliotheks-ID"))
self.label_2.setText(_translate("Dialog", "API Key"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("Dialog", "Zotero"))
self.use_username_smtp_login.setStatusTip(_translate("Dialog", "Anklicken, wenn Nutzername benötigt wird, um sich beim Server anzumelden"))
self.use_username_smtp_login.setText(_translate("Dialog", "Nutzername zum\n"
" Anmelden verwenden"))
self.label_6.setText(_translate("Dialog", "Port"))
self.label_7.setText(_translate("Dialog", "Sender-eMail"))
self.mail_username.setStatusTip(_translate("Dialog", "Kürzel, von der Hochschule vergeben, bsp: Aky547"))
self.label.setText(_translate("Dialog", "SMTP-Server"))
self.label_10.setText(_translate("Dialog", "Passwort"))
self.label_8.setText(_translate("Dialog", "Nutzername"))
self.label_13.setText(_translate("Dialog", "Printmail"))
self.email_settings.setTabText(self.email_settings.indexOf(self.email_settingsPage1_2), _translate("Dialog", "Allgemeines"))
self.bold.setText(_translate("Dialog", "Fett"))
self.italic.setText(_translate("Dialog", "Kursiv"))
self.underscore.setText(_translate("Dialog", "Unterstrichen"))
self.font_size.setItemText(0, _translate("Dialog", "8"))
self.font_size.setItemText(1, _translate("Dialog", "9"))
self.font_size.setItemText(2, _translate("Dialog", "11"))
self.font_size.setItemText(3, _translate("Dialog", "12"))
self.font_size.setItemText(4, _translate("Dialog", "14"))
self.font_size.setItemText(5, _translate("Dialog", "16"))
self.font_size.setItemText(6, _translate("Dialog", "18"))
self.font_size.setItemText(7, _translate("Dialog", "20"))
self.font_size.setItemText(8, _translate("Dialog", "22"))
self.font_size.setItemText(9, _translate("Dialog", "24"))
self.font_size.setItemText(10, _translate("Dialog", "26"))
self.font_size.setItemText(11, _translate("Dialog", "28"))
self.font_size.setItemText(12, _translate("Dialog", "36"))
self.font_size.setItemText(13, _translate("Dialog", "48"))
self.font_size.setItemText(14, _translate("Dialog", "72"))
self.debug.setText(_translate("Dialog", "Debug"))
self.email_settings.setTabText(self.email_settings.indexOf(self.email_settingsPage2_2), _translate("Dialog", "Signatur"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page_3), _translate("Dialog", "e-Mail"))
self.groupBox.setTitle(_translate("Dialog", "Farben"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page_4), _translate("Dialog", "Icons"))

View File

@@ -1,2 +1 @@
from .Ui_mail_preview import Ui_eMailPreview as MailPreviewDialog from .newMailTemplateDesigner_ui import Ui_Dialog as NewMailTemplateDesignerDialog
from .Ui_newMailTemplateDesigner import Ui_Dialog as NewMailTemplateDesignerDialog

View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1001</width>
<height>649</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Medium suchen</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>Titel/Signatursuche</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string/>
</property>
</column>
<column>
<property name="text">
<string>Apparat</string>
</property>
</column>
<column>
<property name="text">
<string>Signatur</string>
</property>
</column>
<column>
<property name="text">
<string>Titel</string>
</property>
</column>
<column>
<property name="text">
<string>Auflage</string>
</property>
</column>
<column>
<property name="text">
<string>ISBN</string>
</property>
</column>
<column>
<property name="text">
<string>ID</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="reset_btn">
<property name="text">
<string>Zurücksetzen</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="delete_btn">
<property name="text">
<string>Löschen</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel_btn">
<property name="text">
<string>Abbrechen</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'deletedialog.ui'
##
## Created by: Qt User Interface Compiler version 6.9.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QHeaderView,
QLabel, QLineEdit, QPushButton, QSizePolicy,
QSpacerItem, QTableWidget, QTableWidgetItem, QVBoxLayout,
QWidget)
class Ui_Dialog(object):
def setupUi(self, Dialog):
if not Dialog.objectName():
Dialog.setObjectName(u"Dialog")
Dialog.resize(1001, 649)
self.verticalLayout = QVBoxLayout(Dialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.label = QLabel(Dialog)
self.label.setObjectName(u"label")
self.horizontalLayout.addWidget(self.label)
self.lineEdit = QLineEdit(Dialog)
self.lineEdit.setObjectName(u"lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
self.verticalLayout.addLayout(self.horizontalLayout)
self.tableWidget = QTableWidget(Dialog)
if (self.tableWidget.columnCount() < 7):
self.tableWidget.setColumnCount(7)
__qtablewidgetitem = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
__qtablewidgetitem1 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1)
__qtablewidgetitem2 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(2, __qtablewidgetitem2)
__qtablewidgetitem3 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3)
__qtablewidgetitem4 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(4, __qtablewidgetitem4)
__qtablewidgetitem5 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(5, __qtablewidgetitem5)
__qtablewidgetitem6 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(6, __qtablewidgetitem6)
self.tableWidget.setObjectName(u"tableWidget")
self.tableWidget.setAlternatingRowColors(True)
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.verticalLayout.addWidget(self.tableWidget)
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalSpacer_2 = QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_2.addItem(self.horizontalSpacer_2)
self.reset_btn = QPushButton(Dialog)
self.reset_btn.setObjectName(u"reset_btn")
self.horizontalLayout_2.addWidget(self.reset_btn)
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_2.addItem(self.horizontalSpacer)
self.delete_btn = QPushButton(Dialog)
self.delete_btn.setObjectName(u"delete_btn")
self.horizontalLayout_2.addWidget(self.delete_btn)
self.cancel_btn = QPushButton(Dialog)
self.cancel_btn.setObjectName(u"cancel_btn")
self.horizontalLayout_2.addWidget(self.cancel_btn)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.retranslateUi(Dialog)
QMetaObject.connectSlotsByName(Dialog)
# setupUi
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
self.label.setText(QCoreApplication.translate("Dialog", u"Medium suchen", None))
self.lineEdit.setPlaceholderText(QCoreApplication.translate("Dialog", u"Titel/Signatursuche", None))
___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(1)
___qtablewidgetitem.setText(QCoreApplication.translate("Dialog", u"Apparat", None));
___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(2)
___qtablewidgetitem1.setText(QCoreApplication.translate("Dialog", u"Signatur", None));
___qtablewidgetitem2 = self.tableWidget.horizontalHeaderItem(3)
___qtablewidgetitem2.setText(QCoreApplication.translate("Dialog", u"Titel", None));
___qtablewidgetitem3 = self.tableWidget.horizontalHeaderItem(4)
___qtablewidgetitem3.setText(QCoreApplication.translate("Dialog", u"Auflage", None));
___qtablewidgetitem4 = self.tableWidget.horizontalHeaderItem(5)
___qtablewidgetitem4.setText(QCoreApplication.translate("Dialog", u"ISBN", None));
___qtablewidgetitem5 = self.tableWidget.horizontalHeaderItem(6)
___qtablewidgetitem5.setText(QCoreApplication.translate("Dialog", u"ID", None));
self.reset_btn.setText(QCoreApplication.translate("Dialog", u"Zur\u00fccksetzen", None))
self.delete_btn.setText(QCoreApplication.translate("Dialog", u"L\u00f6schen", None))
self.cancel_btn.setText(QCoreApplication.translate("Dialog", u"Abbrechen", None))
# retranslateUi

View File

@@ -13,10 +13,6 @@
<property name="windowTitle"> <property name="windowTitle">
<string>eMail Voransicht</string> <string>eMail Voransicht</string>
</property> </property>
<property name="windowIcon">
<iconset>
<normaloff>../../../../../../icons/email.svg</normaloff>../../../../../../icons/email.svg</iconset>
</property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
@@ -190,9 +186,7 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<resources> <resources/>
<include location="../../../../resources.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@@ -1,115 +1,177 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\mail_preview.ui' # -*- coding: utf-8 -*-
#
# Created by: PySide6 UI code generator 6.8.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
################################################################################
## Form generated from reading UI file 'mail_preview.ui'
##
## Created by: Qt User Interface Compiler version 6.9.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6 import QtCore, QtGui, QtWidgets from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel,
QLineEdit, QPushButton, QRadioButton, QSizePolicy,
QSpacerItem, QTextEdit, QWidget)
class Ui_eMailPreview(object): class Ui_eMailPreview(object):
def setupUi(self, eMailPreview): def setupUi(self, eMailPreview):
eMailPreview.setObjectName("eMailPreview") if not eMailPreview.objectName():
eMailPreview.setObjectName(u"eMailPreview")
eMailPreview.resize(700, 668) eMailPreview.resize(700, 668)
icon = QtGui.QIcon() self.gridLayout_2 = QGridLayout(eMailPreview)
icon.addPixmap(QtGui.QPixmap("c:\\Users\\aky547\\GitHub\\SemesterapparatsManager\\src\\ui\\dialogs\\dialog_sources\\../../../../../../icons/email.svg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.gridLayout_2.setObjectName(u"gridLayout_2")
eMailPreview.setWindowIcon(icon) self.gridLayout = QGridLayout()
self.gridLayout_2 = QtWidgets.QGridLayout(eMailPreview) self.gridLayout.setObjectName(u"gridLayout")
self.gridLayout_2.setObjectName("gridLayout_2") self.prof_name = QLineEdit(eMailPreview)
self.gridLayout = QtWidgets.QGridLayout() self.prof_name.setObjectName(u"prof_name")
self.gridLayout.setObjectName("gridLayout")
self.prof_name = QtWidgets.QLineEdit(parent=eMailPreview)
self.prof_name.setObjectName("prof_name")
self.gridLayout.addWidget(self.prof_name, 2, 2, 1, 1) self.gridLayout.addWidget(self.prof_name, 2, 2, 1, 1)
self.newTemplate = QtWidgets.QPushButton(parent=eMailPreview)
self.newTemplate = QPushButton(eMailPreview)
self.newTemplate.setObjectName(u"newTemplate")
self.newTemplate.setAutoFillBackground(False) self.newTemplate.setAutoFillBackground(False)
self.newTemplate.setText("") self.newTemplate.setIconSize(QSize(24, 24))
self.newTemplate.setIconSize(QtCore.QSize(24, 24))
self.newTemplate.setAutoDefault(True) self.newTemplate.setAutoDefault(True)
self.newTemplate.setDefault(False)
self.newTemplate.setFlat(False) self.newTemplate.setFlat(False)
self.newTemplate.setObjectName("newTemplate")
self.gridLayout.addWidget(self.newTemplate, 0, 3, 1, 1) self.gridLayout.addWidget(self.newTemplate, 0, 3, 1, 1)
self.comboBox = QtWidgets.QComboBox(parent=eMailPreview)
self.comboBox.setObjectName("comboBox") self.comboBox = QComboBox(eMailPreview)
self.comboBox.setObjectName(u"comboBox")
self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1) self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1)
self.mail_header = QtWidgets.QLineEdit(parent=eMailPreview)
self.mail_header.setObjectName("mail_header") self.mail_header = QLineEdit(eMailPreview)
self.mail_header.setObjectName(u"mail_header")
self.gridLayout.addWidget(self.mail_header, 3, 2, 1, 1) self.gridLayout.addWidget(self.mail_header, 3, 2, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=eMailPreview)
self.label_6.setObjectName("label_6") self.label_6 = QLabel(eMailPreview)
self.label_6.setObjectName(u"label_6")
self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1) self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1)
self.mail_body = QtWidgets.QTextEdit(parent=eMailPreview)
self.mail_body.setObjectName("mail_body") self.mail_body = QTextEdit(eMailPreview)
self.mail_body.setObjectName(u"mail_body")
self.gridLayout.addWidget(self.mail_body, 5, 2, 1, 1) self.gridLayout.addWidget(self.mail_body, 5, 2, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=eMailPreview)
self.label_2.setObjectName("label_2") self.label_2 = QLabel(eMailPreview)
self.label_2.setObjectName(u"label_2")
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
self.mail_name = QtWidgets.QLineEdit(parent=eMailPreview)
self.mail_name.setObjectName("mail_name") self.mail_name = QLineEdit(eMailPreview)
self.mail_name.setObjectName(u"mail_name")
self.gridLayout.addWidget(self.mail_name, 1, 2, 1, 1) self.gridLayout.addWidget(self.mail_name, 1, 2, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=eMailPreview)
self.label_5.setObjectName("label_5") self.label_5 = QLabel(eMailPreview)
self.label_5.setObjectName(u"label_5")
self.gridLayout.addWidget(self.label_5, 0, 0, 1, 1) self.gridLayout.addWidget(self.label_5, 0, 0, 1, 1)
self.label_4 = QtWidgets.QLabel(parent=eMailPreview)
self.label_4.setObjectName("label_4") self.label_4 = QLabel(eMailPreview)
self.label_4.setObjectName(u"label_4")
self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1) self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.horizontalLayout_3 = QHBoxLayout()
self.gender_male = QtWidgets.QRadioButton(parent=eMailPreview) self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.gender_male.setObjectName("gender_male") self.gender_male = QRadioButton(eMailPreview)
self.gender_male.setObjectName(u"gender_male")
self.horizontalLayout_3.addWidget(self.gender_male) self.horizontalLayout_3.addWidget(self.gender_male)
self.gender_female = QtWidgets.QRadioButton(parent=eMailPreview)
self.gender_female.setObjectName("gender_female") self.gender_female = QRadioButton(eMailPreview)
self.gender_female.setObjectName(u"gender_female")
self.horizontalLayout_3.addWidget(self.gender_female) self.horizontalLayout_3.addWidget(self.gender_female)
self.gender_non = QtWidgets.QRadioButton(parent=eMailPreview)
self.gender_non.setObjectName("gender_non") self.gender_non = QRadioButton(eMailPreview)
self.gender_non.setObjectName(u"gender_non")
self.horizontalLayout_3.addWidget(self.gender_non) self.horizontalLayout_3.addWidget(self.gender_non)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(spacerItem) self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer)
self.gridLayout.addLayout(self.horizontalLayout_3, 4, 2, 1, 1) self.gridLayout.addLayout(self.horizontalLayout_3, 4, 2, 1, 1)
self.label_3 = QtWidgets.QLabel(parent=eMailPreview)
self.label_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop) self.label_3 = QLabel(eMailPreview)
self.label_3.setObjectName("label_3") self.label_3.setObjectName(u"label_3")
self.label_3.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.gridLayout.addWidget(self.label_3, 5, 0, 1, 1) self.gridLayout.addWidget(self.label_3, 5, 0, 1, 1)
self.label = QtWidgets.QLabel(parent=eMailPreview)
self.label.setObjectName("label") self.label = QLabel(eMailPreview)
self.label.setObjectName(u"label")
self.gridLayout.addWidget(self.label, 1, 0, 1, 1) self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.horizontalLayout_2 = QHBoxLayout()
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.addItem(spacerItem1) self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.btn_okay = QtWidgets.QPushButton(parent=eMailPreview)
self.btn_okay.setStatusTip("") self.horizontalLayout_2.addItem(self.horizontalSpacer_2)
self.btn_okay.setObjectName("btn_okay")
self.btn_okay = QPushButton(eMailPreview)
self.btn_okay.setObjectName(u"btn_okay")
self.horizontalLayout_2.addWidget(self.btn_okay) self.horizontalLayout_2.addWidget(self.btn_okay)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=eMailPreview)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) self.buttonBox = QDialogButtonBox(eMailPreview)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel) self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
self.buttonBox.setCenterButtons(True) self.buttonBox.setCenterButtons(True)
self.buttonBox.setObjectName("buttonBox")
self.horizontalLayout_2.addWidget(self.buttonBox) self.horizontalLayout_2.addWidget(self.buttonBox)
self.gridLayout.addLayout(self.horizontalLayout_2, 6, 2, 1, 1) self.gridLayout.addLayout(self.horizontalLayout_2, 6, 2, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1) self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
self.retranslateUi(eMailPreview) self.retranslateUi(eMailPreview)
self.buttonBox.accepted.connect(eMailPreview.accept) # type: ignore self.buttonBox.accepted.connect(eMailPreview.accept)
self.buttonBox.rejected.connect(eMailPreview.reject) # type: ignore self.buttonBox.rejected.connect(eMailPreview.reject)
QtCore.QMetaObject.connectSlotsByName(eMailPreview)
self.newTemplate.setDefault(False)
QMetaObject.connectSlotsByName(eMailPreview)
# setupUi
def retranslateUi(self, eMailPreview): def retranslateUi(self, eMailPreview):
_translate = QtCore.QCoreApplication.translate eMailPreview.setWindowTitle(QCoreApplication.translate("eMailPreview", u"eMail Voransicht", None))
eMailPreview.setWindowTitle(_translate("eMailPreview", "eMail Voransicht")) self.newTemplate.setText("")
self.label_6.setText(_translate("eMailPreview", "Anrede")) self.label_6.setText(QCoreApplication.translate("eMailPreview", u"Anrede", None))
self.label_2.setText(_translate("eMailPreview", "Prof")) self.label_2.setText(QCoreApplication.translate("eMailPreview", u"Prof", None))
self.label_5.setText(_translate("eMailPreview", "Art")) self.label_5.setText(QCoreApplication.translate("eMailPreview", u"Art", None))
self.label_4.setText(_translate("eMailPreview", "Betreff")) self.label_4.setText(QCoreApplication.translate("eMailPreview", u"Betreff", None))
self.gender_male.setText(_translate("eMailPreview", "M")) self.gender_male.setText(QCoreApplication.translate("eMailPreview", u"M", None))
self.gender_female.setText(_translate("eMailPreview", "W")) self.gender_female.setText(QCoreApplication.translate("eMailPreview", u"W", None))
self.gender_non.setText(_translate("eMailPreview", "Divers")) self.gender_non.setText(QCoreApplication.translate("eMailPreview", u"Divers", None))
self.label_3.setText(_translate("eMailPreview", "Mail")) self.label_3.setText(QCoreApplication.translate("eMailPreview", u"Mail", None))
self.label.setText(_translate("eMailPreview", "eMail")) self.label.setText(QCoreApplication.translate("eMailPreview", u"eMail", None))
self.btn_okay.setWhatsThis(_translate("eMailPreview", "test")) #if QT_CONFIG(statustip)
self.btn_okay.setText(_translate("eMailPreview", "Senden")) self.btn_okay.setStatusTip("")
#endif // QT_CONFIG(statustip)
#if QT_CONFIG(whatsthis)
self.btn_okay.setWhatsThis(QCoreApplication.translate("eMailPreview", u"test", None))
#endif // QT_CONFIG(whatsthis)
self.btn_okay.setText(QCoreApplication.translate("eMailPreview", u"Senden", None))
# retranslateUi

View File

@@ -16,157 +16,6 @@
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="bold">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Fett</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="italic">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Kursiv</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="underlined">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Unterstrichen</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QFontComboBox" name="fontBox">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fontSize">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>9</string>
</property>
</item>
<item>
<property name="text">
<string>11</string>
</property>
</item>
<item>
<property name="text">
<string>12</string>
</property>
</item>
<item>
<property name="text">
<string>14</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>18</string>
</property>
</item>
<item>
<property name="text">
<string>20</string>
</property>
</item>
<item>
<property name="text">
<string>22</string>
</property>
</item>
<item>
<property name="text">
<string>24</string>
</property>
</item>
<item>
<property name="text">
<string>26</string>
</property>
</item>
<item>
<property name="text">
<string>28</string>
</property>
</item>
<item>
<property name="text">
<string>36</string>
</property>
</item>
<item>
<property name="text">
<string>48</string>
</property>
</item>
<item>
<property name="text">
<string>76</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4"/>
</item>
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
@@ -308,9 +157,7 @@
<tabstop>testTemplate</tabstop> <tabstop>testTemplate</tabstop>
<tabstop>insertPlaceholder</tabstop> <tabstop>insertPlaceholder</tabstop>
<tabstop>lineEdit</tabstop> <tabstop>lineEdit</tabstop>
<tabstop>fontSize</tabstop>
<tabstop>placeholder_list</tabstop> <tabstop>placeholder_list</tabstop>
<tabstop>fontBox</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections/> <connections/>

View File

@@ -1,163 +1,140 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\newMailTemplateDesigner.ui' # -*- coding: utf-8 -*-
#
# Created by: PySide6 UI code generator 6.8.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
################################################################################
## Form generated from reading UI file 'newMailTemplateDesigner.ui'
##
## Created by: Qt User Interface Compiler version 6.9.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6 import QtCore, QtGui, QtWidgets from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel,
QLineEdit, QPushButton, QSizePolicy, QSpacerItem,
QTextEdit, QVBoxLayout, QWidget)
class Ui_Dialog(object): class Ui_Dialog(object):
def setupUi(self, Dialog): def setupUi(self, Dialog):
Dialog.setObjectName("Dialog") if not Dialog.objectName():
Dialog.setObjectName(u"Dialog")
Dialog.resize(689, 572) Dialog.resize(689, 572)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog) self.verticalLayout_2 = QVBoxLayout(Dialog)
self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.gridLayout = QGridLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.gridLayout.setObjectName(u"gridLayout")
self.bold = QtWidgets.QPushButton(parent=Dialog) self.label = QLabel(Dialog)
self.bold.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.label.setObjectName(u"label")
self.bold.setCheckable(True)
self.bold.setObjectName("bold")
self.horizontalLayout_2.addWidget(self.bold)
self.italic = QtWidgets.QPushButton(parent=Dialog)
self.italic.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.italic.setCheckable(True)
self.italic.setObjectName("italic")
self.horizontalLayout_2.addWidget(self.italic)
self.underlined = QtWidgets.QPushButton(parent=Dialog)
self.underlined.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.underlined.setCheckable(True)
self.underlined.setObjectName("underlined")
self.horizontalLayout_2.addWidget(self.underlined)
self.fontBox = QtWidgets.QFontComboBox(parent=Dialog)
self.fontBox.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.fontBox.setObjectName("fontBox")
self.horizontalLayout_2.addWidget(self.fontBox)
self.fontSize = QtWidgets.QComboBox(parent=Dialog)
self.fontSize.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
self.fontSize.setObjectName("fontSize")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.fontSize.addItem("")
self.horizontalLayout_2.addWidget(self.fontSize)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.verticalLayout.addLayout(self.horizontalLayout_4)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(parent=Dialog)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1) self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.placeholder_list = QtWidgets.QComboBox(parent=Dialog)
self.placeholder_list.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.placeholder_list = QComboBox(Dialog)
self.placeholder_list.setSizeAdjustPolicy(QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToContents)
self.placeholder_list.setObjectName("placeholder_list")
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.setObjectName(u"placeholder_list")
self.placeholder_list.setFocusPolicy(Qt.NoFocus)
self.placeholder_list.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.gridLayout.addWidget(self.placeholder_list, 1, 0, 1, 1) self.gridLayout.addWidget(self.placeholder_list, 1, 0, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=Dialog)
self.label_2.setObjectName("label_2") self.label_2 = QLabel(Dialog)
self.label_2.setObjectName(u"label_2")
self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1) self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
self.lineEdit = QtWidgets.QLineEdit(parent=Dialog)
self.lineEdit = QLineEdit(Dialog)
self.lineEdit.setObjectName(u"lineEdit")
self.lineEdit.setEnabled(True) self.lineEdit.setEnabled(True)
self.lineEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.lineEdit.setFocusPolicy(Qt.NoFocus)
self.lineEdit.setFrame(False) self.lineEdit.setFrame(False)
self.lineEdit.setReadOnly(True) self.lineEdit.setReadOnly(True)
self.lineEdit.setObjectName("lineEdit")
self.gridLayout.addWidget(self.lineEdit, 1, 1, 1, 1) self.gridLayout.addWidget(self.lineEdit, 1, 1, 1, 1)
self.insertPlaceholder = QtWidgets.QPushButton(parent=Dialog)
self.insertPlaceholder.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.insertPlaceholder = QPushButton(Dialog)
self.insertPlaceholder.setObjectName("insertPlaceholder") self.insertPlaceholder.setObjectName(u"insertPlaceholder")
self.insertPlaceholder.setFocusPolicy(Qt.NoFocus)
self.gridLayout.addWidget(self.insertPlaceholder, 1, 2, 1, 1) self.gridLayout.addWidget(self.insertPlaceholder, 1, 2, 1, 1)
self.verticalLayout.addLayout(self.gridLayout) self.verticalLayout.addLayout(self.gridLayout)
self.label_3 = QtWidgets.QLabel(parent=Dialog)
self.label_3.setObjectName("label_3") self.label_3 = QLabel(Dialog)
self.label_3.setObjectName(u"label_3")
self.verticalLayout.addWidget(self.label_3) self.verticalLayout.addWidget(self.label_3)
self.subject = QtWidgets.QLineEdit(parent=Dialog)
self.subject.setObjectName("subject") self.subject = QLineEdit(Dialog)
self.subject.setObjectName(u"subject")
self.verticalLayout.addWidget(self.subject) self.verticalLayout.addWidget(self.subject)
self.templateEdit = QtWidgets.QTextEdit(parent=Dialog)
self.templateEdit.setObjectName("templateEdit") self.templateEdit = QTextEdit(Dialog)
self.templateEdit.setObjectName(u"templateEdit")
self.verticalLayout.addWidget(self.templateEdit) self.verticalLayout.addWidget(self.templateEdit)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.horizontalLayout_3 = QHBoxLayout()
self.testTemplate = QtWidgets.QPushButton(parent=Dialog) self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.testTemplate.setObjectName("testTemplate") self.testTemplate = QPushButton(Dialog)
self.testTemplate.setObjectName(u"testTemplate")
self.horizontalLayout_3.addWidget(self.testTemplate) self.horizontalLayout_3.addWidget(self.testTemplate)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(spacerItem1) self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer_2)
self.verticalLayout.addLayout(self.horizontalLayout_3) self.verticalLayout.addLayout(self.horizontalLayout_3)
self.verticalLayout_2.addLayout(self.verticalLayout) self.verticalLayout_2.addLayout(self.verticalLayout)
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) self.buttonBox = QDialogButtonBox(Dialog)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Discard|QtWidgets.QDialogButtonBox.StandardButton.Save) self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Discard|QDialogButtonBox.Save)
self.verticalLayout_2.addWidget(self.buttonBox) self.verticalLayout_2.addWidget(self.buttonBox)
QWidget.setTabOrder(self.subject, self.templateEdit)
QWidget.setTabOrder(self.templateEdit, self.testTemplate)
QWidget.setTabOrder(self.testTemplate, self.insertPlaceholder)
QWidget.setTabOrder(self.insertPlaceholder, self.lineEdit)
QWidget.setTabOrder(self.lineEdit, self.placeholder_list)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.fontSize.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(Dialog) QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.subject, self.templateEdit) # setupUi
Dialog.setTabOrder(self.templateEdit, self.testTemplate)
Dialog.setTabOrder(self.testTemplate, self.insertPlaceholder)
Dialog.setTabOrder(self.insertPlaceholder, self.lineEdit)
Dialog.setTabOrder(self.lineEdit, self.fontSize)
Dialog.setTabOrder(self.fontSize, self.placeholder_list)
Dialog.setTabOrder(self.placeholder_list, self.fontBox)
def retranslateUi(self, Dialog): def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
Dialog.setWindowTitle(_translate("Dialog", "Dialog")) self.label.setText(QCoreApplication.translate("Dialog", u"Platzhalter", None))
self.bold.setText(_translate("Dialog", "Fett")) self.placeholder_list.setItemText(0, QCoreApplication.translate("Dialog", u"\u00abAnrede\u00bb", None))
self.italic.setText(_translate("Dialog", "Kursiv")) self.placeholder_list.setItemText(1, QCoreApplication.translate("Dialog", u"\u00abApparatsName\u00bb", None))
self.underlined.setText(_translate("Dialog", "Unterstrichen")) self.placeholder_list.setItemText(2, QCoreApplication.translate("Dialog", u"\u00abApparatsFach\u00bb", None))
self.fontSize.setItemText(0, _translate("Dialog", "8")) self.placeholder_list.setItemText(3, QCoreApplication.translate("Dialog", u"\u00abApparatsNummer\u00bb", None))
self.fontSize.setItemText(1, _translate("Dialog", "9")) self.placeholder_list.setItemText(4, QCoreApplication.translate("Dialog", u"\u00abDozentName\u00bb", None))
self.fontSize.setItemText(2, _translate("Dialog", "11")) self.placeholder_list.setItemText(5, QCoreApplication.translate("Dialog", u"\u00abSignatur\u00bb", None))
self.fontSize.setItemText(3, _translate("Dialog", "12"))
self.fontSize.setItemText(4, _translate("Dialog", "14")) self.label_2.setText(QCoreApplication.translate("Dialog", u"Beschreibung", None))
self.fontSize.setItemText(5, _translate("Dialog", "16")) self.insertPlaceholder.setText(QCoreApplication.translate("Dialog", u"An aktiver Position einf\u00fcgen", None))
self.fontSize.setItemText(6, _translate("Dialog", "18")) self.label_3.setText(QCoreApplication.translate("Dialog", u"Betreff", None))
self.fontSize.setItemText(7, _translate("Dialog", "20")) self.testTemplate.setText(QCoreApplication.translate("Dialog", u"Template testen", None))
self.fontSize.setItemText(8, _translate("Dialog", "22")) # retranslateUi
self.fontSize.setItemText(9, _translate("Dialog", "24"))
self.fontSize.setItemText(10, _translate("Dialog", "26"))
self.fontSize.setItemText(11, _translate("Dialog", "28"))
self.fontSize.setItemText(12, _translate("Dialog", "36"))
self.fontSize.setItemText(13, _translate("Dialog", "48"))
self.fontSize.setItemText(14, _translate("Dialog", "76"))
self.label.setText(_translate("Dialog", "Platzhalter"))
self.placeholder_list.setItemText(0, _translate("Dialog", "«Anrede»"))
self.placeholder_list.setItemText(1, _translate("Dialog", "«ApparatsName»"))
self.placeholder_list.setItemText(2, _translate("Dialog", "«ApparatsFach»"))
self.placeholder_list.setItemText(3, _translate("Dialog", "«ApparatsNummer»"))
self.placeholder_list.setItemText(4, _translate("Dialog", "«DozentName»"))
self.placeholder_list.setItemText(5, _translate("Dialog", "«Signatur»"))
self.label_2.setText(_translate("Dialog", "Beschreibung"))
self.insertPlaceholder.setText(_translate("Dialog", "An aktiver Position einfügen"))
self.label_3.setText(_translate("Dialog", "Betreff"))
self.testTemplate.setText(_translate("Dialog", "Template testen"))

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>808</width>
<height>629</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableWidget" name="tableWidget">
<column>
<property name="text">
<string>Bestellen</string>
</property>
</column>
<column>
<property name="text">
<string>Signatur</string>
</property>
</column>
<column>
<property name="text">
<string>Titel</string>
</property>
</column>
<column>
<property name="text">
<string>ISBN</string>
</property>
</column>
<column>
<property name="text">
<string>Autor</string>
</property>
</column>
<column>
<property name="text">
<string>Auflage</string>
</property>
</column>
<column>
<property name="text">
<string>Standort</string>
</property>
</column>
<column>
<property name="text">
<string>Link</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Bestellen</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'order_neweditions.ui'
##
## Created by: Qt User Interface Compiler version 6.9.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QHeaderView,
QPushButton, QSizePolicy, QSpacerItem, QTableWidget,
QTableWidgetItem, QVBoxLayout, QWidget)
class Ui_Dialog(object):
def setupUi(self, Dialog):
if not Dialog.objectName():
Dialog.setObjectName(u"Dialog")
Dialog.resize(808, 629)
self.verticalLayout = QVBoxLayout(Dialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.tableWidget = QTableWidget(Dialog)
if (self.tableWidget.columnCount() < 8):
self.tableWidget.setColumnCount(8)
__qtablewidgetitem = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
__qtablewidgetitem1 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1)
__qtablewidgetitem2 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(2, __qtablewidgetitem2)
__qtablewidgetitem3 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3)
__qtablewidgetitem4 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(4, __qtablewidgetitem4)
__qtablewidgetitem5 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(5, __qtablewidgetitem5)
__qtablewidgetitem6 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(6, __qtablewidgetitem6)
__qtablewidgetitem7 = QTableWidgetItem()
self.tableWidget.setHorizontalHeaderItem(7, __qtablewidgetitem7)
self.tableWidget.setObjectName(u"tableWidget")
self.verticalLayout.addWidget(self.tableWidget)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
self.pushButton = QPushButton(Dialog)
self.pushButton.setObjectName(u"pushButton")
self.horizontalLayout.addWidget(self.pushButton)
self.verticalLayout.addLayout(self.horizontalLayout)
self.retranslateUi(Dialog)
QMetaObject.connectSlotsByName(Dialog)
# setupUi
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(0)
___qtablewidgetitem.setText(QCoreApplication.translate("Dialog", u"Bestellen", None));
___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(1)
___qtablewidgetitem1.setText(QCoreApplication.translate("Dialog", u"Signatur", None));
___qtablewidgetitem2 = self.tableWidget.horizontalHeaderItem(2)
___qtablewidgetitem2.setText(QCoreApplication.translate("Dialog", u"Titel", None));
___qtablewidgetitem3 = self.tableWidget.horizontalHeaderItem(3)
___qtablewidgetitem3.setText(QCoreApplication.translate("Dialog", u"ISBN", None));
___qtablewidgetitem4 = self.tableWidget.horizontalHeaderItem(4)
___qtablewidgetitem4.setText(QCoreApplication.translate("Dialog", u"Autor", None));
___qtablewidgetitem5 = self.tableWidget.horizontalHeaderItem(5)
___qtablewidgetitem5.setText(QCoreApplication.translate("Dialog", u"Auflage", None));
___qtablewidgetitem6 = self.tableWidget.horizontalHeaderItem(6)
___qtablewidgetitem6.setText(QCoreApplication.translate("Dialog", u"Standort", None));
___qtablewidgetitem7 = self.tableWidget.horizontalHeaderItem(7)
___qtablewidgetitem7.setText(QCoreApplication.translate("Dialog", u"Link", None));
self.pushButton.setText(QCoreApplication.translate("Dialog", u"Bestellen", None))
# retranslateUi

View File

@@ -1,10 +1,12 @@
from .dialog_sources.documentprint_ui import Ui_Dialog
from PySide6 import QtWidgets, QtCore
from src import Icon
from src.utils.richtext import SemapSchilder, SemesterDocument
from src.backend import Semester, Database
from natsort import natsorted from natsort import natsorted
from PySide6 import QtWidgets
from src import Icon
from src.backend import Database
from src.logic import Semester
from src.utils.richtext import SemapSchilder, SemesterDocument
from .dialog_sources.documentprint_ui import Ui_Dialog
class DocumentPrintDialog(QtWidgets.QDialog, Ui_Dialog): class DocumentPrintDialog(QtWidgets.QDialog, Ui_Dialog):
@@ -23,25 +25,25 @@ class DocumentPrintDialog(QtWidgets.QDialog, Ui_Dialog):
# Ensure the signal is connected only once # Ensure the signal is connected only once
try: try:
self.pushButton_2.clicked.disconnect() self.pushButton_2.clicked.disconnect()
except TypeError: except (TypeError, RuntimeWarning):
pass # Signal was not connected before pass # Signal was not connected before
self.pushButton_2.clicked.connect(self.on_pushButton_2_clicked) self.pushButton_2.clicked.connect(self.on_pushButton_2_clicked)
try: try:
self.pushButton.clicked.disconnect() self.pushButton.clicked.disconnect()
except TypeError: except (TypeError, RuntimeWarning):
pass pass
self.pushButton.clicked.connect(self.on_pushButton_clicked) self.pushButton.clicked.connect(self.on_pushButton_clicked)
try: try:
self.btn_load_current_apparats.clicked.disconnect() self.btn_load_current_apparats.clicked.disconnect()
except TypeError: except (TypeError, RuntimeWarning):
pass pass
self.btn_load_current_apparats.clicked.connect(self.load_current_clicked) self.btn_load_current_apparats.clicked.connect(self.load_current_clicked)
try: try:
self.manualCheck.clicked.disconnect() self.manualCheck.clicked.disconnect()
except TypeError: except (TypeError, RuntimeWarning):
pass pass
self.manualCheck.clicked.connect(self.manual_request) self.manualCheck.clicked.connect(self.manual_request)
@@ -108,10 +110,10 @@ class DocumentPrintDialog(QtWidgets.QDialog, Ui_Dialog):
def on_pushButton_clicked(self): def on_pushButton_clicked(self):
apparats: list[tuple[int, str]] = [] apparats: list[tuple[int, str]] = []
apps = self.db.getAllAparats(0) apps = self.db.getAllAparats(0)
apps = natsorted(apps, key=lambda x: x[4], reverse=True) apps = natsorted(apps, key=lambda x: x.appnr, reverse=True)
for app in apps: for app in apps:
prof = self.db.getProfById(app[2]) prof = self.db.getProfById(app.prof_id)
data = (app[4], f"{prof.lastname} ({app[1]})") data = (app.appnr, f"{prof.lastname} ({app.name})")
apparats.append(data) apparats.append(data)
SemesterDocument( SemesterDocument(
semester=self.semester.value, semester=self.semester.value,

View File

@@ -1,9 +1,11 @@
from .dialog_sources.Ui_elsa_add_table_entry import Ui_Dialog
from src.logic.webrequest import WebRequest, BibTextTransformer
from src import Icon
from PySide6 import QtWidgets from PySide6 import QtWidgets
from src.transformers.transformers import DictToTable
from src import Icon
from src.logic.webrequest import BibTextTransformer, WebRequest
from src.logic.zotero import ZoteroController from src.logic.zotero import ZoteroController
from src.transformers.transformers import DictToTable
from .dialog_sources.elsa_add_table_entry_ui import Ui_Dialog
zot = ZoteroController() zot = ZoteroController()
dtt = DictToTable() dtt = DictToTable()
@@ -174,7 +176,7 @@ class ElsaAddEntry(QtWidgets.QDialog, Ui_Dialog):
self.stackedWidget.setCurrentIndex(3) self.stackedWidget.setCurrentIndex(3)
def search(self, pages=None): def search(self, pages=None):
print("searching") # #print("searching")
param = self.searchIdent.text() param = self.searchIdent.text()
web = WebRequest() web = WebRequest()
web.get_ppn(param) web.get_ppn(param)

View File

@@ -1,6 +1,7 @@
from .dialog_sources.Ui_elsa_generator_confirm import Ui_Dialog
from PySide6 import QtWidgets from PySide6 import QtWidgets
from .dialog_sources.elsa_generator_confirm_ui import Ui_Dialog
class ElsaGenConfirm(QtWidgets.QDialog, Ui_Dialog): class ElsaGenConfirm(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent=None, data=None): def __init__(self, parent=None, data=None):

View File

@@ -70,7 +70,7 @@ class LoginDialog(Ui_Dialog):
def login(self): def login(self):
username = self.lineEdit.text() username = self.lineEdit.text()
password = self.lineEdit_2.text() password = self.lineEdit_2.text()
# print(type(username), password) # #print(type(username), password)
# Assuming 'Database' is a class to interact with your database # Assuming 'Database' is a class to interact with your database
hashed_password = hashlib.sha256(password.encode()).hexdigest() hashed_password = hashlib.sha256(password.encode()).hexdigest()

View File

@@ -1,52 +1,72 @@
import os import os
import re
import smtplib
import sys import sys
from PySide6 import QtWidgets from PySide6 import QtWidgets
from src import Icon, settings as config from src import Icon
from src import settings as config
from src.shared.logging import log
from .dialog_sources.mail_preview_ui import Ui_eMailPreview as MailPreviewDialog
from .dialog_sources.Ui_mail_preview import Ui_eMailPreview as MailPreviewDialog
from .mailTemplate import MailTemplateDialog from .mailTemplate import MailTemplateDialog
import loguru
import sys CSS_RESET = "<style>html,body{margin:0;padding:0}p{margin:0}</style>"
from src import LOG_DIR
log = loguru.logger empty_signature = """"""
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
def _escape_braces_in_style(html: str) -> str:
"""
Double curly braces ONLY inside <style>...</style> blocks so that
str.format(...) won't treat CSS as placeholders. The doubled braces
will automatically render back to single braces after formatting.
"""
empty_signature = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> def repl(m):
start, css, end = m.group(1), m.group(2), m.group(3)
css_escaped = css.replace("{", "{{").replace("}", "}}")
return f"{start}{css_escaped}{end}"
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style return re.sub(
type="text/css"> r"(<style[^>]*>)(.*?)(</style>)",
repl,
html,
flags=re.IGNORECASE | re.DOTALL,
)
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; } def _split_eml_headers_body(eml_text: str) -> tuple[str, str]:
"""
Return (headers, body_html). Robustly split on first blank line.
Accepts lines that contain only spaces/tabs as the separator.
"""
li.unchecked::marker { content: "\2610"; } parts = re.split(r"\r?\n[ \t]*\r?\n", eml_text, maxsplit=1)
if len(parts) == 2:
li.checked::marker { content: "\2612"; } return parts[0], parts[1]
# Fallback: try to split right after the Content-Transfer-Encoding line
</style></head><body style=" font-family:''Segoe UI''; font-size:9pt; font-weight:400; m = re.search(
font-style:normal;"> r"(?:^|\r?\n)Content-Transfer-Encoding:.*?(?:\r?\n)",
eml_text,
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; flags=re.I | re.S,
margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> )
""" if m:
return eml_text[: m.end()], eml_text[m.end() :]
return "", eml_text # last resort: treat entire content as body
class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog): class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
def __init__( def __init__(
self, self,
app_id, app_id=None,
app_name, app_name=None,
app_subject, app_subject=None,
prof_name, prof_name=None,
prof_mail, prof_mail=None,
accepted_books=None,
ordered_books=None,
parent=None, parent=None,
default_mail="Information zum Semesterapparat", default_mail="Information zum Semesterapparat",
): ):
@@ -58,6 +78,8 @@ class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
self.appname = app_name self.appname = app_name
self.subject = app_subject self.subject = app_subject
self.profname = prof_name self.profname = prof_name
self.books = accepted_books if accepted_books is not None else []
self.ordered_books = ordered_books if ordered_books is not None else []
self.mail_data = "" self.mail_data = ""
self.signature = self.determine_signature() self.signature = self.determine_signature()
self.prof_mail = prof_mail self.prof_mail = prof_mail
@@ -65,52 +87,29 @@ class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
self.prof_name.setText(prof_name) self.prof_name.setText(prof_name)
self.mail_name.setText(self.prof_mail) self.mail_name.setText(self.prof_mail)
self.load_mail_templates() self.load_mail_templates()
# if none of the radio buttons is checked, disable the accept button of the dialog
self.setWindowIcon(Icon("mail").icon) self.setWindowIcon(Icon("mail").icon)
self.btn_okay.setEnabled(False) self.btn_okay.setEnabled(False)
Icon("edit_note", self.newTemplate) Icon("edit_note", self.newTemplate)
self.newTemplate.clicked.connect(self.open_new_template) self.newTemplate.clicked.connect(self.open_new_template)
if default_mail is not None: if default_mail is not None:
# get the nearest match to the default mail
for i in range(self.comboBox.count()): for i in range(self.comboBox.count()):
if default_mail in self.comboBox.itemText(i): if default_mail in self.comboBox.itemText(i):
default_mail = self.comboBox.itemText(i) default_mail = self.comboBox.itemText(i)
break break
self.comboBox.setCurrentText(default_mail) self.comboBox.setCurrentText(default_mail)
self.comboBox.currentIndexChanged.connect(self.set_mail)
# re-render when user changes greeting via radio buttons
self.gender_female.clicked.connect(self.set_mail) self.gender_female.clicked.connect(self.set_mail)
self.gender_male.clicked.connect(self.set_mail) self.gender_male.clicked.connect(self.set_mail)
self.gender_non.clicked.connect(self.set_mail) self.gender_non.clicked.connect(self.set_mail)
# reflect initial state (OK disabled until a greeting is chosen)
self._update_ok_button()
self.btn_okay.clicked.connect(self.createAndSendMail) self.btn_okay.clicked.connect(self.createAndSendMail)
def open_new_template(self): # add these helpers inside Mail_Dialog
log.info("Opening new template dialog")
# TODO: implement new mail template dialog
dialog = MailTemplateDialog()
dialog.updateSignal.connect(self.load_mail_templates)
dialog.exec()
pass
def determine_signature(self):
if config.mail.signature is empty_signature or config.mail.signature == "":
return """Mit freundlichen Grüßen
Ihr Semesterapparatsteam
Mail: semesterapparate@ph-freiburg.de
Tel.: 0761/682-778 | 07617682-545"""
else:
return config.mail.signature
def load_mail_templates(self):
# print("loading mail templates")
log.info("Loading mail templates")
mail_templates = os.listdir("mail_vorlagen")
log.info(f"Mail templates: {mail_templates}")
self.comboBox.clear()
for template in mail_templates:
self.comboBox.addItem(template)
def get_greeting(self): def get_greeting(self):
prof = self.profname.split(" ")[0] prof = self.profname.split(" ")[0]
if self.gender_male.isChecked(): if self.gender_male.isChecked():
@@ -124,37 +123,104 @@ Tel.: 0761/682-778 | 07617682-545"""
name = f"{self.profname.split(' ')[1]} {self.profname.split(' ')[0]}" name = f"{self.profname.split(' ')[1]} {self.profname.split(' ')[0]}"
return f"Guten Tag {name}," return f"Guten Tag {name},"
def _update_ok_button(self):
checked = (
self.gender_male.isChecked()
or self.gender_female.isChecked()
or self.gender_non.isChecked()
)
self.btn_okay.setEnabled(checked)
def _on_gender_toggled(self, checked: bool):
# Only refresh when a button becomes checked
if checked:
self.set_mail()
def open_new_template(self):
log.info("Opening new template dialog")
dialog = MailTemplateDialog()
dialog.updateSignal.connect(self.load_mail_templates)
dialog.exec()
def determine_signature(self):
# use equality, not identity
if (
config.mail.signature == empty_signature
or config.mail.signature.strip() == ""
):
return """Mit freundlichen Grüßen
Ihr Semesterapparatsteam
Mail: semesterapparate@ph-freiburg.de
Tel.: 0761/682-778 | 0761/682-545"""
else:
return config.mail.signature
def load_mail_templates(self):
log.info("Loading mail templates")
mail_templates = [
f for f in os.listdir("mail_vorlagen") if f.lower().endswith(".eml")
]
log.info(f"Mail templates: {mail_templates}")
self.comboBox.clear()
for template in mail_templates:
self.comboBox.addItem(template)
def set_mail(self): def set_mail(self):
log.info("Setting mail") log.info("Setting mail")
self._update_ok_button() # keep OK enabled state in sync
email_template = self.comboBox.currentText() email_template = self.comboBox.currentText()
if email_template == "": if not email_template:
log.debug("No mail template selected") log.debug("No mail template selected")
return return
with open(f"mail_vorlagen/{email_template}", "r", encoding="utf-8") as f: with open(f"mail_vorlagen/{email_template}", "r", encoding="utf-8") as f:
mail_template = f.read() eml_text = f.read()
# header label for UI (unchanged)
email_header = email_template.split(".eml")[0] email_header = email_template.split(".eml")[0]
if "{AppNr}" in email_template: if "{AppNr}" in email_template:
email_header = email_template.split(".eml")[0] email_header = email_header.format(AppNr=self.appid, AppName=self.appname)
email_header = email_header.format(AppNr=self.appid, AppName=self.appname)
self.mail_header.setText(email_header) self.mail_header.setText(email_header)
self.mail_data = mail_template.split("<html>")[0]
mail_html = mail_template.split("<html>")[1]
mail_html = "<html>" + mail_html
Appname = self.appname
mail_html = mail_html.format(
Profname=self.profname.split(" ")[0],
Appname=Appname,
AppNr=self.appid,
AppSubject=self.subject,
greeting=self.get_greeting(),
signature=self.signature,
)
self.mail_body.setHtml(mail_html) headers, body_html = _split_eml_headers_body(eml_text)
body_html = _escape_braces_in_style(body_html)
# compute greeting from the current toggle selection
greeting = self.get_greeting()
try:
body_html = body_html.format(
Profname=self.profname.split(" ")[
0
], # last name if your template uses {Profname}
Appname=self.appname,
AppNr=self.appid,
AppSubject=self.subject,
greeting=greeting,
signature=self.signature,
newEditions="\n".join(
[
f"- {book.title} (ISBN: {','.join(book.isbn)}, Auflage: {book.edition if book.edition else 'nicht bekannt'}, In Bibliothek: {'ja' if getattr(book, 'signature', None) is not None and 'Handbibliothek' not in str(book.library_location) else 'nein'}, Typ: {book.get_book_type()}) Aktuelle Auflage: {book.old_book.edition if book.old_book and book.old_book.edition else 'nicht bekannt'}"
for book in (self.books or [])
]
)
if self.books
else "keine neuen Auflagen gefunden",
newEditionsOrdered="\n".join(
[
f" - {book.title}, ISBN: {','.join(book.isbn)}, Bibliotheksstandort : {book.library_location if book.library_location else 'N/A'}, Link: {book.link}"
for book in (self.ordered_books or [])
]
),
)
except Exception as e:
log.error(f"Template formatting failed: {e}")
self.mail_body.setPlainText(body_html)
def createAndSendMail(self): def createAndSendMail(self):
log.info("Sending mail") log.info("Sending mail")
import smtplib
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
@@ -168,31 +234,29 @@ Tel.: 0761/682-778 | 07617682-545"""
message["From"] = sender_email message["From"] = sender_email
message["To"] = self.prof_mail message["To"] = self.prof_mail
message["Subject"] = self.mail_header.text() message["Subject"] = self.mail_header.text()
# include a Fcc to the senders sent folder message["Cc"] = "semesterapparate@ph-freiburg.de"
message["cc"] = "semesterapparate@ph-freiburg.de"
mail_body = self.mail_body.toPlainText()
# strange_string = """p, li { white-space: pre-wrap; }
# hr { height: 1px; border-width: 0; }
# li.unchecked::marker { content: "\2610"; }
# li.checked::marker { content: "\2612"; }
# """
# mail_body.replace(strange_string, "")
message.attach(MIMEText(mail_body, "Plain", "utf-8"))
mail_body = self.mail_body.toHtml()
message.attach(MIMEText(mail_body, "html"))
mail = message.as_string() mail = message.as_string()
with smtplib.SMTP_SSL(smtp_server, port) as server: with smtplib.SMTP_SSL(smtp_server, port) as server:
server.connect(smtp_server, port) server.connect(smtp_server, port) # not needed for SMTP_SSL
# server.connect(smtp_server, port)
# server.auth(mechanism="PLAIN")
if config.mail.use_user_name is True: if config.mail.use_user_name is True:
# print(config["mail"]["user_name"])
server.login(config.mail.user_name, password) server.login(config.mail.user_name, password)
else: else:
server.login(sender_email, password) server.login(sender_email, password)
server.sendmail(sender_email, tolist, mail) server.sendmail(sender_email, tolist, mail)
# print("Mail sent")
# end active process
server.quit() server.quit()
pass
log.info("Mail sent, closing connection to server and dialog") log.info("Mail sent, closing connection to server and dialog")
# close the dialog
self.accept() self.accept()
@@ -217,8 +281,6 @@ def launch_gui(
if __name__ == "__main__": if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog() Dialog = QtWidgets.QDialog()
ui = Mail_Dialog() ui = Mail_Dialog()

View File

@@ -1,23 +1,18 @@
import os import os
import re
import sys
from PySide6 import QtGui, QtWidgets, QtCore from loguru import logger as log
from PySide6 import QtCore, QtWidgets
from src import Icon from src import Icon
from .dialog_sources import NewMailTemplateDesignerDialog from .dialog_sources import NewMailTemplateDesignerDialog
import sys
from loguru import logger as log
logger = log logger = log
logger.remove() logger.remove()
logger.add("logs/application.log", rotation="1 week", retention="1 month", enqueue=True) logger.add("logs/application.log", rotation="1 week", retention="1 month", enqueue=True)
log.add( log.add("logs/mail.log", enqueue=True)
f"logs/mail.log",
enqueue=True,
)
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
logger.add(sys.stdout) logger.add(sys.stdout)
@@ -28,35 +23,34 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.setWindowIcon(Icon("edit_note").icon) self.setWindowIcon(Icon("edit_note").icon)
self.setWindowTitle("Mailvorlage erstellen") self.setWindowTitle("Mailvorlage erstellen (Text)")
# placeholders UI
self.placeholder_list.addItem("") self.placeholder_list.addItem("")
self.placeholder_list.setCurrentText("") self.placeholder_list.setCurrentText("")
self.insertPlaceholder.clicked.connect(self.insert_placeholder) self.insertPlaceholder.clicked.connect(self.insert_placeholder)
self.placeholder_list.currentTextChanged.connect(self.updateDescription) self.placeholder_list.currentTextChanged.connect(self.updateDescription)
self.bold.clicked.connect(self.setFontBold)
self.italic.clicked.connect(self.setTextFontItalic) # formatting buttons (kept enabled for UX, but saving uses plain text)
self.underlined.clicked.connect(self.setTextFontUnderline)
self.testTemplate.clicked.connect(self.test_template) # buttons
self.fontBox.currentFontChanged.connect(self.setCurrentFont)
self.fontSize.currentTextChanged.connect(self.setFontSize)
# buttonbox
# save button
self.buttonBox.button( self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Save QtWidgets.QDialogButtonBox.StandardButton.Save
).clicked.connect(self.save_template) ).clicked.connect(self.save_template)
# discard button
self.buttonBox.button( self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Discard QtWidgets.QDialogButtonBox.StandardButton.Discard
).clicked.connect(self.discard_changes) ).clicked.connect(self.discard_changes)
# cancel button
self.buttonBox.button( self.buttonBox.button(
QtWidgets.QDialogButtonBox.StandardButton.Cancel QtWidgets.QDialogButtonBox.StandardButton.Cancel
).clicked.connect(self.closeNow) ).clicked.connect(self.closeNow)
log.info("Mail template dialog setup complete") log.info("Mail template dialog (plaintext) setup complete")
def _normalize_newlines(self, s: str) -> str:
# Convert any CRLF/CR to LF then back to CRLF for .eml
s = s.replace("\\r\\n", "\\n").replace("\\r", "\\n")
return s
def save_template(self): def save_template(self):
# print("save triggered")
# create a dialog to ask for the name of the template
dialog = QtWidgets.QInputDialog() dialog = QtWidgets.QInputDialog()
dialog.setInputMode(QtWidgets.QInputDialog.InputMode.TextInput) dialog.setInputMode(QtWidgets.QInputDialog.InputMode.TextInput)
dialog.setLabelText("Bitte geben Sie den Namen des Templates ein:") dialog.setLabelText("Bitte geben Sie den Namen des Templates ein:")
@@ -66,12 +60,11 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog):
dialog.setWindowIcon(Icon("save").icon) dialog.setWindowIcon(Icon("save").icon)
save = dialog.exec() save = dialog.exec()
template_name = dialog.textValue() template_name = dialog.textValue()
log.info("Saving template") log.info("Saving plaintext template")
if template_name != "" and save == 1: if template_name != "" and save == 1:
template = template_name + ".eml" template = template_name + ".eml"
if template in os.listdir("mail_vorlagen"): if template in os.listdir("mail_vorlagen"):
log.error("Template already exists") log.error("Template already exists")
# warning dialog
dialog = QtWidgets.QMessageBox() dialog = QtWidgets.QMessageBox()
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
Icon("warning", dialog) Icon("warning", dialog)
@@ -87,36 +80,34 @@ class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog):
ret = dialog.exec() ret = dialog.exec()
if ret == QtWidgets.QMessageBox.StandardButton.No: if ret == QtWidgets.QMessageBox.StandardButton.No:
return return
mail = f"""Subject: {self.subject.text()}
MIME-Version: 1.0 # Build plaintext from editor
Content-Type: text/html; charset="UTF-8" body_text = self.templateEdit.toPlainText()
Content-Transfer-Encoding: 8bit body_text = self._normalize_newlines(body_text)
{self.templateEdit.toHtml()}"""
html_head = """<html> # Build EML headers and payload (headers, then one blank line, then body)
<head> mail_headers = f"""
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> Subject: {self.subject.text()}
</head> """
""" mail_body = body_text
mail_base = mail.split("<html>")[0]
mail_body = mail.split("</head>")[1] eml = mail_headers + "\n\n" + mail_body
mail = mail_base + html_head + mail_body eml = re.sub(r" +", " ", eml) # remove multiple spaces
mail = ( eml = re.sub(r"\n +", "\n", eml) #
mail.replace("&lt;", "<") print(eml)
.replace("&gt;", ">")
.replace("&quot;", '"') with open(
.replace("&amp;", "&") f"mail_vorlagen/{template}", "w", encoding="utf-8", newline=""
) ) as f:
with open(f"mail_vorlagen/{template}", "w", encoding="utf-8") as f: f.write(eml)
f.write(mail)
self.updateSignal.emit() self.updateSignal.emit()
self.close() self.close()
log.success(f"Template {template} saved successfully") # log.success(f"Template {template} saved successfully (plaintext)")
else: else:
# warning dialog
dialog = QtWidgets.QMessageBox() dialog = QtWidgets.QMessageBox()
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning) dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
dialog.setWindowIcon(Icon("warning").icon) dialog.setWindowIcon(Icon("warning").icon)
dialog.setText("Bitte geben Sie einen Namen für das Template ein.") dialog.setText("Bitte geben Sie einen Namen für das Template ein.")
dialog.setWindowTitle("Fehler beim Speichern") dialog.setWindowTitle("Fehler beim Speichern")
dialog.exec() dialog.exec()
@@ -145,52 +136,20 @@ Content-Transfer-Encoding: 8bit
self.close() self.close()
def updateDescription(self): def updateDescription(self):
# print("update triggered")
# print(self.placeholder_list.currentText())
placeholders = { placeholders = {
"anrede": "Die Anrede beinhaltet sowohl Person als auch Sehr geehrte/r; dargestellt als: {greeting}", "anrede": "Die Anrede inkl. 'Sehr geehrte/r' oder neutral; dargestellt als: {greeting}",
"apparatsfach": "Das Fach, in welchem der Apparat angelegt wurde; dargestellt als: {AppSubject}", "apparatsfach": "Das Fach, in welchem der Apparat angelegt wurde; dargestellt als: {AppSubject}",
"apparatsname": "Der Name des Apparats; dargestellt als: {Appname}", "apparatsname": "Der Name des Apparats; dargestellt als: {Appname}",
"apparatsnummer": "Die Nummer des Apparats; dargestellt als: {AppNr}", "apparatsnummer": "Die Nummer des Apparats; dargestellt als: {AppNr}",
"dozentname": "Der Name des Dozenten / der Dozentin; dargestellt als: {Profname}", "dozentname": "Der Name des Dozenten / der Dozentin; dargestellt als: {Profname}",
"signatur": "Die persönliche / allgemeine Signatur am ende der Mail; dargestellt als: {signature}", "signatur": "Ihre Signatur; dargestellt als: {signature}",
"": " ", "": " ",
} }
for ( for key, item in placeholders.items():
key,
item,
) in placeholders.items():
if key in self.placeholder_list.currentText().lower(): if key in self.placeholder_list.currentText().lower():
self.lineEdit.setText(item) self.lineEdit.setText(item)
break break
def setCurrentFont(self):
font = self.fontBox.currentFont()
font.setPointSize(int(self.fontSize.currentText()))
self.templateEdit.setFont(font)
def setFontSize(self):
size = self.fontSize.currentText()
self.templateEdit.setFontPointSize(int(size))
def setFontBold(self):
if self.bold.isChecked():
self.templateEdit.setFontWeight(QtGui.QFont.Weight.Bold)
else:
self.templateEdit.setFontWeight(QtGui.QFont.Weight.Normal)
def setTextFontItalic(self):
if self.italic.isChecked():
self.templateEdit.setFontItalic(True)
else:
self.templateEdit.setFontItalic(False)
def setTextFontUnderline(self):
if self.underlined.isChecked():
self.templateEdit.setFontUnderline(True)
else:
self.templateEdit.setFontUnderline(False)
def test_template(self): def test_template(self):
placeholders = [ placeholders = [
"{greeting}", "{greeting}",
@@ -201,35 +160,29 @@ Content-Transfer-Encoding: 8bit
"{signature}", "{signature}",
] ]
mail_subject = self.subject.text() mail_subject = self.subject.text()
mail_body = self.templateEdit.toHtml() mail_body = self.templateEdit.toPlainText()
missing_body = [] missing_body = []
missing_subject = [] missing_subject = []
try: try:
assert placeholders[2] in mail_subject assert "{Appname}" in mail_subject or "{AppName}" in mail_subject
except AssertionError: except AssertionError:
missing_subject.append(placeholders[2]) missing_subject.append("{Appname}")
# check if all placeholders are in the mail body
for placeholder in placeholders: for placeholder in placeholders:
try: try:
assert placeholder in mail_body assert placeholder in mail_body
except AssertionError: except AssertionError:
missing_body.append(placeholder) missing_body.append(placeholder)
if missing_body != []: if missing_body:
# warning dialog
Icon("template_fail", self.testTemplate)
dialog = QtWidgets.QMessageBox() dialog = QtWidgets.QMessageBox()
dialog.setWindowIcon(Icon("warning").icon) dialog.setWindowIcon(Icon("warning").icon)
dialog.setText("Folgende Platzhalter fehlen im Template:") dialog.setText("Folgende Platzhalter fehlen im Template:")
missing = ( missing = (
"Betreff:\n" "Betreff:\n"
+ "\n".join(missing_subject) + "\\n".join(missing_subject)
+ "\n\n" + "\\n\\n"
+ "Mailtext:\n" + "Mailtext:\n"
+ "\n".join(missing_body) + "\\n".join(missing_body)
) )
dialog.setInformativeText(f"{missing}") dialog.setInformativeText(f"{missing}")
dialog.setWindowTitle("Fehlende Platzhalter") dialog.setWindowTitle("Fehlende Platzhalter")
dialog.exec() dialog.exec()
@@ -238,21 +191,16 @@ Content-Transfer-Encoding: 8bit
self.testTemplate.setText("") self.testTemplate.setText("")
def insert_placeholder(self): def insert_placeholder(self):
placeholder = { placeholder_map = {
"anrede": "{greeting}", "anrede": "{greeting}",
"apparatsfach": "{AppSubject}", "apparatsfach": "{AppSubject}",
"apparatsname": "{Appname}", "apparatsname": "{Appname}",
"apparatsnummer": "{AppNr}", "apparatsnummer": "{AppNr}",
"dozentname": "{Profname}", "dozentname": "{Profname}",
"signatur": """<pre class="moz-signature" cols="72">-- "signatur": "{signature}",
{signature}
</pre>""",
} }
cursor = self.templateEdit.textCursor() cursor = self.templateEdit.textCursor()
for ( for key, item in placeholder_map.items():
key,
item,
) in placeholder.items():
if key in self.placeholder_list.currentText().lower(): if key in self.placeholder_list.currentText().lower():
cursor.insertText(item) cursor.insertText(item)
break break

View File

@@ -1,8 +1,9 @@
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
from .dialog_sources.medianadder_ui import Ui_Dialog
from src import Icon from src import Icon
from .dialog_sources.medianadder_ui import Ui_Dialog
class MedienAdder(QtWidgets.QDialog, Ui_Dialog): class MedienAdder(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent=None): def __init__(self, parent=None):
@@ -78,4 +79,4 @@ def launch_gui():
dialog = MedienAdder() dialog = MedienAdder()
dialog.show() dialog.show()
app.exec() app.exec()
# print(dialog.mode, dialog.data, dialog.result()) # #print(dialog.mode, dialog.data, dialog.result())

View File

@@ -0,0 +1,109 @@
from PySide6 import QtCore, QtWidgets
from src.backend.catalogue import Catalogue
from src.backend.database import Database
from src.ui.dialogs.mail import Mail_Dialog
from .dialog_sources.order_neweditions_ui import Ui_Dialog
class NewEditionDialog(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, app_id, mail_data):
super().__init__()
self.setupUi(self)
self.setWindowTitle("Neuauflagen bestellen")
self.db = Database()
self.catalogue = Catalogue()
self.app_id = app_id
self.mail_data = mail_data
self.books = self.db.getNewEditionsByApparat(app_id)
self.pushButton.clicked.connect(self.orderBooks)
self.populateTable()
def populateTable(self):
for book in self.books:
# signature not required here; using book.signature directly when needed
link_label = QtWidgets.QLabel()
link = (
book.link
if book.link != "SWB"
else f"https://www.lehmanns.de/search/quick?mediatype_id=&q={book.isbn[0]}"
)
link_label.setText(f'<a href="{link}">Lehmanns.de</a>')
link_label.setOpenExternalLinks(True)
link_label.setTextFormat(QtCore.Qt.TextFormat.RichText)
link_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
link_label.setTextInteractionFlags(
QtCore.Qt.TextInteractionFlag.TextBrowserInteraction
)
self.tableWidget.insertRow(0)
# first column is checkbox for ordering
checkbox = QtWidgets.QCheckBox()
checked = True if not book.signature else False
if book.library_location is not None:
checked = True if "hb" in book.library_location else checked
checkbox.setChecked(checked)
self.tableWidget.setCellWidget(0, 0, checkbox)
self.tableWidget.setItem(
0,
1,
QtWidgets.QTableWidgetItem(
str(book.signature if book.signature else "")
),
)
self.tableWidget.setItem(0, 2, QtWidgets.QTableWidgetItem(book.title))
isbn = (
book.isbn[0]
if isinstance(book.isbn, list) and len(book.isbn) > 0
else book.isbn
)
self.tableWidget.setItem(0, 3, QtWidgets.QTableWidgetItem(isbn))
self.tableWidget.setItem(0, 4, QtWidgets.QTableWidgetItem(book.author))
self.tableWidget.setItem(0, 5, QtWidgets.QTableWidgetItem(book.edition))
self.tableWidget.setItem(
0, 6, QtWidgets.QTableWidgetItem(book.library_location)
)
self.tableWidget.setCellWidget(0, 7, link_label)
def orderBooks(self):
ordered_books = []
for row in range(self.tableWidget.rowCount()):
checkbox = self.tableWidget.cellWidget(row, 0)
if checkbox.isChecked():
book = self.books[row]
book.link = (
book.link
if book.link != "SWB"
else f"https://www.lehmanns.de/search/quick?mediatype_id=&q={book.isbn[0]}"
)
# print(f"Bestelle Neuauflage für {book.title} ({book.edition})")
book.isbn = [book.isbn] if isinstance(book.isbn, str) else book.isbn
ordered_books.append(book)
# Process ordered_books as needed
editionId = self.db.getNewEditionId(book)
self.db.setOrdered(editionId)
self.mail = Mail_Dialog(
app_id=self.mail_data.get("app_nr"),
prof_name=self.mail_data.get("prof_name"),
prof_mail=self.mail_data.get("prof_mail"),
app_name=self.mail_data.get("app_name"),
default_mail="Bitte um Bestellung",
ordered_books=ordered_books,
)
self.mail.exec()
def launch():
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication([])
mail_data = {
"prof_name": "Erwerbung",
"prof_mail": "carola.wiestler@ph-freiburg.de",
"app_nr": 131,
"app_name": "Beratung und Teamarbeit",
}
dialog = NewEditionDialog(app_id=18, mail_data=mail_data)
dialog.exec()

View File

@@ -1,17 +1,19 @@
import sys
import loguru
from PySide6 import QtWidgets from PySide6 import QtWidgets
from src import LOG_DIR
from src.backend import AutoAdder from src.backend import AutoAdder
from .dialog_sources.parsed_titles_ui import Ui_Form from .dialog_sources.parsed_titles_ui import Ui_Form
import loguru
import sys
from src import LOG_DIR
log = loguru.logger log = loguru.logger
log.remove() log.remove()
log.add(sys.stdout, level="INFO") log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
class ParsedTitles(QtWidgets.QWidget, Ui_Form): class ParsedTitles(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@@ -32,7 +34,7 @@ class ParsedTitles(QtWidgets.QWidget, Ui_Form):
self.progressBar.setValue(value) self.progressBar.setValue(value)
def worker_quit(self): def worker_quit(self):
# print("Terminating worker") # #print("Terminating worker")
self.worker.terminate() self.worker.terminate()
self.worker.quit() self.worker.quit()
self.worker.deleteLater() self.worker.deleteLater()
@@ -66,7 +68,7 @@ class ParsedTitles(QtWidgets.QWidget, Ui_Form):
def determine_progress(self, signal): def determine_progress(self, signal):
# check length of listWidget # check length of listWidget
length = self.listWidget.count() length = self.listWidget.count()
# print(f"Length of listWidget: {length}") # #print(f"Length of listWidget: {length}")
if length == 0: if length == 0:
log.info("AutoAdder finished") log.info("AutoAdder finished")
self.buttonBox.accepted.emit() self.buttonBox.accepted.emit()

View File

@@ -10,7 +10,7 @@ from PySide6 import QtWidgets
from src import Icon from src import Icon
from .dialog_sources.Ui_confirm_extend import Ui_extend_confirm from .dialog_sources.confirm_extend_ui import Ui_extend_confirm
class ConfirmDialog(QtWidgets.QDialog, Ui_extend_confirm): class ConfirmDialog(QtWidgets.QDialog, Ui_extend_confirm):

View File

@@ -0,0 +1,94 @@
from typing import List, Optional, Set, Union
import re
from PySide6 import QtCore
from PySide6.QtWidgets import QDialog, QPushButton, QVBoxLayout
from qtqdm import Qtqdm, QtqdmProgressBar
from src.logic import BookData
from src.logic.lehmannsapi import LehmannsClient
from src.logic.SRU import SWB
class CheckThread(QtCore.QThread):
updateSignal = QtCore.Signal()
total_entries_signal = QtCore.Signal(int)
resultSignal = QtCore.Signal(str)
etaSignal = QtCore.Signal(dict)
startSignal = QtCore.Signal()
progress = QtCore.Signal(dict)
def __init__(self, items: list[BookData]):
super().__init__()
self.items: List[BookData] = items
self.results: List[tuple[BookData, List[BookData]]] = []
self.total_entries_signal.emit(len(items))
def _update_callback(self, status):
self.progress.emit(status)
def run(self):
tqdm_object = Qtqdm(
range(len(self.items)),
unit_scale=True,
)
swb_client = SWB()
for i in tqdm_object:
book: BookData = self.items[i]
author = (
book.author.split(";")[0].replace(" ", "")
if ";" in book.author
else book.author.replace(" ", "")
)
# title = book.title.split(":")[0].strip()
# remove trailing punctuation from title
title = book.title.rstrip(" .:,;!?")
response: list[BookData] = []
response = swb_client.getBooks(
[
"pica.bib=20735",
f"pica.tit={title.split(':')[0].strip()}",
# f"pica.per={author}",
]
)
# in the response, remove the entry with the same ppn
response = [entry for entry in response if entry.ppn != book.ppn]
for respo in response:
respo.link = "SWB"
with LehmannsClient() as client:
results = client.search_by_title(title, strict=True)
client.enrich_pages(results)
if not results:
continue
for res in results:
response.append(BookData().from_LehmannsSearchResult(res))
if response == []:
continue
# check results if lehmanns has a result with the same isbn from the results of swb. if so, if we have a signature, remove, else keep
response = filter_prefer_swb(response)
result = (book, response)
self.results.append(result)
class ProgressDialog(QDialog):
def __init__(self, items: list):
super().__init__()
self.setWindowTitle("Progress")
self.setModal(True)
layout = QVBoxLayout(self)
self.progress_bar = QtqdmProgressBar(self)
self.items: List[BookData] = items
layout.addWidget(self.progress_bar)
self.results: List[tuple[BookData, List[BookData]]] = []
self.start_button = QPushButton("Start Progress", self)
self.start_button.hide()
self.start_button.clicked.connect(self.start)
layout.addWidget(self.start_button)
def start(self):
# Start logic is managed externally; keep method for UI wiring
pass

View File

@@ -1,10 +1,12 @@
from PySide6 import QtCore, QtGui, QtWidgets
from src import Icon, settings
from .dialog_sources.settings_ui import Ui_Dialog as _settings
from src.ui.widgets.iconLine import IconWidget
import loguru
import sys import sys
from src import LOG_DIR
import loguru
from PySide6 import QtCore, QtGui, QtWidgets
from src import LOG_DIR, Icon, settings
from src.ui.widgets.iconLine import IconWidget
from .dialog_sources.settings_ui import Ui_Dialog as _settings
log = loguru.logger log = loguru.logger
log.remove() log.remove()
@@ -12,7 +14,6 @@ log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
base = """'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> base = """'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style
@@ -220,7 +221,7 @@ class Settings(QtWidgets.QDialog, _settings):
for color in self.get_colors(): for color in self.get_colors():
settings.icons.colors[color] = self.get_colors()[color] settings.icons.colors[color] = self.get_colors()[color]
# print(color) # #print(color)
for icon in self.get_icons(): for icon in self.get_icons():
settings.icons.icons[icon] = self.get_icons()[icon] settings.icons.icons[icon] = self.get_icons()[icon]
@@ -245,7 +246,7 @@ class Settings(QtWidgets.QDialog, _settings):
def save(self): def save(self):
config = self.return_data() config = self.return_data()
# #print(config) # ##print(config)
config.save() config.save()
self.accept() self.accept()

View File

@@ -54,6 +54,9 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<widget class="QWidget" name="verticalLayoutWidget"> <widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
@@ -68,6 +71,9 @@
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>3</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="focusPolicy"> <property name="focusPolicy">
@@ -244,133 +250,25 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>180</y> <y>180</y>
<width>1261</width> <width>1412</width>
<height>511</height> <height>531</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="0"> <item row="11" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<spacer name="horizontalSpacer"> <widget class="QPushButton" name="add_medium">
<property name="orientation"> <property name="focusPolicy">
<enum>Qt::Horizontal</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="chkbx_show_del_media">
<property name="text"> <property name="text">
<string>gel. Medien anzeigen</string> <string>Medien hinzufügen</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_3"> <spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_reserve">
<property name="text">
<string>im Apparat?</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="add_layout">
<item>
<widget class="QLabel" name="label_info">
<property name="text">
<string>Medien werden hinzugefügt</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="progress_label">
<property name="text">
<string>Medium x/y</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="avail_layout"/>
</item>
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>Medien werden geprüft</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="avail_status">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="automation_add_selected_books">
<property name="text">
<string>Ausgewählte als verfügbar markieren</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
@@ -384,87 +282,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="9" column="0">
<widget class="QTableWidget" name="tableWidget_apparat_media">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1259</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>Buchtitel</string>
</property>
<property name="toolTip">
<string>Es kann sein, dass der Buchtitel leer ist, dies kommt vor, wenn der Titel nicht passend formatiert ist</string>
</property>
</column>
<column>
<property name="text">
<string>Signatur</string>
</property>
</column>
<column>
<property name="text">
<string>Auflage</string>
</property>
</column>
<column>
<property name="text">
<string>Autor</string>
</property>
</column>
<column>
<property name="text">
<string>im Apparat?</string>
</property>
<property name="toolTip">
<string>Diese Angabe ist nicht zuverlässig. Ist das ❌ vorhanden, kann das Medium im Apparat sein, aber aufgrund eines Bugs nicht gefunden worden</string>
</property>
</column>
<column>
<property name="text">
<string>Vorgemerkt</string>
</property>
</column>
<column>
<property name="text">
<string>Link</string>
</property>
</column>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="font"> <property name="font">
@@ -545,7 +362,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="dragDropMode"> <property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum> <enum>QAbstractItemView::NoDragDrop</enum>
</property> </property>
<property name="defaultDropAction"> <property name="defaultDropAction">
<enum>Qt::LinkAction</enum> <enum>Qt::LinkAction</enum>
@@ -1058,23 +875,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="2" column="0">
<widget class="QLineEdit" name="prof_id_adis">
<property name="font">
<font>
<pointsize>9</pointsize>
<bold>false</bold>
</font>
</property>
<property name="inputMethodHints">
<set>Qt::ImhPreferNumbers</set>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13"> <widget class="QLabel" name="label_13">
<property name="font"> <property name="font">
<font> <font>
@@ -1090,7 +891,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="1">
<widget class="QLineEdit" name="apparat_id_adis"> <widget class="QLineEdit" name="apparat_id_adis">
<property name="font"> <property name="font">
<font> <font>
@@ -1103,6 +904,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<widget class="QLineEdit" name="prof_id_adis">
<property name="font">
<font>
<pointsize>9</pointsize>
<bold>false</bold>
</font>
</property>
<property name="inputMethodHints">
<set>Qt::ImhPreferNumbers</set>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QLineEdit" name="sem_year"> <widget class="QLineEdit" name="sem_year">
@@ -1532,7 +1349,7 @@
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Die Apparatsdetails werden aus dem Dokument gelesen und eingetragen <string>Die Apparatsdetails werden aus dem Dokument gelesen und eingetragen
Einige Angaben müssen ggf angepasst werden</string> Die gewünschten Medien werden automatisch in die Medienliste eingetragen, evtl. unvollständig, da eBooks nicht erfasst werden könnenEinige Angaben müssen ggf angepasst werden</string>
</property> </property>
<property name="text"> <property name="text">
<string>Daten aus Dokument <string>Daten aus Dokument
@@ -1544,24 +1361,248 @@ Einige Angaben müssen ggf angepasst werden</string>
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="chkbx_show_only_wit_neweditions">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>WIP - Broken</string>
</property>
<property name="text">
<string>Nur Titel mit Neuauflagen anzeigen</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_reserve">
<property name="toolTip">
<string>Dieser Knopf prüft alle Werke, die mit einem roten X vermerkt sind. Sollten diese inzwischen im Apparat sein, wird dies aktualisiert</string>
</property>
<property name="text">
<string>Medien mit ❌ im Apparat?</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="add_layout">
<item>
<widget class="QLabel" name="label_info">
<property name="text">
<string>Medien werden hinzugefügt</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="progress_label">
<property name="text">
<string>Medium x/y</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="avail_layout">
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>Medien werden geprüft</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_eta">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="avail_status">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="automation_add_selected_books">
<property name="text">
<string>Ausgewählte als verfügbar markieren</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QTableWidget" name="tableWidget_apparat_media">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1259</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>Buchtitel</string>
</property>
<property name="toolTip">
<string>Es kann sein, dass der Buchtitel leer ist, dies kommt vor, wenn der Titel nicht passend formatiert ist</string>
</property>
</column>
<column>
<property name="text">
<string>Signatur</string>
</property>
</column>
<column>
<property name="text">
<string>Auflage</string>
</property>
</column>
<column>
<property name="text">
<string>Autor</string>
</property>
</column>
<column>
<property name="text">
<string>im Apparat?</string>
</property>
<property name="toolTip">
<string>Diese Angabe ist nicht zuverlässig. Ist das ❌ vorhanden, kann das Medium im Apparat sein, aber aufgrund eines Bugs nicht gefunden worden</string>
</property>
</column>
<column>
<property name="text">
<string>Vorgemerkt</string>
</property>
</column>
<column>
<property name="text">
<string>Link</string>
</property>
</column>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QPushButton" name="add_medium">
<property name="geometry">
<rect>
<x>3</x>
<y>695</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Medien hinzufügen</string>
</property>
</widget>
</widget> </widget>
<widget class="QWidget" name="search_statistics"> <widget class="QWidget" name="search_statistics">
<attribute name="title"> <attribute name="title">
@@ -1577,67 +1618,72 @@ Einige Angaben müssen ggf angepasst werden</string>
<attribute name="title"> <attribute name="title">
<string>Admin</string> <string>Admin</string>
</attribute> </attribute>
<widget class="QLabel" name="label_21"> <widget class="QFrame" name="frame">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>0</x>
<y>30</y> <y>0</y>
<width>47</width> <width>1251</width>
<height>22</height> <height>711</height>
</rect> </rect>
</property> </property>
<property name="text"> <property name="frameShape">
<string>Aktion:</string> <enum>QFrame::StyledPanel</enum>
</property> </property>
</widget> <property name="frameShadow">
<widget class="QComboBox" name="select_action_box"> <enum>QFrame::Raised</enum>
<property name="geometry">
<rect>
<x>60</x>
<y>30</y>
<width>181</width>
<height>22</height>
</rect>
</property>
<item>
<property name="text">
<string>Nutzer anlegen</string>
</property>
</item>
<item>
<property name="text">
<string>Nutzer bearbeiten</string>
</property>
</item>
<item>
<property name="text">
<string>Lehrperson bearbeiten</string>
</property>
</item>
</widget>
<widget class="QGroupBox" name="admin_action">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>570</width>
<height>291</height>
</rect>
</property>
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<property name="title">
<string>GroupBox</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property> </property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Aktion:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="select_action_box">
<item>
<property name="text">
<string>Nutzer anlegen</string>
</property>
</item>
<item>
<property name="text">
<string>Nutzer bearbeiten</string>
</property>
</item>
<item>
<property name="text">
<string>Lehrperson bearbeiten</string>
</property>
</item>
<item>
<property name="text">
<string>Medien bearbeiten</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="admin_action">
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<property name="title">
<string>GroupBox</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</widget> </widget>
</widget> </widget>
@@ -1918,6 +1964,7 @@ Einige Angaben müssen ggf angepasst werden</string>
<string>Bearbeiten</string> <string>Bearbeiten</string>
</property> </property>
<addaction name="actionEinstellungen"/> <addaction name="actionEinstellungen"/>
<addaction name="actionMedien_loeschen"/>
</widget> </widget>
<widget class="QMenu" name="menuHelp"> <widget class="QMenu" name="menuHelp">
<property name="title"> <property name="title">
@@ -1972,6 +2019,11 @@ Einige Angaben müssen ggf angepasst werden</string>
<string>F1</string> <string>F1</string>
</property> </property>
</action> </action>
<action name="actionMedien_loeschen">
<property name="text">
<string>Medien löschen</string>
</property>
</action>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>drpdwn_app_nr</tabstop> <tabstop>drpdwn_app_nr</tabstop>
@@ -1990,10 +2042,8 @@ Einige Angaben müssen ggf angepasst werden</string>
<tabstop>check_send_mail</tabstop> <tabstop>check_send_mail</tabstop>
<tabstop>btn_apparat_save</tabstop> <tabstop>btn_apparat_save</tabstop>
<tabstop>btn_apparat_apply</tabstop> <tabstop>btn_apparat_apply</tabstop>
<tabstop>chkbx_show_del_media</tabstop>
<tabstop>btn_reserve</tabstop> <tabstop>btn_reserve</tabstop>
<tabstop>select_action_box</tabstop> <tabstop>select_action_box</tabstop>
<tabstop>prof_id_adis</tabstop>
<tabstop>apparat_id_adis</tabstop> <tabstop>apparat_id_adis</tabstop>
<tabstop>automation_add_selected_books</tabstop> <tabstop>automation_add_selected_books</tabstop>
<tabstop>saveandcreate</tabstop> <tabstop>saveandcreate</tabstop>

View File

@@ -3,7 +3,7 @@
################################################################################ ################################################################################
## Form generated from reading UI file 'semesterapparat_ui.ui' ## Form generated from reading UI file 'semesterapparat_ui.ui'
## ##
## Created by: Qt User Interface Compiler version 6.9.1 ## Created by: Qt User Interface Compiler version 6.9.2
## ##
## WARNING! All changes made in this file will be lost when recompiling UI file! ## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################ ################################################################################
@@ -20,9 +20,9 @@ from PySide6.QtWidgets import (QAbstractItemView, QAbstractScrollArea, QApplicat
QComboBox, QFormLayout, QFrame, QGridLayout, QComboBox, QFormLayout, QFrame, QGridLayout,
QGroupBox, QHBoxLayout, QHeaderView, QLabel, QGroupBox, QHBoxLayout, QHeaderView, QLabel,
QLineEdit, QMainWindow, QMenu, QMenuBar, QLineEdit, QMainWindow, QMenu, QMenuBar,
QPushButton, QSizePolicy, QSpacerItem, QStatusBar, QProgressBar, QPushButton, QSizePolicy, QSpacerItem,
QTabWidget, QTableWidget, QTableWidgetItem, QToolButton, QStatusBar, QTabWidget, QTableWidget, QTableWidgetItem,
QVBoxLayout, QWidget) QToolButton, QVBoxLayout, QWidget)
class Ui_MainWindow(object): class Ui_MainWindow(object):
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
@@ -54,10 +54,13 @@ class Ui_MainWindow(object):
self.actionAbout.setMenuRole(QAction.AboutRole) self.actionAbout.setMenuRole(QAction.AboutRole)
self.actionDokumentation = QAction(MainWindow) self.actionDokumentation = QAction(MainWindow)
self.actionDokumentation.setObjectName(u"actionDokumentation") self.actionDokumentation.setObjectName(u"actionDokumentation")
self.actionMedien_loeschen = QAction(MainWindow)
self.actionMedien_loeschen.setObjectName(u"actionMedien_loeschen")
self.centralwidget = QWidget(MainWindow) self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget") self.centralwidget.setObjectName(u"centralwidget")
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy) self.centralwidget.setSizePolicy(sizePolicy)
self.centralwidget.setAcceptDrops(True)
self.verticalLayoutWidget = QWidget(self.centralwidget) self.verticalLayoutWidget = QWidget(self.centralwidget)
self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget") self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget")
self.verticalLayoutWidget.setGeometry(QRect(0, 0, 1271, 751)) self.verticalLayoutWidget.setGeometry(QRect(0, 0, 1271, 751))
@@ -68,6 +71,7 @@ class Ui_MainWindow(object):
self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setObjectName(u"horizontalLayout")
self.gridLayout = QGridLayout() self.gridLayout = QGridLayout()
self.gridLayout.setObjectName(u"gridLayout") self.gridLayout.setObjectName(u"gridLayout")
self.gridLayout.setContentsMargins(3, -1, -1, -1)
self.tabWidget = QTabWidget(self.verticalLayoutWidget) self.tabWidget = QTabWidget(self.verticalLayoutWidget)
self.tabWidget.setObjectName(u"tabWidget") self.tabWidget.setObjectName(u"tabWidget")
self.tabWidget.setFocusPolicy(Qt.NoFocus) self.tabWidget.setFocusPolicy(Qt.NoFocus)
@@ -155,124 +159,24 @@ class Ui_MainWindow(object):
self.gridLayoutWidget_2 = QWidget(self.createApparat) self.gridLayoutWidget_2 = QWidget(self.createApparat)
self.gridLayoutWidget_2.setObjectName(u"gridLayoutWidget_2") self.gridLayoutWidget_2.setObjectName(u"gridLayoutWidget_2")
self.gridLayoutWidget_2.setEnabled(True) self.gridLayoutWidget_2.setEnabled(True)
self.gridLayoutWidget_2.setGeometry(QRect(0, 180, 1261, 511)) self.gridLayoutWidget_2.setGeometry(QRect(0, 180, 1412, 531))
self.gridLayout_2 = QGridLayout(self.gridLayoutWidget_2) self.gridLayout_2 = QGridLayout(self.gridLayoutWidget_2)
self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_5 = QHBoxLayout() self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.horizontalSpacer = QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) self.add_medium = QPushButton(self.gridLayoutWidget_2)
self.add_medium.setObjectName(u"add_medium")
self.add_medium.setFocusPolicy(Qt.NoFocus)
self.horizontalLayout_5.addItem(self.horizontalSpacer) self.horizontalLayout_3.addWidget(self.add_medium)
self.chkbx_show_del_media = QCheckBox(self.gridLayoutWidget_2) self.horizontalSpacer_5 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.chkbx_show_del_media.setObjectName(u"chkbx_show_del_media")
self.horizontalLayout_5.addWidget(self.chkbx_show_del_media) self.horizontalLayout_3.addItem(self.horizontalSpacer_5)
self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_3)
self.btn_reserve = QPushButton(self.gridLayoutWidget_2)
self.btn_reserve.setObjectName(u"btn_reserve")
self.horizontalLayout_5.addWidget(self.btn_reserve)
self.add_layout = QHBoxLayout()
self.add_layout.setObjectName(u"add_layout")
self.label_info = QLabel(self.gridLayoutWidget_2)
self.label_info.setObjectName(u"label_info")
self.add_layout.addWidget(self.label_info)
self.line_2 = QFrame(self.gridLayoutWidget_2)
self.line_2.setObjectName(u"line_2")
self.line_2.setFrameShape(QFrame.Shape.VLine)
self.line_2.setFrameShadow(QFrame.Shadow.Sunken)
self.add_layout.addWidget(self.line_2)
self.progress_label = QLabel(self.gridLayoutWidget_2)
self.progress_label.setObjectName(u"progress_label")
self.add_layout.addWidget(self.progress_label)
self.horizontalLayout_5.addLayout(self.add_layout) self.gridLayout_2.addLayout(self.horizontalLayout_3, 11, 0, 1, 1)
self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_4)
self.avail_layout = QHBoxLayout()
self.avail_layout.setObjectName(u"avail_layout")
self.horizontalLayout_5.addLayout(self.avail_layout)
self.label_20 = QLabel(self.gridLayoutWidget_2)
self.label_20.setObjectName(u"label_20")
self.horizontalLayout_5.addWidget(self.label_20)
self.line_3 = QFrame(self.gridLayoutWidget_2)
self.line_3.setObjectName(u"line_3")
self.line_3.setFrameShape(QFrame.Shape.VLine)
self.line_3.setFrameShadow(QFrame.Shadow.Sunken)
self.horizontalLayout_5.addWidget(self.line_3)
self.avail_status = QLabel(self.gridLayoutWidget_2)
self.avail_status.setObjectName(u"avail_status")
self.horizontalLayout_5.addWidget(self.avail_status)
self.automation_add_selected_books = QPushButton(self.gridLayoutWidget_2)
self.automation_add_selected_books.setObjectName(u"automation_add_selected_books")
self.horizontalLayout_5.addWidget(self.automation_add_selected_books)
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_2)
self.gridLayout_2.addLayout(self.horizontalLayout_5, 4, 0, 1, 1)
self.tableWidget_apparat_media = QTableWidget(self.gridLayoutWidget_2)
if (self.tableWidget_apparat_media.columnCount() < 7):
self.tableWidget_apparat_media.setColumnCount(7)
__qtablewidgetitem6 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(0, __qtablewidgetitem6)
__qtablewidgetitem7 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(1, __qtablewidgetitem7)
__qtablewidgetitem8 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(2, __qtablewidgetitem8)
__qtablewidgetitem9 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(3, __qtablewidgetitem9)
__qtablewidgetitem10 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(4, __qtablewidgetitem10)
__qtablewidgetitem11 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(5, __qtablewidgetitem11)
__qtablewidgetitem12 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(6, __qtablewidgetitem12)
self.tableWidget_apparat_media.setObjectName(u"tableWidget_apparat_media")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.tableWidget_apparat_media.sizePolicy().hasHeightForWidth())
self.tableWidget_apparat_media.setSizePolicy(sizePolicy2)
self.tableWidget_apparat_media.setMinimumSize(QSize(1259, 0))
self.tableWidget_apparat_media.setFocusPolicy(Qt.NoFocus)
self.tableWidget_apparat_media.setContextMenuPolicy(Qt.CustomContextMenu)
self.tableWidget_apparat_media.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.tableWidget_apparat_media.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.tableWidget_apparat_media.setAlternatingRowColors(True)
self.tableWidget_apparat_media.setSelectionBehavior(QAbstractItemView.SelectRows)
self.tableWidget_apparat_media.setSortingEnabled(True)
self.tableWidget_apparat_media.horizontalHeader().setCascadingSectionResizes(True)
self.gridLayout_2.addWidget(self.tableWidget_apparat_media, 9, 0, 1, 1)
self.label = QLabel(self.gridLayoutWidget_2) self.label = QLabel(self.gridLayoutWidget_2)
self.label.setObjectName(u"label") self.label.setObjectName(u"label")
@@ -286,11 +190,11 @@ class Ui_MainWindow(object):
self.app_group_box = QGroupBox(self.gridLayoutWidget_2) self.app_group_box = QGroupBox(self.gridLayoutWidget_2)
self.app_group_box.setObjectName(u"app_group_box") self.app_group_box.setObjectName(u"app_group_box")
self.app_group_box.setEnabled(True) self.app_group_box.setEnabled(True)
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy3.setHorizontalStretch(0) sizePolicy2.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0) sizePolicy2.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.app_group_box.sizePolicy().hasHeightForWidth()) sizePolicy2.setHeightForWidth(self.app_group_box.sizePolicy().hasHeightForWidth())
self.app_group_box.setSizePolicy(sizePolicy3) self.app_group_box.setSizePolicy(sizePolicy2)
self.app_group_box.setMinimumSize(QSize(0, 210)) self.app_group_box.setMinimumSize(QSize(0, 210))
font1 = QFont() font1 = QFont()
font1.setPointSize(12) font1.setPointSize(12)
@@ -305,17 +209,17 @@ class Ui_MainWindow(object):
font2 = QFont() font2 = QFont()
font2.setFamilies([u"Arial"]) font2.setFamilies([u"Arial"])
font2.setPointSize(8) font2.setPointSize(8)
__qtablewidgetitem13 = QTableWidgetItem() __qtablewidgetitem6 = QTableWidgetItem()
__qtablewidgetitem13.setFont(font2); __qtablewidgetitem6.setFont(font2);
self.document_list.setHorizontalHeaderItem(0, __qtablewidgetitem13) self.document_list.setHorizontalHeaderItem(0, __qtablewidgetitem6)
__qtablewidgetitem14 = QTableWidgetItem() __qtablewidgetitem7 = QTableWidgetItem()
__qtablewidgetitem14.setFont(font2); __qtablewidgetitem7.setFont(font2);
self.document_list.setHorizontalHeaderItem(1, __qtablewidgetitem14) self.document_list.setHorizontalHeaderItem(1, __qtablewidgetitem7)
__qtablewidgetitem15 = QTableWidgetItem() __qtablewidgetitem8 = QTableWidgetItem()
__qtablewidgetitem15.setFont(font2); __qtablewidgetitem8.setFont(font2);
self.document_list.setHorizontalHeaderItem(2, __qtablewidgetitem15) self.document_list.setHorizontalHeaderItem(2, __qtablewidgetitem8)
__qtablewidgetitem16 = QTableWidgetItem() __qtablewidgetitem9 = QTableWidgetItem()
self.document_list.setHorizontalHeaderItem(3, __qtablewidgetitem16) self.document_list.setHorizontalHeaderItem(3, __qtablewidgetitem9)
self.document_list.setObjectName(u"document_list") self.document_list.setObjectName(u"document_list")
self.document_list.setGeometry(QRect(780, 20, 321, 181)) self.document_list.setGeometry(QRect(780, 20, 321, 181))
font3 = QFont() font3 = QFont()
@@ -328,7 +232,7 @@ class Ui_MainWindow(object):
self.document_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.document_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.document_list.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) self.document_list.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.document_list.setDragEnabled(True) self.document_list.setDragEnabled(True)
self.document_list.setDragDropMode(QAbstractItemView.DragOnly) self.document_list.setDragDropMode(QAbstractItemView.NoDragDrop)
self.document_list.setDefaultDropAction(Qt.LinkAction) self.document_list.setDefaultDropAction(Qt.LinkAction)
self.document_list.setSelectionMode(QAbstractItemView.SingleSelection) self.document_list.setSelectionMode(QAbstractItemView.SingleSelection)
self.document_list.horizontalHeader().setDefaultSectionSize(107) self.document_list.horizontalHeader().setDefaultSectionSize(107)
@@ -453,25 +357,25 @@ class Ui_MainWindow(object):
self.formLayout_3.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_12) self.formLayout_3.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_12)
self.prof_id_adis = QLineEdit(self.formLayoutWidget_2)
self.prof_id_adis.setObjectName(u"prof_id_adis")
self.prof_id_adis.setFont(font4)
self.prof_id_adis.setInputMethodHints(Qt.ImhPreferNumbers)
self.formLayout_3.setWidget(0, QFormLayout.ItemRole.FieldRole, self.prof_id_adis)
self.label_13 = QLabel(self.formLayoutWidget_2) self.label_13 = QLabel(self.formLayoutWidget_2)
self.label_13.setObjectName(u"label_13") self.label_13.setObjectName(u"label_13")
self.label_13.setFont(font4) self.label_13.setFont(font4)
self.formLayout_3.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_13) self.formLayout_3.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_13)
self.apparat_id_adis = QLineEdit(self.formLayoutWidget_2) self.apparat_id_adis = QLineEdit(self.formLayoutWidget_2)
self.apparat_id_adis.setObjectName(u"apparat_id_adis") self.apparat_id_adis.setObjectName(u"apparat_id_adis")
self.apparat_id_adis.setFont(font4) self.apparat_id_adis.setFont(font4)
self.apparat_id_adis.setInputMethodHints(Qt.ImhPreferNumbers) self.apparat_id_adis.setInputMethodHints(Qt.ImhPreferNumbers)
self.formLayout_3.setWidget(1, QFormLayout.ItemRole.FieldRole, self.apparat_id_adis) self.formLayout_3.setWidget(2, QFormLayout.ItemRole.FieldRole, self.apparat_id_adis)
self.prof_id_adis = QLineEdit(self.formLayoutWidget_2)
self.prof_id_adis.setObjectName(u"prof_id_adis")
self.prof_id_adis.setFont(font4)
self.prof_id_adis.setInputMethodHints(Qt.ImhPreferNumbers)
self.formLayout_3.setWidget(1, QFormLayout.ItemRole.FieldRole, self.prof_id_adis)
self.sem_year = QLineEdit(self.app_group_box) self.sem_year = QLineEdit(self.app_group_box)
self.sem_year.setObjectName(u"sem_year") self.sem_year.setObjectName(u"sem_year")
@@ -558,35 +462,35 @@ class Ui_MainWindow(object):
self.verticalLayout_8.setContentsMargins(0, 0, 0, 0) self.verticalLayout_8.setContentsMargins(0, 0, 0, 0)
self.btn_add_document = QPushButton(self.verticalLayoutWidget_3) self.btn_add_document = QPushButton(self.verticalLayoutWidget_3)
self.btn_add_document.setObjectName(u"btn_add_document") self.btn_add_document.setObjectName(u"btn_add_document")
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.MinimumExpanding) sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.MinimumExpanding)
sizePolicy4.setHorizontalStretch(0) sizePolicy3.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0) sizePolicy3.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.btn_add_document.sizePolicy().hasHeightForWidth()) sizePolicy3.setHeightForWidth(self.btn_add_document.sizePolicy().hasHeightForWidth())
self.btn_add_document.setSizePolicy(sizePolicy4) self.btn_add_document.setSizePolicy(sizePolicy3)
self.btn_add_document.setFont(font4) self.btn_add_document.setFont(font4)
self.verticalLayout_8.addWidget(self.btn_add_document) self.verticalLayout_8.addWidget(self.btn_add_document)
self.btn_open_document = QPushButton(self.verticalLayoutWidget_3) self.btn_open_document = QPushButton(self.verticalLayoutWidget_3)
self.btn_open_document.setObjectName(u"btn_open_document") self.btn_open_document.setObjectName(u"btn_open_document")
sizePolicy4.setHeightForWidth(self.btn_open_document.sizePolicy().hasHeightForWidth()) sizePolicy3.setHeightForWidth(self.btn_open_document.sizePolicy().hasHeightForWidth())
self.btn_open_document.setSizePolicy(sizePolicy4) self.btn_open_document.setSizePolicy(sizePolicy3)
self.btn_open_document.setFont(font4) self.btn_open_document.setFont(font4)
self.verticalLayout_8.addWidget(self.btn_open_document) self.verticalLayout_8.addWidget(self.btn_open_document)
self.check_file = QPushButton(self.verticalLayoutWidget_3) self.check_file = QPushButton(self.verticalLayoutWidget_3)
self.check_file.setObjectName(u"check_file") self.check_file.setObjectName(u"check_file")
sizePolicy4.setHeightForWidth(self.check_file.sizePolicy().hasHeightForWidth()) sizePolicy3.setHeightForWidth(self.check_file.sizePolicy().hasHeightForWidth())
self.check_file.setSizePolicy(sizePolicy4) self.check_file.setSizePolicy(sizePolicy3)
self.check_file.setFont(font4) self.check_file.setFont(font4)
self.verticalLayout_8.addWidget(self.check_file) self.verticalLayout_8.addWidget(self.check_file)
self.btn_extract_data_from_document = QPushButton(self.verticalLayoutWidget_3) self.btn_extract_data_from_document = QPushButton(self.verticalLayoutWidget_3)
self.btn_extract_data_from_document.setObjectName(u"btn_extract_data_from_document") self.btn_extract_data_from_document.setObjectName(u"btn_extract_data_from_document")
sizePolicy4.setHeightForWidth(self.btn_extract_data_from_document.sizePolicy().hasHeightForWidth()) sizePolicy3.setHeightForWidth(self.btn_extract_data_from_document.sizePolicy().hasHeightForWidth())
self.btn_extract_data_from_document.setSizePolicy(sizePolicy4) self.btn_extract_data_from_document.setSizePolicy(sizePolicy3)
self.btn_extract_data_from_document.setFont(font4) self.btn_extract_data_from_document.setFont(font4)
self.verticalLayout_8.addWidget(self.btn_extract_data_from_document) self.verticalLayout_8.addWidget(self.btn_extract_data_from_document)
@@ -598,10 +502,133 @@ class Ui_MainWindow(object):
self.gridLayout_2.addWidget(self.app_group_box, 1, 0, 1, 1) self.gridLayout_2.addWidget(self.app_group_box, 1, 0, 1, 1)
self.add_medium = QPushButton(self.createApparat) self.horizontalLayout_5 = QHBoxLayout()
self.add_medium.setObjectName(u"add_medium") self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
self.add_medium.setGeometry(QRect(3, 695, 121, 20)) self.horizontalSpacer = QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.add_medium.setFocusPolicy(Qt.NoFocus)
self.horizontalLayout_5.addItem(self.horizontalSpacer)
self.chkbx_show_only_wit_neweditions = QCheckBox(self.gridLayoutWidget_2)
self.chkbx_show_only_wit_neweditions.setObjectName(u"chkbx_show_only_wit_neweditions")
self.chkbx_show_only_wit_neweditions.setEnabled(False)
self.horizontalLayout_5.addWidget(self.chkbx_show_only_wit_neweditions)
self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_3)
self.btn_reserve = QPushButton(self.gridLayoutWidget_2)
self.btn_reserve.setObjectName(u"btn_reserve")
self.horizontalLayout_5.addWidget(self.btn_reserve)
self.add_layout = QHBoxLayout()
self.add_layout.setObjectName(u"add_layout")
self.label_info = QLabel(self.gridLayoutWidget_2)
self.label_info.setObjectName(u"label_info")
self.add_layout.addWidget(self.label_info)
self.line_2 = QFrame(self.gridLayoutWidget_2)
self.line_2.setObjectName(u"line_2")
self.line_2.setFrameShape(QFrame.Shape.VLine)
self.line_2.setFrameShadow(QFrame.Shadow.Sunken)
self.add_layout.addWidget(self.line_2)
self.progress_label = QLabel(self.gridLayoutWidget_2)
self.progress_label.setObjectName(u"progress_label")
self.add_layout.addWidget(self.progress_label)
self.horizontalLayout_5.addLayout(self.add_layout)
self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_4)
self.avail_layout = QHBoxLayout()
self.avail_layout.setObjectName(u"avail_layout")
self.label_20 = QLabel(self.gridLayoutWidget_2)
self.label_20.setObjectName(u"label_20")
self.avail_layout.addWidget(self.label_20)
self.progressBar = QProgressBar(self.gridLayoutWidget_2)
self.progressBar.setObjectName(u"progressBar")
self.progressBar.setValue(24)
self.avail_layout.addWidget(self.progressBar)
self.label_eta = QLabel(self.gridLayoutWidget_2)
self.label_eta.setObjectName(u"label_eta")
self.avail_layout.addWidget(self.label_eta)
self.horizontalLayout_5.addLayout(self.avail_layout)
self.line_3 = QFrame(self.gridLayoutWidget_2)
self.line_3.setObjectName(u"line_3")
self.line_3.setFrameShape(QFrame.Shape.VLine)
self.line_3.setFrameShadow(QFrame.Shadow.Sunken)
self.horizontalLayout_5.addWidget(self.line_3)
self.avail_status = QLabel(self.gridLayoutWidget_2)
self.avail_status.setObjectName(u"avail_status")
self.horizontalLayout_5.addWidget(self.avail_status)
self.automation_add_selected_books = QPushButton(self.gridLayoutWidget_2)
self.automation_add_selected_books.setObjectName(u"automation_add_selected_books")
self.horizontalLayout_5.addWidget(self.automation_add_selected_books)
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_2)
self.gridLayout_2.addLayout(self.horizontalLayout_5, 4, 0, 1, 1)
self.tableWidget_apparat_media = QTableWidget(self.gridLayoutWidget_2)
if (self.tableWidget_apparat_media.columnCount() < 7):
self.tableWidget_apparat_media.setColumnCount(7)
__qtablewidgetitem10 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(0, __qtablewidgetitem10)
__qtablewidgetitem11 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(1, __qtablewidgetitem11)
__qtablewidgetitem12 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(2, __qtablewidgetitem12)
__qtablewidgetitem13 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(3, __qtablewidgetitem13)
__qtablewidgetitem14 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(4, __qtablewidgetitem14)
__qtablewidgetitem15 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(5, __qtablewidgetitem15)
__qtablewidgetitem16 = QTableWidgetItem()
self.tableWidget_apparat_media.setHorizontalHeaderItem(6, __qtablewidgetitem16)
self.tableWidget_apparat_media.setObjectName(u"tableWidget_apparat_media")
sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.tableWidget_apparat_media.sizePolicy().hasHeightForWidth())
self.tableWidget_apparat_media.setSizePolicy(sizePolicy4)
self.tableWidget_apparat_media.setMinimumSize(QSize(1259, 0))
self.tableWidget_apparat_media.setFocusPolicy(Qt.NoFocus)
self.tableWidget_apparat_media.setContextMenuPolicy(Qt.CustomContextMenu)
self.tableWidget_apparat_media.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.tableWidget_apparat_media.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.tableWidget_apparat_media.setAlternatingRowColors(True)
self.tableWidget_apparat_media.setSelectionBehavior(QAbstractItemView.SelectRows)
self.tableWidget_apparat_media.setSortingEnabled(True)
self.tableWidget_apparat_media.horizontalHeader().setCascadingSectionResizes(True)
self.gridLayout_2.addWidget(self.tableWidget_apparat_media, 9, 0, 1, 1)
self.tabWidget.addTab(self.createApparat, "") self.tabWidget.addTab(self.createApparat, "")
self.search_statistics = QWidget() self.search_statistics = QWidget()
self.search_statistics.setObjectName(u"search_statistics") self.search_statistics.setObjectName(u"search_statistics")
@@ -611,23 +638,37 @@ class Ui_MainWindow(object):
self.tabWidget.addTab(self.elsatab, "") self.tabWidget.addTab(self.elsatab, "")
self.admin = QWidget() self.admin = QWidget()
self.admin.setObjectName(u"admin") self.admin.setObjectName(u"admin")
self.label_21 = QLabel(self.admin) self.frame = QFrame(self.admin)
self.frame.setObjectName(u"frame")
self.frame.setGeometry(QRect(0, 0, 1251, 711))
self.frame.setFrameShape(QFrame.StyledPanel)
self.frame.setFrameShadow(QFrame.Raised)
self.formLayout_2 = QFormLayout(self.frame)
self.formLayout_2.setObjectName(u"formLayout_2")
self.label_21 = QLabel(self.frame)
self.label_21.setObjectName(u"label_21") self.label_21.setObjectName(u"label_21")
self.label_21.setGeometry(QRect(10, 30, 47, 22))
self.select_action_box = QComboBox(self.admin) self.formLayout_2.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_21)
self.select_action_box = QComboBox(self.frame)
self.select_action_box.addItem("")
self.select_action_box.addItem("") self.select_action_box.addItem("")
self.select_action_box.addItem("") self.select_action_box.addItem("")
self.select_action_box.addItem("") self.select_action_box.addItem("")
self.select_action_box.setObjectName(u"select_action_box") self.select_action_box.setObjectName(u"select_action_box")
self.select_action_box.setGeometry(QRect(60, 30, 181, 22))
self.admin_action = QGroupBox(self.admin) self.formLayout_2.setWidget(0, QFormLayout.ItemRole.FieldRole, self.select_action_box)
self.admin_action = QGroupBox(self.frame)
self.admin_action.setObjectName(u"admin_action") self.admin_action.setObjectName(u"admin_action")
self.admin_action.setGeometry(QRect(10, 70, 570, 291))
font5 = QFont() font5 = QFont()
font5.setBold(False) font5.setBold(False)
self.admin_action.setFont(font5) self.admin_action.setFont(font5)
self.admin_action.setFlat(True) self.admin_action.setFlat(True)
self.admin_action.setCheckable(False) self.admin_action.setCheckable(False)
self.formLayout_2.setWidget(1, QFormLayout.ItemRole.FieldRole, self.admin_action)
self.tabWidget.addTab(self.admin, "") self.tabWidget.addTab(self.admin, "")
self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
@@ -806,11 +847,9 @@ class Ui_MainWindow(object):
QWidget.setTabOrder(self.check_file, self.check_send_mail) QWidget.setTabOrder(self.check_file, self.check_send_mail)
QWidget.setTabOrder(self.check_send_mail, self.btn_apparat_save) QWidget.setTabOrder(self.check_send_mail, self.btn_apparat_save)
QWidget.setTabOrder(self.btn_apparat_save, self.btn_apparat_apply) QWidget.setTabOrder(self.btn_apparat_save, self.btn_apparat_apply)
QWidget.setTabOrder(self.btn_apparat_apply, self.chkbx_show_del_media) QWidget.setTabOrder(self.btn_apparat_apply, self.btn_reserve)
QWidget.setTabOrder(self.chkbx_show_del_media, self.btn_reserve)
QWidget.setTabOrder(self.btn_reserve, self.select_action_box) QWidget.setTabOrder(self.btn_reserve, self.select_action_box)
QWidget.setTabOrder(self.select_action_box, self.prof_id_adis) QWidget.setTabOrder(self.select_action_box, self.apparat_id_adis)
QWidget.setTabOrder(self.prof_id_adis, self.apparat_id_adis)
QWidget.setTabOrder(self.apparat_id_adis, self.automation_add_selected_books) QWidget.setTabOrder(self.apparat_id_adis, self.automation_add_selected_books)
QWidget.setTabOrder(self.automation_add_selected_books, self.saveandcreate) QWidget.setTabOrder(self.automation_add_selected_books, self.saveandcreate)
@@ -819,6 +858,7 @@ class Ui_MainWindow(object):
self.menubar.addAction(self.menuHelp.menuAction()) self.menubar.addAction(self.menuHelp.menuAction())
self.menuDatei.addAction(self.actionBeenden) self.menuDatei.addAction(self.actionBeenden)
self.menuEinstellungen.addAction(self.actionEinstellungen) self.menuEinstellungen.addAction(self.actionEinstellungen)
self.menuEinstellungen.addAction(self.actionMedien_loeschen)
self.menuHelp.addAction(self.actionAbout) self.menuHelp.addAction(self.actionAbout)
self.menuHelp.addAction(self.actionDokumentation) self.menuHelp.addAction(self.actionDokumentation)
@@ -848,6 +888,7 @@ class Ui_MainWindow(object):
#if QT_CONFIG(shortcut) #if QT_CONFIG(shortcut)
self.actionDokumentation.setShortcut(QCoreApplication.translate("MainWindow", u"F1", None)) self.actionDokumentation.setShortcut(QCoreApplication.translate("MainWindow", u"F1", None))
#endif // QT_CONFIG(shortcut) #endif // QT_CONFIG(shortcut)
self.actionMedien_loeschen.setText(QCoreApplication.translate("MainWindow", u"Medien l\u00f6schen", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.create_document.setToolTip(QCoreApplication.translate("MainWindow", u"Erstellt die \u00dcbersicht, welche am Regal ausgeh\u00e4ngt werden kann", None)) self.create_document.setToolTip(QCoreApplication.translate("MainWindow", u"Erstellt die \u00dcbersicht, welche am Regal ausgeh\u00e4ngt werden kann", None))
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
@@ -866,43 +907,17 @@ class Ui_MainWindow(object):
___qtablewidgetitem4.setText(QCoreApplication.translate("MainWindow", u"Dauerapparat", None)); ___qtablewidgetitem4.setText(QCoreApplication.translate("MainWindow", u"Dauerapparat", None));
___qtablewidgetitem5 = self.tableWidget_apparate.horizontalHeaderItem(5) ___qtablewidgetitem5 = self.tableWidget_apparate.horizontalHeaderItem(5)
___qtablewidgetitem5.setText(QCoreApplication.translate("MainWindow", u"KontoNr", None)); ___qtablewidgetitem5.setText(QCoreApplication.translate("MainWindow", u"KontoNr", None));
self.chkbx_show_del_media.setText(QCoreApplication.translate("MainWindow", u"gel. Medien anzeigen", None)) self.add_medium.setText(QCoreApplication.translate("MainWindow", u"Medien hinzuf\u00fcgen", None))
self.btn_reserve.setText(QCoreApplication.translate("MainWindow", u"im Apparat?", None))
self.label_info.setText(QCoreApplication.translate("MainWindow", u"Medien werden hinzugef\u00fcgt", None))
self.progress_label.setText(QCoreApplication.translate("MainWindow", u"Medium x/y", None))
self.label_20.setText(QCoreApplication.translate("MainWindow", u"Medien werden gepr\u00fcft", None))
self.avail_status.setText(QCoreApplication.translate("MainWindow", u"TextLabel", None))
self.automation_add_selected_books.setText(QCoreApplication.translate("MainWindow", u"Ausgew\u00e4hlte als verf\u00fcgbar markieren", None))
___qtablewidgetitem6 = self.tableWidget_apparat_media.horizontalHeaderItem(0)
___qtablewidgetitem6.setText(QCoreApplication.translate("MainWindow", u"Buchtitel", None));
#if QT_CONFIG(tooltip)
___qtablewidgetitem6.setToolTip(QCoreApplication.translate("MainWindow", u"Es kann sein, dass der Buchtitel leer ist, dies kommt vor, wenn der Titel nicht passend formatiert ist", None));
#endif // QT_CONFIG(tooltip)
___qtablewidgetitem7 = self.tableWidget_apparat_media.horizontalHeaderItem(1)
___qtablewidgetitem7.setText(QCoreApplication.translate("MainWindow", u"Signatur", None));
___qtablewidgetitem8 = self.tableWidget_apparat_media.horizontalHeaderItem(2)
___qtablewidgetitem8.setText(QCoreApplication.translate("MainWindow", u"Auflage", None));
___qtablewidgetitem9 = self.tableWidget_apparat_media.horizontalHeaderItem(3)
___qtablewidgetitem9.setText(QCoreApplication.translate("MainWindow", u"Autor", None));
___qtablewidgetitem10 = self.tableWidget_apparat_media.horizontalHeaderItem(4)
___qtablewidgetitem10.setText(QCoreApplication.translate("MainWindow", u"im Apparat?", None));
#if QT_CONFIG(tooltip)
___qtablewidgetitem10.setToolTip(QCoreApplication.translate("MainWindow", u"Diese Angabe ist nicht zuverl\u00e4ssig. Ist das \u274c vorhanden, kann das Medium im Apparat sein, aber aufgrund eines Bugs nicht gefunden worden", None));
#endif // QT_CONFIG(tooltip)
___qtablewidgetitem11 = self.tableWidget_apparat_media.horizontalHeaderItem(5)
___qtablewidgetitem11.setText(QCoreApplication.translate("MainWindow", u"Vorgemerkt", None));
___qtablewidgetitem12 = self.tableWidget_apparat_media.horizontalHeaderItem(6)
___qtablewidgetitem12.setText(QCoreApplication.translate("MainWindow", u"Link", None));
self.label.setText(QCoreApplication.translate("MainWindow", u" Medienliste", None)) self.label.setText(QCoreApplication.translate("MainWindow", u" Medienliste", None))
self.app_group_box.setTitle(QCoreApplication.translate("MainWindow", u"SemesterApparatsdetails", None)) self.app_group_box.setTitle(QCoreApplication.translate("MainWindow", u"SemesterApparatsdetails", None))
___qtablewidgetitem13 = self.document_list.horizontalHeaderItem(0) ___qtablewidgetitem6 = self.document_list.horizontalHeaderItem(0)
___qtablewidgetitem13.setText(QCoreApplication.translate("MainWindow", u"Dokumentname", None)); ___qtablewidgetitem6.setText(QCoreApplication.translate("MainWindow", u"Dokumentname", None));
___qtablewidgetitem14 = self.document_list.horizontalHeaderItem(1) ___qtablewidgetitem7 = self.document_list.horizontalHeaderItem(1)
___qtablewidgetitem14.setText(QCoreApplication.translate("MainWindow", u"Dateityp", None)); ___qtablewidgetitem7.setText(QCoreApplication.translate("MainWindow", u"Dateityp", None));
___qtablewidgetitem15 = self.document_list.horizontalHeaderItem(2) ___qtablewidgetitem8 = self.document_list.horizontalHeaderItem(2)
___qtablewidgetitem15.setText(QCoreApplication.translate("MainWindow", u"Neu?", None)); ___qtablewidgetitem8.setText(QCoreApplication.translate("MainWindow", u"Neu?", None));
___qtablewidgetitem16 = self.document_list.horizontalHeaderItem(3) ___qtablewidgetitem9 = self.document_list.horizontalHeaderItem(3)
___qtablewidgetitem16.setText(QCoreApplication.translate("MainWindow", u"path", None)); ___qtablewidgetitem9.setText(QCoreApplication.translate("MainWindow", u"path", None));
self.appname_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) self.appname_mand.setText(QCoreApplication.translate("MainWindow", u"*", None))
self.profname_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) self.profname_mand.setText(QCoreApplication.translate("MainWindow", u"*", None))
self.fach_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) self.fach_mand.setText(QCoreApplication.translate("MainWindow", u"*", None))
@@ -927,8 +942,8 @@ class Ui_MainWindow(object):
self.label_10.setText(QCoreApplication.translate("MainWindow", u"Fach", None)) self.label_10.setText(QCoreApplication.translate("MainWindow", u"Fach", None))
self.prof_mail.setPlaceholderText("") self.prof_mail.setPlaceholderText("")
self.label_12.setText(QCoreApplication.translate("MainWindow", u"Prof-ID-aDIS", None)) self.label_12.setText(QCoreApplication.translate("MainWindow", u"Prof-ID-aDIS", None))
self.prof_id_adis.setText("")
self.label_13.setText(QCoreApplication.translate("MainWindow", u"Apparat-ID-aDIS", None)) self.label_13.setText(QCoreApplication.translate("MainWindow", u"Apparat-ID-aDIS", None))
self.prof_id_adis.setText("")
self.sem_year.setPlaceholderText(QCoreApplication.translate("MainWindow", u"2023", None)) self.sem_year.setPlaceholderText(QCoreApplication.translate("MainWindow", u"2023", None))
self.check_send_mail.setText(QCoreApplication.translate("MainWindow", u"Mail senden", None)) self.check_send_mail.setText(QCoreApplication.translate("MainWindow", u"Mail senden", None))
self.sem_winter.setText(QCoreApplication.translate("MainWindow", u"Winter", None)) self.sem_winter.setText(QCoreApplication.translate("MainWindow", u"Winter", None))
@@ -961,11 +976,44 @@ class Ui_MainWindow(object):
" hinzuf\u00fcgen", None)) " hinzuf\u00fcgen", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.btn_extract_data_from_document.setToolTip(QCoreApplication.translate("MainWindow", u"Die Apparatsdetails werden aus dem Dokument gelesen und eingetragen\n" self.btn_extract_data_from_document.setToolTip(QCoreApplication.translate("MainWindow", u"Die Apparatsdetails werden aus dem Dokument gelesen und eingetragen\n"
"Einige Angaben m\u00fcssen ggf angepasst werden", None)) "Die gew\u00fcnschten Medien werden automatisch in die Medienliste eingetragen, evtl. unvollst\u00e4ndig, da eBooks nicht erfasst werden k\u00f6nnenEinige Angaben m\u00fcssen ggf angepasst werden", None))
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
self.btn_extract_data_from_document.setText(QCoreApplication.translate("MainWindow", u"Daten aus Dokument\n" self.btn_extract_data_from_document.setText(QCoreApplication.translate("MainWindow", u"Daten aus Dokument\n"
"\u00fcbernehmen", None)) "\u00fcbernehmen", None))
self.add_medium.setText(QCoreApplication.translate("MainWindow", u"Medien hinzuf\u00fcgen", None)) #if QT_CONFIG(tooltip)
self.chkbx_show_only_wit_neweditions.setToolTip(QCoreApplication.translate("MainWindow", u"WIP - Broken", None))
#endif // QT_CONFIG(tooltip)
self.chkbx_show_only_wit_neweditions.setText(QCoreApplication.translate("MainWindow", u"Nur Titel mit Neuauflagen anzeigen", None))
#if QT_CONFIG(tooltip)
self.btn_reserve.setToolTip(QCoreApplication.translate("MainWindow", u"Dieser Knopf pr\u00fcft alle Werke, die mit einem roten X vermerkt sind. Sollten diese inzwischen im Apparat sein, wird dies aktualisiert", None))
#endif // QT_CONFIG(tooltip)
self.btn_reserve.setText(QCoreApplication.translate("MainWindow", u"Medien mit \u274c im Apparat?", None))
self.label_info.setText(QCoreApplication.translate("MainWindow", u"Medien werden hinzugef\u00fcgt", None))
self.progress_label.setText(QCoreApplication.translate("MainWindow", u"Medium x/y", None))
self.label_20.setText(QCoreApplication.translate("MainWindow", u"Medien werden gepr\u00fcft", None))
self.label_eta.setText("")
self.avail_status.setText(QCoreApplication.translate("MainWindow", u"TextLabel", None))
self.automation_add_selected_books.setText(QCoreApplication.translate("MainWindow", u"Ausgew\u00e4hlte als verf\u00fcgbar markieren", None))
___qtablewidgetitem10 = self.tableWidget_apparat_media.horizontalHeaderItem(0)
___qtablewidgetitem10.setText(QCoreApplication.translate("MainWindow", u"Buchtitel", None));
#if QT_CONFIG(tooltip)
___qtablewidgetitem10.setToolTip(QCoreApplication.translate("MainWindow", u"Es kann sein, dass der Buchtitel leer ist, dies kommt vor, wenn der Titel nicht passend formatiert ist", None));
#endif // QT_CONFIG(tooltip)
___qtablewidgetitem11 = self.tableWidget_apparat_media.horizontalHeaderItem(1)
___qtablewidgetitem11.setText(QCoreApplication.translate("MainWindow", u"Signatur", None));
___qtablewidgetitem12 = self.tableWidget_apparat_media.horizontalHeaderItem(2)
___qtablewidgetitem12.setText(QCoreApplication.translate("MainWindow", u"Auflage", None));
___qtablewidgetitem13 = self.tableWidget_apparat_media.horizontalHeaderItem(3)
___qtablewidgetitem13.setText(QCoreApplication.translate("MainWindow", u"Autor", None));
___qtablewidgetitem14 = self.tableWidget_apparat_media.horizontalHeaderItem(4)
___qtablewidgetitem14.setText(QCoreApplication.translate("MainWindow", u"im Apparat?", None));
#if QT_CONFIG(tooltip)
___qtablewidgetitem14.setToolTip(QCoreApplication.translate("MainWindow", u"Diese Angabe ist nicht zuverl\u00e4ssig. Ist das \u274c vorhanden, kann das Medium im Apparat sein, aber aufgrund eines Bugs nicht gefunden worden", None));
#endif // QT_CONFIG(tooltip)
___qtablewidgetitem15 = self.tableWidget_apparat_media.horizontalHeaderItem(5)
___qtablewidgetitem15.setText(QCoreApplication.translate("MainWindow", u"Vorgemerkt", None));
___qtablewidgetitem16 = self.tableWidget_apparat_media.horizontalHeaderItem(6)
___qtablewidgetitem16.setText(QCoreApplication.translate("MainWindow", u"Link", None));
self.tabWidget.setTabText(self.tabWidget.indexOf(self.createApparat), QCoreApplication.translate("MainWindow", u"Anlegen", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.createApparat), QCoreApplication.translate("MainWindow", u"Anlegen", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.search_statistics), QCoreApplication.translate("MainWindow", u"Suchen / Statistik", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.search_statistics), QCoreApplication.translate("MainWindow", u"Suchen / Statistik", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.elsatab), QCoreApplication.translate("MainWindow", u"ELSA", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.elsatab), QCoreApplication.translate("MainWindow", u"ELSA", None))
@@ -973,6 +1021,7 @@ class Ui_MainWindow(object):
self.select_action_box.setItemText(0, QCoreApplication.translate("MainWindow", u"Nutzer anlegen", None)) self.select_action_box.setItemText(0, QCoreApplication.translate("MainWindow", u"Nutzer anlegen", None))
self.select_action_box.setItemText(1, QCoreApplication.translate("MainWindow", u"Nutzer bearbeiten", None)) self.select_action_box.setItemText(1, QCoreApplication.translate("MainWindow", u"Nutzer bearbeiten", None))
self.select_action_box.setItemText(2, QCoreApplication.translate("MainWindow", u"Lehrperson bearbeiten", None)) self.select_action_box.setItemText(2, QCoreApplication.translate("MainWindow", u"Lehrperson bearbeiten", None))
self.select_action_box.setItemText(3, QCoreApplication.translate("MainWindow", u"Medien bearbeiten", None))
self.admin_action.setTitle(QCoreApplication.translate("MainWindow", u"GroupBox", None)) self.admin_action.setTitle(QCoreApplication.translate("MainWindow", u"GroupBox", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.admin), QCoreApplication.translate("MainWindow", u"Admin", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.admin), QCoreApplication.translate("MainWindow", u"Admin", None))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,21 +5,25 @@ import sys
import tempfile import tempfile
import time import time
import webbrowser import webbrowser
from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Any, Union from typing import Any, List, Optional, Tuple, Union
import loguru
from natsort import natsorted from natsort import natsorted
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import QThread, Qt from PySide6.QtCore import QThread
from PySide6.QtGui import QRegularExpressionValidator from PySide6.QtGui import QRegularExpressionValidator
from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
from src import LOG_DIR, Icon from src import Icon
from src.backend import AvailChecker, BookGrabber, Database, DocumentationThread from src.backend import (
AvailChecker,
BookGrabber,
Database,
DocumentationThread,
NewEditionCheckerThread,
)
from src.backend.create_file import recreateFile from src.backend.create_file import recreateFile
from src.backend.delete_temp_contents import delete_temp_contents as tempdelete from src.backend.delete_temp_contents import delete_temp_contents as tempdelete
from src.backend.semester import Semester
from src.logic import ( from src.logic import (
APP_NRS, APP_NRS,
Apparat, Apparat,
@@ -27,18 +31,24 @@ from src.logic import (
BookData, BookData,
Prof, Prof,
SemapDocument, SemapDocument,
Semester,
csv_to_list, csv_to_list,
eml_to_semap,
pdf_to_semap,
word_to_semap, word_to_semap,
) )
from src.shared.logging import log
from src.ui import Ui_Semesterapparat from src.ui import Ui_Semesterapparat
from src.ui.dialogs import ( from src.ui.dialogs import (
About, About,
ApparatExtendDialog, ApparatExtendDialog,
BookDataUI, BookDataUI,
DeleteDialog,
DocumentPrintDialog, DocumentPrintDialog,
LoginDialog, LoginDialog,
Mail_Dialog, Mail_Dialog,
MedienAdder, MedienAdder,
NewEditionDialog,
ParsedTitles, ParsedTitles,
ReminderDialog, ReminderDialog,
Settings, Settings,
@@ -51,25 +61,19 @@ from src.ui.widgets import (
ElsaDialog, ElsaDialog,
FilePicker, FilePicker,
MessageCalendar, MessageCalendar,
NewEditionChecker,
NewEditionCheckSelector,
SearchStatisticPage, SearchStatisticPage,
UpdaterThread,
UpdateSignatures,
UserCreate, UserCreate,
) )
log = loguru.logger log.success("UI started")
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
log.add(
f"{LOG_DIR}/{datetime.now().strftime('%Y-%m-%d')}.log",
rotation="1 day",
retention="1 month",
)
log.critical("UI started")
valid_input = (0, 0, 0, 0, 0, 0) valid_input = (0, 0, 0, 0, 0, 0)
class Ui(Ui_Semesterapparat): class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
# use the Ui_MainWindow class from mainwindow.py # use the Ui_MainWindow class from mainwindow.py
def __init__(self, MainWindow, username: str) -> None: # type:ignore def __init__(self, MainWindow, username: str) -> None: # type:ignore
log.info("Starting Semesterapparatsmanagement") log.info("Starting Semesterapparatsmanagement")
@@ -78,7 +82,9 @@ class Ui(Ui_Semesterapparat):
self.setupUi(MainWindow) # type:ignore self.setupUi(MainWindow) # type:ignore
self.MainWindow = MainWindow # type:ignore self.MainWindow = MainWindow # type:ignore
# set the window title # set the window title
MainWindow.setWindowTitle("Semesterapparatsmanagement") # type:ignore MainWindow.setWindowTitle(
f"Semesterapparatsmanagement Semester: {Semester().value}"
) # type:ignore
MainWindow.setWindowIcon(Icon("logo").icon) # type:ignore MainWindow.setWindowIcon(Icon("logo").icon) # type:ignore
self.db = Database() self.db = Database()
@@ -121,6 +127,8 @@ class Ui(Ui_Semesterapparat):
Icon("offAction", self.actionBeenden) Icon("offAction", self.actionBeenden)
self.actionBeenden.triggered.connect(self.quit) # type:ignore self.actionBeenden.triggered.connect(self.quit) # type:ignore
self.actionAbout.triggered.connect(self.open_about) # type:ignore self.actionAbout.triggered.connect(self.open_about) # type:ignore
self.actionMedien_loeschen.triggered.connect(self.open_delete_dialog)
self.actionMedien_loeschen.setIcon(Icon("trash").icon)
# set validators # set validators
self.sem_sommer.clicked.connect(lambda: self.toggleButton(self.sem_winter)) # type:ignore self.sem_sommer.clicked.connect(lambda: self.toggleButton(self.sem_winter)) # type:ignore
@@ -177,11 +185,11 @@ class Ui(Ui_Semesterapparat):
self.app_fach.currentTextChanged.connect(self.validate_app_fach) # type:ignore self.app_fach.currentTextChanged.connect(self.validate_app_fach) # type:ignore
self.sem_year.textChanged.connect(self.validate_semester) # type:ignore self.sem_year.textChanged.connect(self.validate_semester) # type:ignore
self.check_eternal_app.stateChanged.connect(self.validate_semester) # type:ignore self.check_eternal_app.stateChanged.connect(self.validate_semester) # type:ignore
self.chkbx_show_del_media.stateChanged.connect(self.update_app_media_list) # type:ignore # self.chkbx_show_del_media.stateChanged.connect(self.update_app_media_list) # type:ignore
self.progress_label.setText("Bitte warten...") self.progress_label.setText("Bitte warten...")
# Set visibility/enabled state of certain entries # Set visibility/enabled state of certain entries
self.chkbx_show_del_media.setEnabled(False) # self.chkbx_show_del_media.setEnabled(False)
self.label_info.hide() self.label_info.hide()
self.app_group_box.setEnabled(False) self.app_group_box.setEnabled(False)
self.line_2.hide() self.line_2.hide()
@@ -189,8 +197,12 @@ class Ui(Ui_Semesterapparat):
self.btn_reserve.hide() self.btn_reserve.hide()
self.label_20.hide() self.label_20.hide()
self.line_3.hide() self.line_3.hide()
self.progressBar.setValue(0)
self.progressBar.hide()
self.progressBar.setMinimum(0)
self.avail_status.hide() self.avail_status.hide()
self.chkbx_show_del_media.hide() # self.chkbx_show_del_media.hide()
self.chkbx_show_only_wit_neweditions.hide()
self.automation_add_selected_books.hide() self.automation_add_selected_books.hide()
# self.btn_del_select_apparats.setEnabled(False) # self.btn_del_select_apparats.setEnabled(False)
@@ -238,6 +250,7 @@ class Ui(Ui_Semesterapparat):
self.availChecker = None self.availChecker = None
self.mail_thread = None self.mail_thread = None
self.autoGrabber = None self.autoGrabber = None
self.newEditionChecker = NewEditionCheckerThread()
self.elsatab.setLayout(QtWidgets.QVBoxLayout()) self.elsatab.setLayout(QtWidgets.QVBoxLayout())
self.search_statistics.setLayout(QtWidgets.QVBoxLayout()) self.search_statistics.setLayout(QtWidgets.QVBoxLayout())
@@ -252,8 +265,69 @@ class Ui(Ui_Semesterapparat):
self.steps.hide() self.steps.hide()
self.player = QMediaPlayer()
self.audio_output = QAudioOutput()
self.status_progress = QtWidgets.QProgressBar()
self.statusBar.addWidget(self.status_progress)
self.status_progress.hide()
self.valid_check_semester.clicked.connect(self.display_valid_semester) # type:ignore self.valid_check_semester.clicked.connect(self.display_valid_semester) # type:ignore
# allow files to be dragged into document_list
self.document_list.setDragDropMode(
QtWidgets.QAbstractItemView.DragDropMode.DropOnly
)
self.document_list.setAcceptDrops(True)
self.document_list.viewport().setAcceptDrops(True)
self.document_list.installEventFilter(self)
self.document_list.viewport().installEventFilter(self)
def open_delete_dialog(self):
# this will open a dialog to select and delete multiple entries. by default, all books will be loaded, and a search bar can be used to fuzzy search the books and select books. on a button press, the selected books will be deleted from the apparat(s) they are part in
# raise NotImplementedError("This feature is not yet implemented.")
dialog = DeleteDialog()
dialog.exec()
def eventFilter(self, obj, event):
# Only handle events for document_list and its viewport
if obj in (self.document_list, self.document_list.viewport()):
et = event.type()
if et == QtCore.QEvent.Type.DragEnter:
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.acceptProposedAction()
else:
event.ignore()
return True
elif et == QtCore.QEvent.Type.DragMove:
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.acceptProposedAction()
else:
event.ignore()
return True
elif et == QtCore.QEvent.Type.Drop:
if not self.app_group_box.isEnabled():
self.confirm_popup(
"Bitte öffnen Sie zuerst einen Apparat!", title="Fehler"
)
return True
if event.mimeData().hasUrls():
for url in event.mimeData().urls():
self.add_document(url.toLocalFile())
event.setDropAction(QtCore.Qt.CopyAction)
event.acceptProposedAction()
else:
event.ignore()
return True
return super().eventFilter(obj, event)
def update_eta(self, eta: str):
eta = int(eta)
# transform seconds eta to HH:MM:SS
transformed_eta = f"{eta // 3600}:{(eta % 3600) // 60}:{eta % 60}"
self.label_eta.setText(f"Bitte warten... (ETA: {transformed_eta})")
def create_doc(self): def create_doc(self):
log.debug("Creating document") log.debug("Creating document")
# open DocumentPrintDialog # open DocumentPrintDialog
@@ -295,8 +369,11 @@ class Ui(Ui_Semesterapparat):
elif self.select_action_box.currentText() == "Lehrperson bearbeiten": elif self.select_action_box.currentText() == "Lehrperson bearbeiten":
self.setWidget(EditProf()) self.setWidget(EditProf())
self.admin_action.setTitle("Lehrperson bearbeiten") self.admin_action.setTitle("Lehrperson bearbeiten")
elif self.select_action_box.currentText() == "Medien bearbeiten":
self.setWidget(UpdateSignatures())
self.admin_action.setTitle("Medien bearbeiten")
else: else:
self.hideWidget() # self.hideWidget()
self.admin_action.setTitle("") self.admin_action.setTitle("")
def toggleButton(self, button: QtWidgets.QCheckBox): def toggleButton(self, button: QtWidgets.QCheckBox):
@@ -312,6 +389,12 @@ class Ui(Ui_Semesterapparat):
tempdelete() tempdelete()
sys.exit() sys.exit()
def play_sound(self, sound_file: str):
self.player.setAudioOutput(self.audio_output)
self.audio_output.setVolume(50)
self.player.setSource(QtCore.QUrl.fromLocalFile(f"src/sounds/{sound_file}"))
self.player.play()
def get_apparats(self): def get_apparats(self):
alist = self.db.getAllAparats(deleted=0) alist = self.db.getAllAparats(deleted=0)
alist = natsorted(alist, key=lambda x: x.appnr, reverse=True) alist = natsorted(alist, key=lambda x: x.appnr, reverse=True)
@@ -340,9 +423,18 @@ class Ui(Ui_Semesterapparat):
self.statusBar.showMessage("") self.statusBar.showMessage("")
def update_calendar(self, data: list[dict[str, Any]]): def update_calendar(self, data: list[dict[str, Any]]):
self.calendarWidget.setMessages([data]) self.calendarWidget.setMessages(data)
self.calendarWidget.updateCells() self.calendarWidget.updateCells()
def status_bar_progress(self, current: int, total: int):
self.status_progress.setRange(0, total)
self.status_progress.setValue(current)
if current == total:
self.status_progress.hide()
self.status_progress.setValue(0)
else:
self.status_progress.show()
def tabW1_changed(self): def tabW1_changed(self):
if self.tabWidget.currentIndex() == 1: # Statistics if self.tabWidget.currentIndex() == 1: # Statistics
stats_layout = self.search_statistics.layout() stats_layout = self.search_statistics.layout()
@@ -357,6 +449,7 @@ class Ui(Ui_Semesterapparat):
statistics.apparat_open.connect(self.open_apparat) statistics.apparat_open.connect(self.open_apparat)
statistics.refreshSignal.connect(self.update_apparat_list) statistics.refreshSignal.connect(self.update_apparat_list)
statistics.updateCalendar.connect(self.update_calendar) statistics.updateCalendar.connect(self.update_calendar)
statistics.status_update.connect(self.status_bar_progress)
stats_layout.addWidget(statistics) stats_layout.addWidget(statistics)
# #log.debug("searchpage") # #log.debug("searchpage")
@@ -399,7 +492,7 @@ class Ui(Ui_Semesterapparat):
else: else:
return f"WiSe {currentYear}/{currentYear + 1}" return f"WiSe {currentYear}/{currentYear + 1}"
def open_apparat(self, apparat): def open_apparat(self, apparat: Union[int, str]):
if self.load_app_data(apparat): if self.load_app_data(apparat):
# change tab focus to tab 0 # change tab focus to tab 0
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
@@ -471,7 +564,7 @@ class Ui(Ui_Semesterapparat):
self.update_app_media_list() self.update_app_media_list()
self.cancel_active_selection.click() self.cancel_active_selection.click()
self.check_send_mail.show() self.check_send_mail.show()
self.chkbx_show_del_media.show() # self.chkbx_show_del_media.show()
self.cancel_active_selection.setEnabled(False) self.cancel_active_selection.setEnabled(False)
self.add_medium.setEnabled(False) self.add_medium.setEnabled(False)
# update apparat table # update apparat table
@@ -608,17 +701,17 @@ class Ui(Ui_Semesterapparat):
self.prof_mail.setText(data.mail) self.prof_mail.setText(data.mail)
self.app_name.setFocus() self.app_name.setFocus()
def get_index_of_value(self, table_widget, value): def get_index_of_value(self, table_widget: QtWidgets.QTableWidget, value: str):
for i in range(table_widget.rowCount()): for i in range(table_widget.rowCount()):
for j in range(table_widget.columnCount()): for j in range(table_widget.columnCount()):
if ( if (
table_widget.item(i, j) is not None table_widget.item(i, j) is not None
and table_widget.item(i, j).text() == value and table_widget.item(i, j).text() == value # type: ignore
): ):
return i, j return i, j
return (None, None) return (None, None)
def load_app_data(self, app_id=None): def load_app_data(self, app_id: Optional[Union[int, str]] = None):
self.cancel_active_selection.setEnabled(True) self.cancel_active_selection.setEnabled(True)
self.add_medium.setEnabled(True) self.add_medium.setEnabled(True)
for child in self.app_group_box.findChildren(QtWidgets.QToolButton): for child in self.app_group_box.findChildren(QtWidgets.QToolButton):
@@ -638,12 +731,12 @@ class Ui(Ui_Semesterapparat):
self.sem_winter.setEnabled(False) self.sem_winter.setEnabled(False)
self.sem_year.setEnabled(False) self.sem_year.setEnabled(False)
self.document_list.setRowCount(0) self.document_list.setRowCount(0)
self.chkbx_show_del_media.setEnabled(True) # self.chkbx_show_del_media.setEnabled(True)
appdata = self.db.getApparatData(appnr, appname) appdata = self.db.getApparatData(appnr, appname)
self.populate_frame(appdata) self.populate_frame(appdata)
self.btn_apparat_save.hide() self.btn_apparat_save.hide()
self.btn_reserve.show() self.btn_reserve.show()
self.chkbx_show_del_media.show() # self.chkbx_show_del_media.show()
self.drpdwn_app_nr.setDisabled(True) self.drpdwn_app_nr.setDisabled(True)
self.update_app_media_list() self.update_app_media_list()
@@ -680,7 +773,7 @@ class Ui(Ui_Semesterapparat):
self.sem_year.setEnabled(True) self.sem_year.setEnabled(True)
self.sem_sommer.setEnabled(True) self.sem_sommer.setEnabled(True)
self.sem_winter.setEnabled(True) self.sem_winter.setEnabled(True)
self.chkbx_show_del_media.setEnabled(True) # self.chkbx_show_del_media.setEnabled(True)
self.drpdwn_app_nr.setEnabled(True) self.drpdwn_app_nr.setEnabled(True)
self.app_fach.setEnabled(True) self.app_fach.setEnabled(True)
self.check_send_mail.show() self.check_send_mail.show()
@@ -734,13 +827,14 @@ class Ui(Ui_Semesterapparat):
"Bitte mindestens ein Medium hinzufügen!", title="Fehler" "Bitte mindestens ein Medium hinzufügen!", title="Fehler"
) )
app_id = self.active_apparat app_nr = self.db.getId(self.app_name.text())
prof_id = self.db.getProfId(self.profdata) prof_id = self.db.getProfId(self.profdata)
log.debug(prof_id) log.debug(f"{prof_id}, {app_nr}")
# check if app_id is in database # check if app_id is in database
if self.db.checkApparatExistsById(app_id) is False: if app_nr is None:
# create apparat # create apparat
self.btn_save_apparat(False) self.btn_save_apparat(False)
app_nr = self.db.getId(self.app_name.text())
# create a thread that updates the progress label after each medium # create a thread that updates the progress label after each medium
# self.bookGrabber = None # self.bookGrabber = None
@@ -748,7 +842,7 @@ class Ui(Ui_Semesterapparat):
bookGrabber.add_values( bookGrabber.add_values(
mode=mode, mode=mode,
prof_id=prof_id, prof_id=prof_id,
app_id=app_id, app_id=app_nr,
data=data, data=data,
any_book=use_any, any_book=use_any,
exact=use_exact, exact=use_exact,
@@ -804,7 +898,7 @@ class Ui(Ui_Semesterapparat):
) )
# thread = QThread() # thread = QThread()
appnumber = self.active_apparat appnumber = self.drpdwn_app_nr.currentText()
# #log.debug(links) # #log.debug(links)
self.availChecker = AvailChecker(links, appnumber, books=books) self.availChecker = AvailChecker(links, appnumber, books=books)
# availcheck.moveToThread(thread) # availcheck.moveToThread(thread)
@@ -825,7 +919,7 @@ class Ui(Ui_Semesterapparat):
self.document_list.setRowCount(0) self.document_list.setRowCount(0)
self.app_group_box.setEnabled(False) self.app_group_box.setEnabled(False)
self.app_fach.setCurrentText("") self.app_fach.setCurrentText("")
self.chkbx_show_del_media.hide() # self.chkbx_show_del_media.hide()
self.check_send_mail.hide() self.check_send_mail.hide()
self.btn_reserve.hide() self.btn_reserve.hide()
self.check_eternal_app.setEnabled(False) self.check_eternal_app.setEnabled(False)
@@ -847,16 +941,14 @@ class Ui(Ui_Semesterapparat):
self.validate_semester() self.validate_semester()
def update_app_media_list(self): def update_app_media_list(self):
deleted = 0 if not self.chkbx_show_del_media.isChecked() else 1 app_id = self.db.getId(self.app_name.text())
app_id = self.active_apparat
prof_id = self.db.getProfId(self.profdata) prof_id = self.db.getProfId(self.profdata)
books: list[dict[int, BookData, int]] = self.db.getBooks( books: list[dict[int, BookData, int]] = self.db.getBooks(app_id, prof_id, 0)
app_id, prof_id, deleted
)
# # #log.debug(books) # # #log.debug(books)
# take the dataclass from the tuple # take the dataclass from the tuple
# booklist:list[BookData]=[book[0] for book in books] # booklist:list[BookData]=[book[0] for book in books]
self.tableWidget_apparat_media.clearContents()
self.tableWidget_apparat_media.setRowCount(0) self.tableWidget_apparat_media.setRowCount(0)
for book in books: for book in books:
book["id"] book["id"]
@@ -889,10 +981,15 @@ class Ui(Ui_Semesterapparat):
3, 3,
QtWidgets.QTableWidgetItem(book_data.author), QtWidgets.QTableWidgetItem(book_data.author),
) )
self.tableWidget_apparat_media.setItem( label = QtWidgets.QLabel(f'<a href="{book_data.link}">Katalog</a>')
self.tableWidget_apparat_media.rowCount() - 1, label.setOpenExternalLinks(True)
6, label.setTextFormat(QtCore.Qt.TextFormat.RichText)
QtWidgets.QTableWidgetItem(book_data.link), label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
label.setTextInteractionFlags(
QtCore.Qt.TextInteractionFlag.TextBrowserInteraction
)
self.tableWidget_apparat_media.setCellWidget(
self.tableWidget_apparat_media.rowCount() - 1, 6, label
) )
if availability == 1: if availability == 1:
# display green checkmark at column 4 in the row # display green checkmark at column 4 in the row
@@ -915,10 +1012,6 @@ class Ui(Ui_Semesterapparat):
self.tableWidget_apparat_media.rowCount() - 1, 4 self.tableWidget_apparat_media.rowCount() - 1, 4
).setToolTip("Das Medium wurde nicht im Apparat gefunden") ).setToolTip("Das Medium wurde nicht im Apparat gefunden")
# make table link clickable
# self.tableWidget_apparat_media.itemClicked.connect(self.open_link)
# self.tableWidget_apparat_media.
def open_link(self, item): def open_link(self, item):
def __openLink(link): def __openLink(link):
if link == "": if link == "":
@@ -956,21 +1049,33 @@ class Ui(Ui_Semesterapparat):
for prof in profs: for prof in profs:
self.drpdwn_prof_name.addItem(prof) self.drpdwn_prof_name.addItem(prof)
def add_document(self): def add_document(self, url: Optional[str] = None):
# #log.debug("Add document") # #log.debug("Add document")
picker = FilePicker() if not url:
files = picker.pick_files() picker = FilePicker()
for file in files: files = picker.pick_files()
# #log.debug(file) for file in files:
filename = file.split("/")[-1] # #log.debug(file)
filetype = filename.split(".")[-1] filename = file.split("/")[-1]
filetype = filename.split(".")[-1]
self.document_list.insertRow(0)
self.document_list.setItem(0, 0, QtWidgets.QTableWidgetItem(filename))
self.document_list.setItem(0, 1, QtWidgets.QTableWidgetItem(filetype))
self.document_list.setItem(0, 2, QtWidgets.QTableWidgetItem("*"))
self.document_list.setItem(0, 3, QtWidgets.QTableWidgetItem(file))
# set tooltip of row 3 to the file path for each row
self.document_list.item(0, 3).setToolTip(file)
self.document_list.item(0, 0).setToolTip(filename)
else:
self.document_list.insertRow(0) self.document_list.insertRow(0)
filename = url.split("/")[-1]
filetype = filename.split(".")[-1]
self.document_list.setItem(0, 0, QtWidgets.QTableWidgetItem(filename)) self.document_list.setItem(0, 0, QtWidgets.QTableWidgetItem(filename))
self.document_list.setItem(0, 1, QtWidgets.QTableWidgetItem(filetype)) self.document_list.setItem(0, 1, QtWidgets.QTableWidgetItem(filetype))
self.document_list.setItem(0, 2, QtWidgets.QTableWidgetItem("*")) self.document_list.setItem(0, 2, QtWidgets.QTableWidgetItem("*"))
self.document_list.setItem(0, 3, QtWidgets.QTableWidgetItem(file)) self.document_list.setItem(0, 3, QtWidgets.QTableWidgetItem(url))
# set tooltip of row 3 to the file path for each row # set tooltip of row 3 to the file path for each row
self.document_list.item(0, 3).setToolTip(file) self.document_list.item(0, 3).setToolTip(url)
self.document_list.item(0, 0).setToolTip(filename) self.document_list.item(0, 0).setToolTip(filename)
def open_document(self): def open_document(self):
@@ -987,7 +1092,7 @@ class Ui(Ui_Semesterapparat):
self.document_list.currentRow(), 1 self.document_list.currentRow(), 1
).text() ).text()
except AttributeError: except AttributeError:
self.confirm_popup("Bitte erst ein document auswählen!", title="Fehler") self.confirm_popup("Bitte erst ein Dokument auswählen!", title="Fehler")
return return
if not _selected_doc_location == "Database": if not _selected_doc_location == "Database":
path = Path(_selected_doc_location) path = Path(_selected_doc_location)
@@ -1006,7 +1111,7 @@ class Ui(Ui_Semesterapparat):
) )
def add_media_from_file(self): def add_media_from_file(self):
app_id = self.active_apparat app_id = self.db.getId(self.app_name.text())
prof_id = self.db.getProfId(self.profdata) prof_id = self.db.getProfId(self.profdata)
def __open_dialog(signatures: list[str]): def __open_dialog(signatures: list[str]):
@@ -1052,17 +1157,24 @@ class Ui(Ui_Semesterapparat):
temp_file = tempfile.NamedTemporaryFile( temp_file = tempfile.NamedTemporaryFile(
delete=False, suffix="." + file_type delete=False, suffix="." + file_type
) )
temp_file.write( temp_file.write(self.db.getBlob(file_name, int(app_id)))
self.db.getBlob(file_name, int(self.active_apparat))
)
temp_file.close() temp_file.close()
file = temp_file.name file = temp_file.name
if file_type == "pdf": if file_type == "pdf":
# Todo: implement parser here data = pdf_to_semap(file)
self.confirm_popup( signatures = data.signatures
"PDF Dateien werden noch nicht unterstützt!", title="Fehler" data = __open_dialog(signatures)
) # if no data was returned, return
return if data == []:
return
for book in data:
if not isinstance(book, BookData):
continue
self.db.addBookToDatabase(
bookdata=book,
app_id=app_id,
prof_id=prof_id,
)
if file_type == "csv": if file_type == "csv":
signatures = csv_to_list(file) signatures = csv_to_list(file)
data = __open_dialog(signatures) data = __open_dialog(signatures)
@@ -1086,6 +1198,8 @@ class Ui(Ui_Semesterapparat):
self.db.addBookToDatabase( self.db.addBookToDatabase(
bookdata=book, app_id=app_id, prof_id=prof_id bookdata=book, app_id=app_id, prof_id=prof_id
) )
if file_type == "eml":
data = eml_to_semap(file)
self.update_app_media_list() self.update_app_media_list()
# #log.debug(len(signatures)) # #log.debug(len(signatures))
@@ -1099,7 +1213,9 @@ class Ui(Ui_Semesterapparat):
log.info("File selected: {}, {}", file_name, file_location) log.info("File selected: {}, {}", file_name, file_location)
if file_location == "Database": if file_location == "Database":
# create warning, then return # create warning, then return
self.db.recreateFile(file_name, self.active_apparat, filetype=file_type) file = self.db.recreateFile(
file_name, self.active_apparat, filetype=file_type
)
if file_type == "pdf": if file_type == "pdf":
# Todo: implement parser here # Todo: implement parser here
self.confirm_popup("PDF Dateien werden nicht unterstützt!", title="Fehler") self.confirm_popup("PDF Dateien werden nicht unterstützt!", title="Fehler")
@@ -1108,12 +1224,14 @@ class Ui(Ui_Semesterapparat):
signatures = csv_to_list(file) signatures = csv_to_list(file)
# add the data to the database # add the data to the database
return signatures return signatures
if file_type == "docx": if file_type in ("docx", "doc"):
data = word_to_semap(file) data = word_to_semap(file)
log.info("Converted data from semap file") log.info("Converted data from semap file")
log.debug("Got the data: {}", data) log.debug("Got the data: {}", data)
return data return data
else:
raise ValueError("Dateityp wird nicht unterstützt")
def import_data_from_document(self): def import_data_from_document(self):
global valid_input global valid_input
@@ -1125,6 +1243,7 @@ class Ui(Ui_Semesterapparat):
self.prof_mail.setText(data.mail) self.prof_mail.setText(data.mail)
self.prof_tel_nr.setText(str(data.phoneNumber).replace("-", "")) self.prof_tel_nr.setText(str(data.phoneNumber).replace("-", ""))
self.app_name.setText(data.title)
if len(data.title_suggestions) > 0: if len(data.title_suggestions) > 0:
# create a dialog that has a dropdown with the suggestions, and oc and cancel button. on ok return the selected text and set it as title # create a dialog that has a dropdown with the suggestions, and oc and cancel button. on ok return the selected text and set it as title
dialog = QtWidgets.QDialog() dialog = QtWidgets.QDialog()
@@ -1151,10 +1270,11 @@ class Ui(Ui_Semesterapparat):
dialog.exec() dialog.exec()
if dialog.result() == QtWidgets.QDialog.DialogCode.Accepted: if dialog.result() == QtWidgets.QDialog.DialogCode.Accepted:
print("Selected title:", dropdown.currentText()) # print("Selected title:", dropdown.currentText())
self.app_name.setText(dropdown.currentText().split(" [")[0].strip()) self.app_name.setText(dropdown.currentText().split(" [")[0].strip())
else: else:
self.app_name.setText("CHANGEME") self.app_name.setText("CHANGEME")
# self.app_name.setText(data.title) # self.app_name.setText(data.title)
subjects = self.db.getSubjects() subjects = self.db.getSubjects()
subjects = [subject[1] for subject in subjects] subjects = [subject[1] for subject in subjects]
@@ -1171,11 +1291,14 @@ class Ui(Ui_Semesterapparat):
if data.eternal: if data.eternal:
self.check_eternal_app.setChecked(True) self.check_eternal_app.setChecked(True)
self.validate_semester() self.validate_semester()
if data.books != []:
self.btn_check_file_threaded(data)
def btn_check_file_threaded(self): def btn_check_file_threaded(self, c_document: Optional[SemapDocument] = None):
for runner in self.bookGrabber: for runner in self.bookGrabber:
if not runner.isRunning(): if not runner.isRunning():
runner.deleteLater() runner.deleteLater()
self.bookGrabber.remove(runner)
# #log.debug("Checking file") # #log.debug("Checking file")
# get active app_id and prof_id # get active app_id and prof_id
self.tableWidget_apparate.setEnabled(False) self.tableWidget_apparate.setEnabled(False)
@@ -1198,7 +1321,7 @@ class Ui(Ui_Semesterapparat):
) )
self.db.createProf(prof) self.db.createProf(prof)
# if app_id not in database, create apparat # if app_id not in database, create apparat
if not self.db.checkApparatExistsById(app_id): if not app_id:
log.info("Apparat does not exist, creating new apparat") log.info("Apparat does not exist, creating new apparat")
# create apparat # create apparat
# #log.debug("Creating apparat") # #log.debug("Creating apparat")
@@ -1218,7 +1341,10 @@ class Ui(Ui_Semesterapparat):
prof_id = self.db.getProfId(self.profdata) prof_id = self.db.getProfId(self.profdata)
# log.debug("Prof ID is None", prof_id) # log.debug("Prof ID is None", prof_id)
document = self.extract_document_data() document = None
if c_document is None or not isinstance(c_document, SemapDocument):
document = self.extract_document_data()
if document is None: if document is None:
log.error("Document is None") log.error("Document is None")
elif isinstance(document, SemapDocument): elif isinstance(document, SemapDocument):
@@ -1228,7 +1354,7 @@ class Ui(Ui_Semesterapparat):
autoGrabber = BookGrabber() autoGrabber = BookGrabber()
autoGrabber.add_values( autoGrabber.add_values(
mode="ARRAY", mode="ARRAY",
app_id=int(app_id), app_id=app_id,
prof_id=int(prof_id), prof_id=int(prof_id),
data=signatures, data=signatures,
any_book=True, any_book=True,
@@ -1293,7 +1419,7 @@ class Ui(Ui_Semesterapparat):
) )
prof.title = self.prof_title.text() prof.title = self.prof_title.text()
apparat = Apparat( apparat = Apparat(
appnr=self.active_apparat, appnr=int(self.drpdwn_app_nr.currentText()),
name=self.app_name.text(), name=self.app_name.text(),
created_semester=self.generateSemester(), created_semester=self.generateSemester(),
eternal=1 if self.check_eternal_app.isChecked() else 0, eternal=1 if self.check_eternal_app.isChecked() else 0,
@@ -1316,7 +1442,8 @@ class Ui(Ui_Semesterapparat):
return return
appdata = self.db.getAllAparats() appdata = self.db.getAllAparats()
# merge self.appdata and appdata, remove duplicates # merge self.appdata and appdata, remove duplicates
self.apparats = list(set(self.apparats + appdata))
self.apparats = self.__uniques(self.apparats, appdata)
self.apparats = natsorted(self.apparats, key=lambda x: x[4], reverse=True) self.apparats = natsorted(self.apparats, key=lambda x: x[4], reverse=True)
self.update_apparat_list() self.update_apparat_list()
@@ -1335,12 +1462,22 @@ class Ui(Ui_Semesterapparat):
self.__clear_fields() self.__clear_fields()
return True return True
def __uniques(self, list1, list2):
seen = set()
unique_list = []
for item in list1 + list2:
identifier = (item.appnr, item.name)
if identifier not in seen:
seen.add(identifier)
unique_list.append(item)
return unique_list
def send_mail_preview(self): def send_mail_preview(self):
pass pass
@property @property
def active_apparat(self): def active_apparat(self):
return self.drpdwn_app_nr.currentText() return self.db.getId(self.app_name.text())
@property @property
def profdata(self): def profdata(self):
@@ -1434,10 +1571,18 @@ class Ui(Ui_Semesterapparat):
contact_action = menu.addAction("Kontaktieren") contact_action = menu.addAction("Kontaktieren")
delete_action = menu.addAction("Löschen") delete_action = menu.addAction("Löschen")
remind_action = menu.addAction("Erinnerung") remind_action = menu.addAction("Erinnerung")
new_edition_check = menu.addAction("Auf Neuauflagen prüfen")
order_newedition_action = menu.addAction("Neuauflagen bestellen")
menu.addAction(extend_action) menu.addAction(extend_action)
menu.addActions([contact_action, delete_action, remind_action]) menu.addActions(
extend_action.triggered.connect(self.extend_apparat) [
remind_action.triggered.connect(self.reminder) contact_action,
delete_action,
remind_action,
new_edition_check,
order_newedition_action,
]
)
# convert point to row and column # convert point to row and column
row = self.tableWidget_apparate.rowAt(position.y()) row = self.tableWidget_apparate.rowAt(position.y())
column = self.tableWidget_apparate.columnAt(position.x()) column = self.tableWidget_apparate.columnAt(position.x())
@@ -1447,6 +1592,12 @@ class Ui(Ui_Semesterapparat):
app_id = self.tableWidget_apparate.item(row, 0).text() app_id = self.tableWidget_apparate.item(row, 0).text()
pid = self.db.getProfIDByApparat(app_id) pid = self.db.getProfIDByApparat(app_id)
log.debug(app_id, pid) log.debug(app_id, pid)
extend_action.triggered.connect(self.extend_apparat)
remind_action.triggered.connect(self.reminder)
new_edition_check.triggered.connect(lambda: self.check_new_editions())
order_newedition_action.triggered.connect(
lambda: self.order_new_editions(app_id, pid)
)
delete_action.triggered.connect(lambda: self.delete_apparat(pos)) delete_action.triggered.connect(lambda: self.delete_apparat(pos))
# pass pos to contact_prof # pass pos to contact_prof
contact_action.triggered.connect( contact_action.triggered.connect(
@@ -1454,6 +1605,119 @@ class Ui(Ui_Semesterapparat):
) )
menu.exec(self.tableWidget_apparate.mapToGlobal(position)) menu.exec(self.tableWidget_apparate.mapToGlobal(position))
def order_new_editions(self, app_nr, prof_id):
log.info("Opening order new editions dialog")
app_id = self.db.getId(self.db.getApparatNameByAppNr(app_nr))
prof_id = prof_id
mail_data = {
"prof_name": "Erwerbung",
"prof_mail": "erw@ph-freiburg.de",
"app_nr": app_nr,
"app_name": self.db.getApparatName(app_nr, prof_id),
}
orderDialog = NewEditionDialog(app_id, mail_data)
orderDialog.exec()
def update_status(self, curr, total):
self.avail_status.show()
self.label_20.show()
self.progressBar.show()
self.avail_status.setText(f"{curr}/{total}")
self.progressBar.setValue(curr)
if curr == total:
self.avail_status.hide()
self.label_20.hide()
self.progressBar.hide()
self.progressBar.setValue(0)
self.avail_status.setText("0/0")
def check_new_editions(self):
# create a dialog that asks "Prof oder Apparat" with a button for each. based on that either search through the books of the apparat, or all books associated with the prof
selector = NewEditionCheckSelector()
selector.exec()
pick = selector.selection
app_id = self.tableWidget_apparate.item(
self.tableWidget_apparate.currentRow(), 0
).text()
prof_id: int = self.db.getProfIDByApparat(app_id)
app_name = self.tableWidget_apparate.item(
self.tableWidget_apparate.currentRow(), 1
).text()
subject = self.tableWidget_apparate.item(
self.tableWidget_apparate.currentRow(), 4
).text()
if pick == "professor":
books = self.db.getBooksByProfId(prof_id)
app_name = "Sammelmail"
app_id = ", ".join(
[str(app.appnr) for app in self.db.getApparatsByProf(prof_id)]
)
else:
apparats_id = self.db.getId(app_name)
books = self.db.getBooks(apparats_id, prof_id, deleted=0)
searchable_books = []
for book in books:
b = book["bookdata"]
b.in_apparat = True
b.library_location = self.db.query_db(
"""SELECT s.appnr FROM media AS m
JOIN semesterapparat AS s on m.app_id = s.id
WHERE m.id = ?""",
(book["id"],),
one=True,
)[0]
searchable_books.append(b)
log.info(f"Checking {len(searchable_books)} for new editions")
self.newEditionChecker.entries = searchable_books
self.newEditionChecker.finished.connect(self.newEditionChecker.reset)
self.newEditionChecker.finished.connect(self.reset_eta)
self.newEditionChecker.etaSignal.connect(self.update_eta)
self.progressBar.setMaximum(len(searchable_books))
self.newEditionChecker.updateSignal.connect(self.update_status)
self.newEditionChecker.start()
while self.newEditionChecker.isRunning():
QtWidgets.QApplication.processEvents()
results = self.newEditionChecker.results
log.success("Finished checking for new editions, got {} results", len(results))
if results == []:
self.play_sound("error.mp3")
return
self.play_sound("ding.mp3")
log.info(f"Found {len(results)} possible new editions - opening dialog")
newEditionChecker = NewEditionChecker(results=results)
newEditionChecker.exec()
accepted_books = newEditionChecker.accepted_books
if accepted_books == []:
return
for book in accepted_books:
oldBookId = self.db.getBookIdByPPN(book.old_book.ppn)
apparats_id = self.db.getId(
self.db.getApparatNameByAppNr(book.old_book.library_location)
)
self.db.insertNewEdition(book, oldBookId, apparats_id)
pass
self.mail_thread = Mail_Dialog(
prof_name=self.db.getSpecificProfData(prof_id, ["fullname"]),
prof_mail=self.db.getProfMailById(prof_id),
app_id=app_id,
app_name=app_name,
app_subject=subject,
accepted_books=accepted_books,
default_mail="Neuauflagen für Semesterapparat",
)
self.mail_thread.show()
def reset_eta(self):
self.label_eta.setText("")
def reminder(self): def reminder(self):
log.info("Opening reminder dialog") log.info("Opening reminder dialog")
reminder = ReminderDialog() reminder = ReminderDialog()
@@ -1522,11 +1786,17 @@ class Ui(Ui_Semesterapparat):
apparat_add_action = QtGui.QAction("Zum Apparat hinzufügen") apparat_add_action = QtGui.QAction("Zum Apparat hinzufügen")
apparat_move_action = QtGui.QAction("In Apparat verschieben") apparat_move_action = QtGui.QAction("In Apparat verschieben")
apparat_copy_action = QtGui.QAction("In Apparat kopieren") apparat_copy_action = QtGui.QAction("In Apparat kopieren")
replace_old_editions = QtGui.QAction("Neuauflagen ersetzen")
apparatmenu = menu.addMenu("Apparate") apparatmenu = menu.addMenu("Apparate")
generalmenu = menu.addMenu("Allgemeines") generalmenu = menu.addMenu("Allgemeines")
apparatmenu.addActions( # type: ignore apparatmenu.addActions( # type: ignore
[apparat_add_action, apparat_copy_action, apparat_move_action] [
apparat_add_action,
apparat_copy_action,
apparat_move_action,
replace_old_editions,
]
) )
generalmenu.addActions([edit_action, delete_action, update_data_action]) # type: ignore generalmenu.addActions([edit_action, delete_action, update_data_action]) # type: ignore
# disable apparat_add_action # disable apparat_add_action
@@ -1537,11 +1807,77 @@ class Ui(Ui_Semesterapparat):
apparat_copy_action.triggered.connect(self.copy_to_apparat) # type: ignore apparat_copy_action.triggered.connect(self.copy_to_apparat) # type: ignore
apparat_move_action.triggered.connect(self.move_to_apparat) # type: ignore apparat_move_action.triggered.connect(self.move_to_apparat) # type: ignore
update_data_action.triggered.connect(self.update_data) # type: ignore update_data_action.triggered.connect(self.update_data) # type: ignore
replace_old_editions.triggered.connect(self.replace_old_edition) # type: ignore
menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) # type: ignore menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) # type: ignore
def replace_old_edition(self):
# open dialog
dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Neuauflagen:")
layout = QtWidgets.QVBoxLayout()
label = QtWidgets.QLabel("Folgende Medien haben Neuauflagen:")
layout.addWidget(label)
table = QtWidgets.QTableWidget()
table.setColumnCount(4)
table.setHorizontalHeaderLabels(["Titel", "Auflage", "Signatur", "Neues Werk"])
table.horizontalHeader().setStretchLastSection(True)
new_editions = self.db.getBooksWithNewEditions(
self.active_apparat,
)
for book in new_editions:
table.insertRow(0)
table.setItem(0, 0, QtWidgets.QTableWidgetItem(book[0].title))
table.setItem(0, 1, QtWidgets.QTableWidgetItem(str(book[0].edition)))
table.setItem(0, 2, QtWidgets.QTableWidgetItem(book[0].signature))
new_ed_data = (
f"{book[1].title} (Auflage {book[1].edition}, {book[1].signature})"
)
table.setItem(0, 3, QtWidgets.QTableWidgetItem(new_ed_data))
layout.addWidget(table)
dialog.setLayout(layout)
dialog.exec()
def update_data(self): def update_data(self):
# TODO: use link in table, parse data and if needed, update location / signature signatures = [
pass self.tableWidget_apparat_media.item(row, 1).text()
for row in range(self.tableWidget_apparat_media.rowCount())
] # type: ignore
prof_id = self.db.getProfId(self.profdata) # type: ignore
app_id = self.db.getId(self.app_name.text()) # type: ignore
books: List[Tuple[int, BookData]] = []
for signature in signatures:
book = self.db.getBookBasedOnSignature(
app_id=app_id,
signature=signature,
prof_id=prof_id,
)
book_id = self.db.getBookIdBasedOnSignature(
app_id,
prof_id,
signature,
)
books.append((book_id, book))
# self.autoUpdater.entries = books
# self.autoUpdater.finished.connect(self.autoUpdater.reset)
self.updater = UpdaterThread()
u_books = []
for book_id, book in books:
u_books.append({"id": book_id, "bookdata": book})
self.updater.books = u_books
self.progressBar.setMaximum(len(books))
self.updater.finished.connect(self.updater.deleteLater)
self.updater.finished.connect(self.update_app_media_list)
self.updater.currtot.connect(self.update_status)
self.updater.start()
# ppn = book.link.split("kid=")[-1]
# result = cat.get_book(ppn)
# if result:
# book.signature = result.signature
# book.in_apparat = True
# #print(book)
# self.db.updateBookdata(book_id, book)
# self.update_app_media_list()
def copy_to_apparat(self): def copy_to_apparat(self):
selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() # type: ignore selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() # type: ignore
@@ -1587,7 +1923,7 @@ class Ui(Ui_Semesterapparat):
def confirm_action_dialog(self, message, title="Bestätigung"): def confirm_action_dialog(self, message, title="Bestätigung"):
appnrs = self.db.getUnavailableApparatNumbers() appnrs = self.db.getUnavailableApparatNumbers()
appnrs = [str(i) for i in appnrs] appnrs = [str(i) for i in appnrs]
appnrs.remove(self.active_apparat) appnrs.remove(self.drpdwn_app_nr.currentText())
if len(appnrs) == 0: if len(appnrs) == 0:
# create a warning dialog, saying no apparats present # create a warning dialog, saying no apparats present
self.confirm_popup("Keine weiteren Apparate vorhanden", title="Fehler") self.confirm_popup("Keine weiteren Apparate vorhanden", title="Fehler")
@@ -1639,12 +1975,12 @@ class Ui(Ui_Semesterapparat):
).text() ).text()
prof_id = self.db.getProfId(self.profdata) prof_id = self.db.getProfId(self.profdata)
data = self.db.getBookBasedOnSignature( data = self.db.getBookBasedOnSignature(
app_id=self.active_apparat, app_id=self.db.getId(self.app_name.text()),
signature=book, signature=book,
prof_id=prof_id, prof_id=prof_id,
) )
book_id = self.db.getBookIdBasedOnSignature( book_id = self.db.getBookIdBasedOnSignature(
self.active_apparat, self.db.getId(self.app_name.text()),
prof_id, prof_id,
book, book,
) )
@@ -1668,9 +2004,7 @@ class Ui(Ui_Semesterapparat):
pass pass
def delete_medium(self): def delete_medium(self):
selected_apparat_id = self.tableWidget_apparate.item( selected_apparat_id = self.active_apparat
self.tableWidget_apparate.currentRow(), 0
).text()
prof_id = self.db.getProfId(self.profdata) prof_id = self.db.getProfId(self.profdata)
# check how many rows are selected # check how many rows are selected
selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows()
@@ -1732,10 +2066,18 @@ class Ui(Ui_Semesterapparat):
else: else:
return return
def __contact_dialog(self, apparat, location: tuple | str, mail=None, pid=""): def __contact_dialog(
self,
apparat,
location: tuple | str,
mail=None,
pid="",
accepted_books=None,
app_id="",
):
log.debug( log.debug(
"Got these values apparat: {}, location: {}, mail: {}, pid: {}".format( "Got these values apparat: {}, location: {}, mail: {}, pid: {}, accepted_books: {}, app_id: {}".format(
apparat, location, mail, pid apparat, location, mail, pid, accepted_books, app_id
) )
) )
@@ -1798,10 +2140,16 @@ class Ui(Ui_Semesterapparat):
if state == 1: if state == 1:
log.debug("Deleting apparat {}", selected_apparat_id) log.debug("Deleting apparat {}", selected_apparat_id)
pid = self.db.getProfIDByApparat(selected_apparat_id) pid = self.db.getProfIDByApparat(selected_apparat_id)
self.db.deleteApparat(selected_apparat_id, Semester().value) apparat = Apparat(
appnr=int(selected_apparat_id),
name=self.tableWidget_apparate.item(
self.tableWidget_apparate.currentRow(), 1
).text(),
)
self.db.deleteApparat(apparat=apparat, semester=Semester().value)
# delete the corresponding entry from self.apparats # delete the corresponding entry from self.apparats
for apparat in self.apparats: for apparat in self.apparats:
if apparat[4] == int(selected_apparat_id): if apparat.appnr == int(selected_apparat_id):
self.apparats.remove(apparat) self.apparats.remove(apparat)
break break
self.old_apparats = self.apparats self.old_apparats = self.apparats
@@ -1842,6 +2190,7 @@ def launch_gui():
) # if that thread uses an event loop ) # if that thread uses an event loop
app.aboutToQuit.connect(aui.docu.terminate) # our new slot app.aboutToQuit.connect(aui.docu.terminate) # our new slot
app.aboutToQuit.connect(aui.docu.wait) app.aboutToQuit.connect(aui.docu.wait)
app.aboutToQuit.connect(aui.newEditionChecker.terminate)
atexit.register(tempdelete) atexit.register(tempdelete)
# atexit.register(aui.validate_thread.quit) # atexit.register(aui.validate_thread.quit)
# atexit.register(aui.docu.quit) # atexit.register(aui.docu.quit)

View File

@@ -1,20 +1,12 @@
import sys
from typing import Any from typing import Any
import darkdetect import darkdetect
import loguru
from PySide6 import QtCore, QtWidgets from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import QDate from PySide6.QtCore import QDate
from PySide6.QtGui import QColor, QPen from PySide6.QtGui import QColor, QPen
from src import LOG_DIR
from src.backend import Database from src.backend import Database
from src.shared.logging import log
log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
color = "#ddfb00" if darkdetect.isDark() else "#2204ff" color = "#ddfb00" if darkdetect.isDark() else "#2204ff"
pen = QPen(QColor(color)) pen = QPen(QColor(color))
@@ -43,9 +35,7 @@ class MessageCalendar(QtWidgets.QCalendarWidget):
def setMessages(self, messages: list[dict[str, Any]]): def setMessages(self, messages: list[dict[str, Any]]):
# remove all drawn circles # remove all drawn circles
for message in messages: for message in messages:
# print(message)
# Convert the date string to a QDate object # Convert the date string to a QDate object
date = QDate.fromString(message["remind_at"], "yyyy-MM-dd") date = QDate.fromString(message["remind_at"], "yyyy-MM-dd")
# Store the message for the date # Store the message for the date

View File

@@ -1,9 +1,7 @@
__all__ = [ __all__ = [
"LoginWidget", "DataQtGraph",
"RegisterWidget",
"StatusWidget", "StatusWidget",
"FilePicker", "FilePicker",
"DataGraph",
"CalendarEntry", "CalendarEntry",
"MessageCalendar", "MessageCalendar",
"SearchStatisticPage", "SearchStatisticPage",
@@ -12,16 +10,23 @@ __all__ = [
"EditUser", "EditUser",
"EditProf", "EditProf",
"IconWidget", "IconWidget",
"NewEditionChecker",
"NewEditionCheckSelector",
"UpdateSignatures",
"UpdaterThread"
] ]
from .admin_create_user import UserCreate
from .admin_edit_prof import EditProf
from .admin_edit_user import EditUser
from .calendar_entry import CalendarEntry
from .collapse import StatusWidget from .collapse import StatusWidget
from .elsa_main import ElsaDialog
from .filepicker import FilePicker from .filepicker import FilePicker
from .graph import DataQtGraph from .graph import DataQtGraph
from .calendar_entry import CalendarEntry
from .MessageCalendar import MessageCalendar
from .searchPage import SearchStatisticPage
from .elsa_main import ElsaDialog
from .admin_create_user import UserCreate
from .admin_edit_user import EditUser
from .admin_edit_prof import EditProf
from .iconLine import IconWidget from .iconLine import IconWidget
from .MessageCalendar import MessageCalendar
from .new_edition_check import NewEditionChecker, NewEditionCheckSelector
from .searchPage import SearchStatisticPage
from .signature_update import UpdateSignatures, UpdaterThread

View File

@@ -1,18 +1,20 @@
from .widget_sources.admin_edit_prof_ui import Ui_Dialog # import sys
import loguru
from PySide6 import QtWidgets from PySide6 import QtWidgets
from src.logic import Prof
from src.backend import Database
import loguru
import sys
from src import LOG_DIR from src import LOG_DIR
from src.backend import Database
from src.logic import Prof
from .widget_sources.admin_edit_prof_ui import Ui_Dialog #
log = loguru.logger log = loguru.logger
log.remove() log.remove()
log.add(sys.stdout, level="INFO") log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
class EditProf(QtWidgets.QDialog, Ui_Dialog): class EditProf(QtWidgets.QDialog, Ui_Dialog):
def __init__(self): def __init__(self):
super(EditProf, self).__init__() super(EditProf, self).__init__()
@@ -55,11 +57,11 @@ class EditProf(QtWidgets.QDialog, Ui_Dialog):
# get the selected member # get the selected member
name = self.edit_faculty_member_select_member.currentText() name = self.edit_faculty_member_select_member.currentText()
fullname = name.replace(",", "") fullname = name.replace(",", "")
# print(fullname, name) # #print(fullname, name)
# get the data for the selected member # get the data for the selected member
data = self.db.getProfByName(fullname) data = self.db.getProfByName(fullname)
# set the data # set the data
# print(data) # #print(data)
if data is None: if data is None:
self.edit_faculty_member_title.setText("") self.edit_faculty_member_title.setText("")
self.faculty_member_old_telnr.setText("") self.faculty_member_old_telnr.setText("")

View File

@@ -1,7 +1,8 @@
from .widget_sources.admin_edit_user_ui import Ui_Dialog
from PySide6 import QtWidgets from PySide6 import QtWidgets
from src.backend import Database
from src.backend import AdminCommands from src.backend import AdminCommands, Database
from .widget_sources.admin_edit_user_ui import Ui_Dialog
admin = AdminCommands() admin = AdminCommands()
@@ -51,7 +52,7 @@ class EditUser(QtWidgets.QDialog, Ui_Dialog):
"role": role, "role": role,
} }
data = {key: value for key, value in data.items() if value is not None} data = {key: value for key, value in data.items() if value is not None}
# print(data) # #print(data)
self.db.updateUser(username=username, data=data) self.db.updateUser(username=username, data=data)
self.user_delete_frame_user_select.setCurrentText("") self.user_delete_frame_user_select.setCurrentText("")
self.user_edit_frame_new_password.clear() self.user_edit_frame_new_password.clear()

View File

@@ -1,9 +1,10 @@
from .widget_sources.admin_query_ui import Ui_Form from PySide6 import QtCore, QtWidgets
from PySide6 import QtWidgets, QtCore
from src import Icon from src import Icon
from src.backend import Database from src.backend import Database
from .widget_sources. import Ui_Form
class AdminQueryWidget(QtWidgets.QWidget, Ui_Form): class AdminQueryWidget(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent=None): def __init__(self, parent=None):
@@ -22,7 +23,7 @@ class AdminQueryWidget(QtWidgets.QWidget, Ui_Form):
return return
data = self.db.query_db(request_text) data = self.db.query_db(request_text)
print(data) # print(data)
table_names = ( table_names = (
request_text.lower().split("select")[1].split("from")[0].split(",") request_text.lower().split("select")[1].split("from")[0].split(",")
) )

View File

@@ -1,8 +1,10 @@
from .widget_sources.calendar_entry_ui import Ui_Dialog
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Signal from PySide6.QtCore import Signal
from src.backend.database import Database
from src import Icon from src import Icon
from src.backend.database import Database
from .widget_sources.calendar_entry_ui import Ui_Dialog
class CalendarEntry(QtWidgets.QDialog, Ui_Dialog): class CalendarEntry(QtWidgets.QDialog, Ui_Dialog):
@@ -36,12 +38,12 @@ class CalendarEntry(QtWidgets.QDialog, Ui_Dialog):
def delete_message(self): def delete_message(self):
value = self.spin_select_message.value() value = self.spin_select_message.value()
# print(value) # #print(value)
if value > 0: if value > 0:
value = value - 1 value = value - 1
message = self.messages[value] message = self.messages[value]
id = self.__get_id(message) id = self.__get_id(message)
# print("id", id) # #print("id", id)
# del self.messages[value - 1] # del self.messages[value - 1]
self.spin_select_message.setMaximum(len(self.messages)) self.spin_select_message.setMaximum(len(self.messages))
self.message_box.clear() self.message_box.clear()

View File

@@ -1,7 +1,6 @@
# import pysignal pyslot # import pysignal pyslot
from PySide6.QtCore import Signal as Signal from PySide6.QtCore import Signal as Signal
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QApplication,
QTreeWidget, QTreeWidget,
QTreeWidgetItem, QTreeWidgetItem,
QVBoxLayout, QVBoxLayout,
@@ -30,7 +29,7 @@ class StatusWidget(QWidget):
while parent: while parent:
parent_depth += 1 parent_depth += 1
parent = parent.parent() parent = parent.parent()
# print(parent_depth) # #print(parent_depth)
# Emit the person_double_clicked signal with the name of the person and the parent depth # Emit the person_double_clicked signal with the name of the person and the parent depth
self.person_double_clicked.emit(self.header, item.text(column), parent_depth) self.person_double_clicked.emit(self.header, item.text(column), parent_depth)
@@ -61,17 +60,3 @@ class StatusWidget(QWidget):
# Make the action entry collapsible # Make the action entry collapsible
action_item.setExpanded(True) action_item.setExpanded(True)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
data = {"test": {"test2": ["test3", "test4"]}}
widget = StatusWidget(data, "test")
widget.show()
# detect emit signal
widget.person_double_clicked.connect(lambda x: print(x))
sys.exit(app.exec())

View File

@@ -1,22 +1,18 @@
import os import os
from .widget_sources.elsa_maindialog_ui import Ui_Dialog
from PySide6 import QtCore, QtWidgets, QtGui
from PySide6.QtGui import QRegularExpressionValidator
from PySide6.QtCore import QDate
from src import Icon
from src.backend import Semester, Database
from src.logic import elsa_word_to_csv, Prof
from src.ui.dialogs import ElsaAddEntry, popus_confirm
from src.ui.widgets import FilePicker, DataQtGraph
from src.backend import recreateElsaFile
import loguru
import sys
from src import LOG_DIR
log = loguru.logger
log.remove()
log.add(sys.stdout, level="INFO")
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtCore import QDate
from PySide6.QtGui import QRegularExpressionValidator
from src import Icon
from src.backend import Database, recreateElsaFile
from src.logic import Prof, Semester, elsa_word_to_csv
from src.shared.logging import log
from src.ui.dialogs import ElsaAddEntry, popus_confirm
from src.ui.widgets.filepicker import FilePicker
from src.ui.widgets.graph import DataQtGraph
from .widget_sources.elsa_maindialog_ui import Ui_Dialog
class ElsaDialog(QtWidgets.QDialog, Ui_Dialog): class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
@@ -240,7 +236,7 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
if prof_id is None: if prof_id is None:
self.db.createProf(profdata) self.db.createProf(profdata)
prof_id = self.db.getProfId(prof) prof_id = self.db.getProfId(profdata)
self.profs.append( self.profs.append(
"f{}, {}".format(profdata.lastname, profdata.firstname), prof_id "f{}, {}".format(profdata.lastname, profdata.firstname), prof_id
) )
@@ -397,6 +393,7 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
# get the file path of the selected file based on it's row # get the file path of the selected file based on it's row
row = self.dokument_list_elsa.currentRow() row = self.dokument_list_elsa.currentRow()
file = self.dokument_list_elsa.item(row, 3).text() file = self.dokument_list_elsa.item(row, 3).text()
file_location = file
if file == "Database": if file == "Database":
filename = self.dokument_list_elsa.item(row, 0).text() filename = self.dokument_list_elsa.item(row, 0).text()
filetype = self.dokument_list_elsa.item(row, 1).text() filetype = self.dokument_list_elsa.item(row, 1).text()
@@ -413,6 +410,17 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
log.debug( log.debug(
f"elsa_id: {elsa_id}, prof: {self.elsa_prof.currentText()}, semester: {self.elsa_semester.text()}, date: {self.elsa_date.text()}" f"elsa_id: {elsa_id}, prof: {self.elsa_prof.currentText()}, semester: {self.elsa_semester.text()}, date: {self.elsa_date.text()}"
) )
if file_location != "Database":
self.db.insertElsaFile(
[
{
"name": file.split("/")[-1],
"path": file,
"type": file.split(".")[-1],
}
],
elsa_id,
)
for row in data: for row in data:
if self.seperateEntries.isChecked(): if self.seperateEntries.isChecked():
if ";" in row["pages"]: if ";" in row["pages"]:
@@ -426,6 +434,7 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
else: else:
self.setElsaRow(row) self.setElsaRow(row)
self.db.addElsaMedia(row, elsa_id) self.db.addElsaMedia(row, elsa_id)
self.quote_entry.setEnabled(True) self.quote_entry.setEnabled(True)
def openDocumentElsa(self): def openDocumentElsa(self):

Some files were not shown because too many files have changed in this diff Show More