diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index d9a5503..8ad09c0 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -51,10 +51,9 @@ jobs: - name: Bump version (local only) id: bump run: | - uv tool install bump-my-version + uv version --bump "${{ github.event.inputs.bump }}" - uv tool run bump-my-version bump "${{ github.event.inputs.bump }}" - version="$(uv tool run bump-my-version show current_version)" + version="$(uv version --short)" echo "VERSION=$version" >> "$GITHUB_ENV" echo "version=$version" >> "$GITHUB_OUTPUT" @@ -95,6 +94,11 @@ jobs: - name: Install all dependencies run: uv sync --all-groups + - name: Build documentation + run: | + uv zensical + uv run zensical build --clean + - name: Build Linux release with Nuitka run: | uv run python -m nuitka \ @@ -155,6 +159,8 @@ jobs: exit 1 } + & $env:UV_PATH self update + $version = "3.12" Write-Host "Checking for Python $version via uv..." $exists = & $env:UV_PATH python list | Select-String $version -Quiet @@ -171,6 +177,12 @@ jobs: run: | & $env:UV_PATH sync --all-groups + - name: Build documentation + shell: powershell + run: | + & $env:UV_PATH run pip install zensical + & $env:UV_PATH run zensical build --clean + - name: Build Windows release with Nuitka shell: powershell run: | @@ -238,9 +250,7 @@ jobs: - name: Bump version and push run: | - uv tool install bump-my-version - uv tool run bump-my-version bump "${{ github.event.inputs.bump }}" - + uv version --bump "${{ github.event.inputs.bump }}" - name: Push version bump uses: ad-m/github-push-action@master with: diff --git a/.vscode/settings.json b/.vscode/settings.json index 930051d..529ac2d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ "cSpell.words": [ "adis", "Adminbereich", + "akkey", "Apparatdetails", "apparate", "appname", @@ -54,4 +55,7 @@ "Strg", "telnr" ], + "yaml.schemas": { + "https://www.schemastore.org/github-workflow.json": "file:///c%3A/Users/aky547/GitHub/SemesterapparatsManager/.gitea/workflows/release.yml" + }, } \ No newline at end of file diff --git a/docs/admin/uebersicht.md b/docs/admin/uebersicht.md new file mode 100644 index 0000000..23eb03a --- /dev/null +++ b/docs/admin/uebersicht.md @@ -0,0 +1 @@ +# Übersicht \ No newline at end of file diff --git a/docs/allgemein/hauptoberflaeche.md b/docs/allgemein/hauptoberflaeche.md index ecfcd31..967d6a3 100644 --- a/docs/allgemein/hauptoberflaeche.md +++ b/docs/allgemein/hauptoberflaeche.md @@ -32,6 +32,16 @@ Die Hauptoberfläche des SemesterapparatsManager besteht aus drei Hauptbereichen [:octicons-arrow-right-24: Zu ELSA](../elsa/anlegen.md) +- :octicons-person-24:{ .lg .middle } **Admin** + + --- + + Adminbereich (nur wenn der angemeldete Nutzer die `Admin` Rolle hat) + + [:octicons-arrow-right-24: Zur Übersicht](../admin/uebersicht.md) + + + --- diff --git a/docs/allgemein/info.md b/docs/allgemein/info.md index c26fca0..f74b8a6 100644 --- a/docs/allgemein/info.md +++ b/docs/allgemein/info.md @@ -54,4 +54,4 @@ Die Anwendung ermöglicht eine benutzerfreundliche Verwaltung von physischen und Der SemesterapparatsManager wurde entwickelt von **Alexander Kirchner** für die Pädagogische Hochschule Freiburg. !!! info "Open Source" - Der Quellcode ist auf GitHub verfügbar und kann nach Absprache eingesehen werden. + Der Quellcode ist auf einer privaten [Gitea](https://about.gitea.com/) Instanz und kann bei Bedarf eingesehen werden. diff --git a/docs/allgemein/installation.md b/docs/allgemein/installation.md index e044b2b..d01dda0 100644 --- a/docs/allgemein/installation.md +++ b/docs/allgemein/installation.md @@ -8,6 +8,8 @@ Bevor Sie den SemesterapparatsManager installieren können, stellen Sie sicher, - [x] Internetzugang für Katalog-Abfragen - [x] Optional: Zotero-Account für ELSA-Funktionen +!!! warning "Buchmetadaten" + Die Metadaten für Bücher können aktuell nur aus dem Hochschulnetz geladen werden, da diese auf ein Internes Format zugreifen, welches nur im Hochschulnetz angezeigt wird. ## Installation ### Für Endanwender @@ -69,7 +71,12 @@ Beim ersten Start werden Sie aufgefordert, sich anzumelden: ![Login](../images/login.png){ loading=lazy } !!! tip "Tipp" - Ihre Anmeldedaten werden sicher gespeichert. Bei Fragen zur Einrichtung wenden Sie sich an den Administrator. + Ihre Anmeldedaten werden verschlüsselt gespeichert. Sollten Sie Ihr Passwort vergessen, wenden Sie sich bitten an einen Nutzer mir Adminberechtigungen, um das Passwort zu ändern. Bei Fragen zur Einrichtung wenden Sie sich an den Administrator. + + Sofern Sie keine eigenen Zugangsdaten bei der Einrichtung eingegeben haben, sind dies die Standardanmeldedaten: + + **Username**: admin + **Password**: admin ## Konfiguration diff --git a/docs/citing.md b/docs/citing.md deleted file mode 100644 index 78a62da..0000000 --- a/docs/citing.md +++ /dev/null @@ -1,30 +0,0 @@ -# Zitieren -## Oberfläche -![Zitieroberfläche](images/generate_quote_emtpy.png) - -Die [ELSA](elsa.md) Oberfläche bietet die Möglichkeit, für Einträge automatisch Zitate zu erstellen. Hierfür wird der Stil `Deutsche Gesellschaft für Psychologie (DGPs)` verwendet. - -Um ein Zitat zu erstellen, muss zuerst ein Eintrag in der Tabelle ausgewählt werden. Über den Knopf **Eintrag zitieren** wird ein Dialog geöffnet, in dem der Eintrag zitiert werden kann. -Sollte ein Eintrag mehrere Abschnitte beinhalten, muss nach der automatischen Suche die Seitenzahl angepasst werden. Ist die Seitenzahl in der Tabelle nur für einen Abschnitt, so wird diese automatisch übernommen. - - -## Zitierdialog - -![Zitierdialog mit Ergebnis](images/quote_search_result.png) - -Nachdem auf den Knopf **Eintrag zitieren** geklickt wird, wird automatisch der Katalog angefragt und relevante Daten werden in die Felder eingetragen. Ist die Seitenzahl in der Tabelle nur für einen Abschnitt, so wird diese automatisch übernommen. - -!!! info "Erläuterung der Knöpfe" - - - **Suchen** Sucht im Katalog nach dem eingegebenen Identifikator - - **Zitat erstellen** Stellt eine Verbindung zu Zotero her, erstellt ein entsprechendes Werk und erhält die Zitate als Ergebnis; wechselt zur Oberfläche der Zitate - - **Ok** Schließt den Dialog - - **Discard** Entfernt alle Eingaben - - **Abbrechen** Schließt den Dialog - - **Wiederholen** Geht zu den Eingabefeldern zurück, ermöglicht eine erneute Suche mit geänderten Eingaben - -## Zitate - -![Zitate](images/quote_generated.png) - -Über den Knopf **Zitat erstellen** wird eine Verbindung zu Zotero hergestellt und ein entsprechendes Werk erstellt. Die Zitate werden als Ergebnis angezeigt. Der Dialog wechselt automatisch zur Oberfläche der Zitate. \ No newline at end of file diff --git a/docs/config.md b/docs/config.md deleted file mode 100644 index 4119e68..0000000 --- a/docs/config.md +++ /dev/null @@ -1,42 +0,0 @@ -# Einstellungen -![Einstellungen](images/settings.png) - -In den Einstellungen werden alle Informationen angezeigt, die in der config.yaml Datei hinterlegt sind. Diese Datei wird beim Start der Datei eingelesen und als globale Klasse `Config` gespeichert. Dadurch können Einstellungen sofort geändert werden und das Programm muss nicht für jede Änderung neu gestartet werden. - -!!! Info - Die zuletzt geöffnete Seite wird automatisch beim nächsten Start geöffnet. - -## Seiten -### Datenbank - -Hier sind alle Informationen zur Datenbank, sowie den Tempörären Daten hinterlegt. -Der Speicherort der Datenbank kann über den Knopf `...` neben dem Datenbanknamen geändert werden. Der Datenbankpfad passt sich automatisch an. - -!!! Warning "Hinweis - Mehrere Nutzer" - Sollte die Anwendung von mehreren Nutzern benutzt werden, sollte dieser Pfad nur in absprache geändert werden. Ansonsten kann es zu Problemen kommen. - -### Zotero -![Zotero](images/settings_zotero.png) - -In diesem Bereich können die Zugangsdaten für Zotero hinterlegt werden. Diese werden benötigt, um die Zitate für die [ELSA](elsa.md#einträge-zitieren) Zitate zu erstellen. - - -### e-Mail -![e-Mail](images/settings_mail.png) -Dieser Bereich ist zweigeteilt, einmal der Allgemeine Teil, und einmal der Teil für die Mailsignatur - -#### Allgemein -Hier können die Zugangsdaten für den Mailversand hinterlegt werden. Diese werden benötigt, um die Nachrichten an die ProffessorInnen zu versenden. Mehr Infos: [Mailversand](mail.md) - -#### Signatur -Hier kann die Signatur für die Mails hinterlegt werden. Diese wird automatisch an jede Mail angehängt. - -### Icons -![Icons](images/settings_icons.png) -Hier werden sowohl die aktuellen Icons, wie auch die verfügbaren Farben angezeigt. - -## Speichern - -Über den Knopf **Ok** werden die Einstellungen gespeichert. Sollte ein Neustart der Anwendung erforderlich sein, wird darüber informiert. Ansonsten werden die Einstellungen sofort übernommen. - - diff --git a/docs/create.md b/docs/create.md deleted file mode 100644 index adf1e2c..0000000 --- a/docs/create.md +++ /dev/null @@ -1,90 +0,0 @@ -# Semesterapparat anlegen - -Um einen neuen Semesterapparat anzulegen, muss zuerst der Knopf "neu. App anlegen" gedrückt werden. Das Feld der Apparatdetails wird nun zum bearbeiten entsperrt, und die Daten können in die Felder eingetragen werden. - -![Anlegen](images/main_apparatdetails.png) - -## Apparatdetails -### Apparat anlegen -Um einen Apparat erfolgreich anzulegen, müssen alle Felder, die ein Sternchen (*) haben, ausgefüllt werden. Ist eine Eingabe nicht valide, wird neben der Eingabe ein rotes X angezeigt (siehe Bild). -Folgende Felder haben spezielle Formatierungsanforderungen: - -- Prof. Name: Der Name des Professors muss in der Form "Nachname, Vorname" eingegeben werden. -- Mail: Die Mailadresse muss in der Form "irgend@etwas.xy" eingegeben werden. -- Tel: Die Telefonnummer muss mindestens 3 Ziffern enthalten. -- Semester: Das Semester wird wie folgt angegeben: - - Wintersemester: Checkbox Winter + aktives Jahr wenn Monat zwischen Oktober und Dezember; ansonsten aktives Jahr - 1 - - Sommersemester: Checkbox Sommer + aktives Jahr wenn Monat zwischen März und August - - -Beim Versuch, den Apparat zu speichern, bevor alle Felder korrekt ausgefüllt sind, wird eine Fehlermeldung angezeigt, die auf die fehlerhaften Felder hinweist. Nachdem alle Felder korrekt ausgefüllt sind, kann der Apparat gespeichert werden. - -![Fehler](images/main_apparatdetails_error.png) - -Über einen Klick auf Ok oder Cancel wird die Meldung geschlossen und der Apparat kann weiter bearbeitet werden. - -### Dokumente hinzufügen - -Dem Apparat können Dokumente hinzugefügt werden. Besonders hilfreich ist das hinzufügen der Antragsformulare, da der SemesterapparatsManager diese Datei lesen und die Bücher automatisch dem Apparat hinzufügen kann. - -Dokumente werden über den Knopf "Dokumente hinzufügen" hinzugefügt werden. Es öffnet sich ein Auswahldialog, bei dem Sie dei Datei(en) auswählen können, die Sie hinzufügen möchten. - -Handelt es sich bei der Datei um den Antrag, so kann dieser mit dem Knopf "Medien aus Dokument hinzufügen" ausgelesen werden. - -!!! Warning "ZU BEACHTEN" - - Wird dieser Knopf gedrückt, wird der Apparat, wenn möglich, gespeichert und angelegt. Dies ist notwendig, da die Medien nur dann dem Apparat hinzugefügt werden können, wenn dieser bereits in der Datenbank existiert. - -Die erkannten Medien werden nun hinzugefügt. Über den Bereich "Medienliste" kann der Fortschritt eingesehen werden. Solange noch Medien hinzugefügt werden, ist es nicht möglich, den Apparat zu bearbeiten, die Auswahl zu beenden oder einen anderen Apparat auszuwählen. - -### Apparat speichern - -Nachdem alle Felder korrekt ausgefüllt sind, kann der Apparat gespeichert werden. Dazu muss der Knopf "Speichern" gedrückt werden. Der Apparat wird nun in der Datenbank gespeichert und wird in der Tabelle angezeigt. Wurde vor dem Speichern der Haken "Mail senden" gesetzt, öffnet sich ein Fenster, in dem einen Mail, basierend auf einem Template, an den Professor gesendet werden kann. (Erfordert Mail Zugangsdaten [siehe Konfiguration](config.md#email)) - -## Medienliste - -In der Medienliste werden alle Medien angezeigt, die dem Apparat hinzugefügt wurden. Hier können die Medien bearbeitet, gelöscht oder hinzugefügt werden. - -Wurde ein Apparat ausgewählt, werden einige Felder unterhalb der Medienliste angezeigt: -![Optionen der Medienliste](images/medialist_options.png) - - -Standardmäßig werden nur Medien angezeigt, die nicht aus dem Apparat entfernt wurden. Über den Checkbox "gel. Medien anzeigen" werden auch gelöschte Medien angezeigt. -Der Knopf "im Apparat?" kann für ein einzelnes, oder mehrere Medien verwendet werden, um zu prüfen, ob die ausgewählten Medien inzwischen dem Apparat hinzugefügt wurden. - -Unter der Liste befindet sich ein Knopf "Medien hinzufügen", der es ermöglicht, Medien manuell hinzuzufügen. Hierbei wird ein Dialog geöffnet, in dem die Signaturen der Medien eingetragen werden können. Die Medien werden dann dem Apparat hinzugefügt. - -### Kontextmenü - -Mit einem Rechtsklick auf ein Medium wird ein Kontextmenü geöffnet, das folgende Optionen enthält (Mehrfachauswahl der Medien mit Strg + Linksklick möglich): - -![Kontextmenü](images/media_context_menu_root.png) - -#### Subbereich Allgemeines - -![Kontextmenü](images/media_context_menu_general.png) -- Bearbeiten: Öffnet ein Fenster, in dem die Metadaten des Mediums eingesehen bzw, bearbeitet werden können. (s. [Metadaten bearbeiten](edit_media.md)) -- Löschen: Löscht das Medium aus dem Apparat. Das Medium wird nicht gelöscht, sondern nur aus dem Apparat entfernt. (s. [Bild](images.md#Exemplar löschen)) - -#### Subbereich Apparate - -![Kontextmenü](images/media_context_menu_apparats.png) - - -- Zum Apparat hinzufügen: *Noch nicht implementiert* (derzeit deaktiviert) Fügt das Medium dem Apparat in aDIS hinzu -- In Apparat verschieben: Öffnet ein Fenster, in dem ein anderer Apparat ausgewählt werden kann, in den die ausgewählten Medien verschoben werden sollen. -- In Apparat kopieren: Öffnet ein Fenster, in dem ein anderer Apparat ausgewählt werden kann, in den die ausgewählten Medien kopiert werden sollen. - - -## Medien hinzufügen - -![Medien hinzufügen](images/add_media.png) - -Um Medien hinzuzufügen, müssen die Signaturen der Medien in das Textfeld eingetragen werden. Jede Signatur muss in die Zeile eingegeben werden und mit Enter bestätigt werden. -Nachdem alle Signaturen hinzugefügt werden, können folgende Optionen gesetzt werden: - -- Modus: Gibt an, welche Metadaten verwendet werden. Die beiliegende Tabelle gibt an, welche Metadaten welche Angaben enthalten. -- Jedes Buch verwenden: Diese Option ermöglicht es, Medien hinzuzufügen, die noch nicht im Apparat sind -- Exakte Signatur: Diese Option teilt dem System mit, dass genau diese Signatur verwendet werden muss. Ist diese Option nicht gesetzt, wird nach der Signatur gesucht, die am ehesten der eingegebenen Signatur entspricht. (Das gefundene Buch ist das gleiche, nur evtl. ein anderes Exemplar) - -Mit dem Knopf "Ok" werden die Medien gesucht und hinzugefügt. diff --git a/docs/edit_media.md b/docs/edit_media.md deleted file mode 100644 index 0952f12..0000000 --- a/docs/edit_media.md +++ /dev/null @@ -1,7 +0,0 @@ -# Metadaten bearbeiten - -![Metadaten bearbeiten](images/edit_book_metadata.png) - -In diesem Fenster können die Metadaten eines Mediums bearbeitet werden. Diese bearbeitung macht Sinn, wenn Angaben nicht direkt aus dem Katalog übernommen werden konnten, oder wenn die Angaben nicht korrekt sind. - -Über den Knopf "Ok" werden die geänderten Metadaten gespeichert und das Fenster geschlossen. Über den Knopf "Abbrechen" werden die Änderungen verworfen und das Fenster geschlossen. diff --git a/docs/elsa.md b/docs/elsa.md deleted file mode 100644 index e6bfbaa..0000000 --- a/docs/elsa.md +++ /dev/null @@ -1,38 +0,0 @@ -# ELSA - -![ELSA](images/elsa_main.png) - -## ELSA anlegen -Um einen ELSA zu erstellen, muss der Knopf **Neuer Auftrag** gedrückt werden. Das Feld *Auftragsdaten* wird zum bearbeiten freigeschaltet, der Fokus wird automatisch auf das Feld **Prof.** gesetzt. - -Hier werden automatisch alle bereits vorhandenen Professoren der Semesterapparate eingetragen. Sollte der Professor noch keinen Apparat haben, so kann der Name manuell eingetragen werden. Es wird nun ein neues Element angezeigt: - -![Kontaktdaten](images/new_prof_elsa_fields.png) - -Solange diese Felder nicht ausgefüllt sind, kann der Auftrag nicht gespeichert werden. - - - -Um den ELSA zu speichern, müssen alle Felder ausgefüllt sein. Nach dem Speichern wird der ELSA in der Tabelle angezeigt. Über das Icon ![Icon](images/icon_cal.png) können der aktuelle Tag und das aktuelle Semester eingetragen werden. - -### Dokumente hinzufügen -![Dokumenttabelle](images/elsa_docs.png) - -Hat der Professor ein passendes Formular geliefert, so kann dieses über den Knopf **Dokument hinzufügen** hinzugefügt werden. Das Dokument wird in der Datenbank gespeichert und kann über den Knopf **Dokument öffnen** geöffnet werden. Über den Knopf **Medien aus Dokument hinzufügen** werden alle erkannten Medien aus dem Dokument in die Tabelle eingetragen und können zitiert werden. - -Sollte der Professor mehrere Segmente aus einem Medium in einer Zeile gelistet haben, so können diese als seperate Einträge hinzugefügt werden. Dazu muss ein Haken bei **Abschnitte trennen** gesetzt sein. - -!!! Warn "Hinweis: Datenformat im Dokument" - Um die Abschnitte erfolgreich zu trennen, müssen diese durch ein Semikolon getrennt sein. - - Beispiel: `1-5; 18-25; 30-35` - - - -Durch den Klick auf den Knopf **Medien aus Dokument hinzufügen** wird der Auftrag automatisch gespeichert, die Medien, bzw Abschnitte werden in der Tabelle angezeigt. - -### Einträge zitieren - -Da alle gescannten Dokumente später auf Illias hochgeladen werden, gibt es die Funktion **Eintrag zitieren**. Für diese Funktion muss ein Eintrag in der Tabelle ausgewählt werden. Über den Knopf **Eintrag zitieren** wird ein Dialog geöffnet, in dem der Eintrag zitiert werden kann. Die Angegebene Seitenzahl wird automatisch übernommen. -Genauere Beschreibung: [Zitieren](citing.md) - diff --git a/docs/elsa/anlegen.md b/docs/elsa/anlegen.md index f24ec9f..d598124 100644 --- a/docs/elsa/anlegen.md +++ b/docs/elsa/anlegen.md @@ -34,7 +34,7 @@ Im Feld **Prof.** werden automatisch alle bereits vorhandenen Dozenten aus den S ## Schnelleingabe -Über das Kalender-Icon ![Kalender](../images/icon_cal.png){ .twemoji } können automatisch eingetragen werden: +Über das Kalender-Icon :octicons-calendar-24: können automatisch eingetragen werden: - [x] Aktuelles Datum - [x] Aktuelles Semester @@ -83,4 +83,4 @@ Die erkannten Medien erscheinen in der Tabelle und können: ## Dokument öffnen -Über **Dokument öffnen** kann das hinzugefügte Formular jederzeit eingesehen werden. +Über **Dokument öffnen** kann das hinzugefügte Formular jederzeit eingesehen werden. Es wird hierfür eine temporäre Datei erstellt und im entsprechenden Program (Bspw: Word, Excel, Email) geöffnet. diff --git a/docs/extend.md b/docs/extend.md deleted file mode 100644 index abe685b..0000000 --- a/docs/extend.md +++ /dev/null @@ -1,12 +0,0 @@ -# Verlängerung - -Ein Dialog zum Verlängern eines Apparates. - -![Verlängerung](images/extend.png) - -Zum Verlängern muss ein Semester ausgewählt, und ein Jahr eingetragen sein. Die Checkbox für den Dauerapparat kann angekreuzt werden, um den Apparat als Dauerapparat zu markieren. - -!!! Info "Info Dauerapparat" - Damit der Apparat als Dauerapparat verlängert werden kann, muss ein Semester angegeben werden. - -Nach dem Speichern wird das Semester automatisch angepasst und in den entsprechenden Tabellen angezeigt. \ No newline at end of file diff --git a/docs/images.md b/docs/images.md deleted file mode 100644 index e49be0f..0000000 --- a/docs/images.md +++ /dev/null @@ -1,14 +0,0 @@ -# Bilder - -## Admin Aktionen -![Bild](images/actions.png) - -## Apparatscheckliste -![Bild](images/checklist.png) - -## Medien hinzufügen -![Bild](images/add_media.png) - -## Kalendar -![Bild](images/calendar.png) - diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 0e766af..0000000 --- a/docs/index.md +++ /dev/null @@ -1,93 +0,0 @@ -# SemesterapparatsManager - -Willkommen zur Dokumentation des **SemesterapparatsManager**! - -![Hauptoberfläche](images/mainUI.png){ loading=lazy } - -Der SemesterapparatsManager ist ein grafisches Werkzeug zur Verwaltung von Semesterapparaten an der Pädagogischen Hochschule Freiburg. Die Anwendung ermöglicht eine benutzerfreundliche Verwaltung von physischen und digitalen Semesterapparaten. - ---- - -## :lucide-rocket: Schnellstart - -
- -- :lucide-download:{ .lg .middle } **Installation** - - --- - - Installieren Sie den SemesterapparatsManager in wenigen Schritten. - - [:octicons-arrow-right-24: Zur Installation](allgemein/installation.md) - -- :lucide-layout-dashboard:{ .lg .middle } **Hauptoberfläche** - - --- - - Lernen Sie die Benutzeroberfläche kennen. - - [:octicons-arrow-right-24: Zur Übersicht](allgemein/hauptoberflaeche.md) - -- :lucide-book-open:{ .lg .middle } **Semesterapparat anlegen** - - --- - - Erstellen Sie Ihren ersten Semesterapparat. - - [:octicons-arrow-right-24: Zum Tutorial](semesterapparat/anlegen.md) - -- :lucide-file-text:{ .lg .middle } **ELSA** - - --- - - Elektronische Semesterapparate verwalten. - - [:octicons-arrow-right-24: Zu ELSA](elsa/anlegen.md) - -
- ---- - -## :lucide-folder-tree: Dokumentationsstruktur - -Diese Dokumentation ist in folgende Bereiche unterteilt: - -| Bereich | Beschreibung | -|---------|--------------| -| **[Allgemein](allgemein/index.md)** | Grundlegende Informationen, Installation und Übersicht der Hauptoberfläche | -| **[Semesterapparat](semesterapparat/index.md)** | Anlegen, Verlängern, Löschen und Suchen von Semesterapparaten | -| **[ELSA](elsa/index.md)** | Elektronische Semesterapparate anlegen und Zitate erstellen | -| **[Sonstiges](sonstiges/index.md)** | Zusätzliche Informationen und Bildmaterial | - ---- - -## :lucide-sparkles: Features - -
- -!!! success "Automatisierung" - - Automatisches Auslesen von Antragsformularen - - Automatische Zitat-Erstellung via Zotero - - E-Mail-Benachrichtigungen an Dozenten - -!!! info "Verwaltung" - - Zentrale Datenbank für alle Apparate - - Statistiken und Auswertungen - - Such- und Filterfunktionen - -!!! tip "Integration" - - Anbindung an den Bibliothekskatalog - - Zotero-Integration für Zitate - - Mail-System für Kommunikation - -!!! example "Benutzerfreundlich" - - Intuitive grafische Oberfläche - - Keyboard-Shortcuts - - Kontextmenüs für schnellen Zugriff - -
- ---- - -!!! tip "Tipp: Suche" - Nutzen Sie die **Suchfunktion** oben rechts ++ctrl+k++, um schnell zu den gewünschten Informationen zu gelangen. diff --git a/docs/mail.md b/docs/mail.md deleted file mode 100644 index 44ae0c0..0000000 --- a/docs/mail.md +++ /dev/null @@ -1,47 +0,0 @@ -# Mails - -Der SemesterapparatsManager hat die Option, den Dozenten beim erstellen eines Apparates eine Mail zu schicken. Diese Mail enthält die Information, dass der Apparat erstellt wurde und wie er aufgerufen werden kann. - -Zusätzlich kann beim Löschen eines Apparates eine Mail an den Dozenten geschickt werden. Diese Mail enthält die Information, dass der Apparat gelöscht wurde. - -Über eine neue Oberfläche können neue Mail Templates erstellt werden. - - -## Mail - -Abhängig davon, ob ein Apparat erstellt oder gelöscht wird/wurde, wird automatisch ein Template geladen. Somit muss nur noch die Anrede und der Text angepasst werden. - -Bsp: - -![Mail](images/mail_delete.png) - -!!! Info - Die Felder **eMail** und **Prof** sind schreibgeschützt und können nicht verändert werden. - -Über das Dropdown Menü **Art** kann das entsprechende Template ausgewählt werden. Der Knopf recht danaben kann für das [erstellen](#neue-mail-templates) neuer Templates genutzt werden. - -Um die Mail abschicken zu können, muss die Anrede mithilfe der Knöpfe über dem Mailtext konfiguriert werden. Die Anrede passt sich automatisch an, basierend auf der angegebenen Anrede. - -Wird die Mail erfolgreich verschickt, schließt sich das Fenster automatisch, und es wird eine Kopie der Mail an die Mail semesterapparate@ph-freiburg.de geschickt. - -## Neue Mail Templates - -Über den Knopf rechts neben der Auswahl der Email Templates können neue Templates erstellt werden. - -![MailTemplate](images/mail_create_template.png) - -Diese Oberfläche bietet die Möglichkeit, ein neues Template zu erstellen. Mithilfe des Dropdowns **Platzhalter** können Platzhalter ausgewählt und eingefügt werden. Bevor das Template gespeichert werden kann, muss dieses getestet werden. Sollte der Test erfolgreich sein, kann ein Name vergeben werden, das Template wird nun in dem Dropdown angezeigt. - -### Template testen -Sollten Angaben fehlen, wird eine Fehlermeldung angezeigt: - -![TemplateTest](images/mail_create_missing.png) - -!!! info - Die Fehlermeldung ist dynamisch und gibt immer die fehlenden Angaben an. - - -### Template speichern -Ist das Template fehlerfrei, wird folgender Dialog angezeigt: - -![TemplateSave](images/mail_set_template_name.png) \ No newline at end of file diff --git a/docs/mainUI.md b/docs/mainUI.md deleted file mode 100644 index 9a1b480..0000000 --- a/docs/mainUI.md +++ /dev/null @@ -1,27 +0,0 @@ -# Hauptoberfläche - -![Hauptoberfläche](images/mainUI.png) - -Die Hauptoberfläche des SemesterapparatsManager besteht aus drei Hauptbereichen: - -- **Anlegen**: Auf dieser Seite können neue Semesterapparate angelegt werden, bestehende Apparate aktualisiert oder gelöscht werden. Weitere Informationen siehe: [Anlegen](create.md) - -- **Suchen/Statistik**: Hier können Semesterapparate gesucht, gefiltert, gelöscht und verlängert werden. Zudem werden Statistiken zum erstellen / löschen von Semesterapparaten angezeigt. Ein zweiter Tab ermöglicht die Suche der Medien in den Semesterapparaten. Weitere Informationen siehe: [Suchen / Statistik](search.md) - -- **ELSA**: Hier können ELSA Apparate angelegt werden, und entsprechende Dateinamen, Beschreibungen und Zitate erstellt werden. Weitere Informationen siehe: [ELSA](elsa.md) - -## Übersichtstabelle - -![Übersichtstabelle](images/main_overview.png) - -In diesem Bereich werden alle erstellten Semesterapparate angezeigt. Über einen Doppelklick auf einen Apparat werden alle Details geladen und in den Apparatdetails angezeigt. Hier können dann auch einige Angaben geändert werden. -Weitere Infos siehe: [Anlegen](create.md) - -### Knöpfe -Dieser Bereich hat folgende Knöpfe: - -- **Übersicht erstellen**: Erstellt eine Übersicht der angezeigten Apparate, welche am Regal ausgehängt werden kann. Diese Übersicht wird per Mail an den Drucker geschickt. Vor dem Drucken erfolgt einen Bestätigungsabfrage. -- **neu. App anlegen**: Schaltet die Apparatdetails frei, um einen neuen Apparat anzulegen. Weiteres siehe: [Anlegen](create.md) -- **Auswahl abbrechen**: Entfernt alle Daten aus den Apparatsdetails und schaltet diese wieder auf inaktiv. - - diff --git a/docs/search.md b/docs/search.md deleted file mode 100644 index 191a1af..0000000 --- a/docs/search.md +++ /dev/null @@ -1,106 +0,0 @@ -# Suche und Statistik - -![Suche und Statistik](images/main_search_stat.png) - -Auf dieser Seite gibt es zwei Hauptfunktionen: die Suche und die Statistik. Standardmäßig wird die Statistik geöffnet. - - -## Statistikbereich - -### Suche -![Statistiksuche](images/ss_stat_search.png) - -In diesem Bereich kann die Suche nach Semesterapparaten durchgeführt werden. Suchoptionen sind: - -- **Appnr**: Die Nummer des Semesterapparates, die Auswahl zeigt alle belegten Semesterapparat an -- **Person**: Der Name des Dozenten, der den Semesterapparat erstellt hat -- **Fach**: Das Fach des Semesterapparates -- **Erstell-** und **Endsemester**: Semester, in denen der Semesterapparat erstellt wurde, bzw enden soll -- **Dauerapp**: Alle Apparate, die als Dauerapparat vermerkt sind -- **Löschbar**: Überschreibt alle vorhergehenden Parameter und zeigt alle Semesterapparate an, die gelöscht werden können - -!!! Info - Um alle Semesterapparate anzuzeigen, kann die Suche ohne Eingabe gestartet werden. - -Die Suche kann durch Klicken auf den Button **Suchen** gestartet werden. Die Ergebnisse werden in der Tabelle darunter angezeigt. - -### Suchergebnisse -!!! Info - Der Ergebnisbereich kann über den Vertikalen Slider verschoben werden, um mehr Platz für Tabelle, oder den Graphen zu schaffen. Hierzu mit der Maus auf den Raum zwischen den beiden Bereichen klicken und ziehen. -![Statistiksuchergebnisse](images/ss_stat_result.png) - -In diesem Bereich werden die Suchergebnisse angezeigt. Für jeden gefundenen Treffer wird eine Zeile angelegt: - -![Statistiksuchergebnisse](images/ss_search_entries.png) - -Angezeigt werden: - -- **Checkbox** -- **Apparatsname** -- **Apparatsnummer** -- **Person** -- **Fach** - -!!! failure "Info: Gelöschte Apparate" - Gelöschte Apparate werden in der Tabelle mit rotem Hintergrund angezeigt. (s. Ausgewählte Löschen) -Über der Tabelle sind zwei Knöpfe: **Ausgewählte Löschen** und **Ausgewählte Benachrichtigen** - -Um diese Aktionen auszuführen, muss mindestens eine Checkbox bei einem Apparat angekreuzt sein. - -#### Ausgewählte Löschen -![Statistiksuchergebnisse](images/ss_stat_delete.png) - -Nach dem Klicken auf den Button **Ausgewählte Löschen** wird jeder ausgewählte Apparat gelöscht. Die gelöschten Apparate werden in der Tabelle mit rotem Hintergrund angezeigt. - -#### Ausgewählte Benachrichtigen - -Mit dem Klick auf den Button wird ein neues Fenster geöffnet: - -![Statistiksuchergebnisse](images/mail_delete.png) - -Bevor die Mail abgeschickt werden kann, muss die Anrede konfiguriert werden. Weitere Infors finden Sie im Abschnitt [Mails](mail.md). - -#### Kontextmenü - -Diese Tabelle bietet auch ein Rechtsklickmenu mit folgenden Optionen: - -- **Verlängern**: Öffnet den [Verlängerungsdialog](extend.md) -- **Wiederherstellen**: Stellt einen gelöschten Apparat wieder her - -!!! Info "Info: Wiederherstellen" - Diese Option kann für einen oder mehrere gelöschte Apparate verwendet werden. Für mehrere Apprate müssen die entsprechenden Zeilen mit Strg+Klick auf die Zeilennummer markiert werden. - - -### Apparatsstatistik -Rechts neben der Tabelle wird die Statistik der gefundenen Apparate angezeigt: -![Statistiksuchergebnisse](images/ss_stats_table.png) -Hierbei werden die Angaben sowohl in einer Tabelle als auch in einem Diagramm dargestellt. -#### Tabelle -In der Tabelle werden alle Verwendeten Semester agegeben, in denen ein Apparat entweder erstellt oder gelöscht wurde. - -Über einen Doppelklick auf ein Semester werden die Apparate angezeigt, die in diesem Semester erstellt oder gelöscht wurden. - -![DetailedView](images/ss_stats_detailed.png) - -Ein Klick auf das `>` Symbol einer Person zeigt alle erstellten oder gelöschten Apparate der Person an. Ein Doppelklick auf den erstellten Apparat wechselt die Ansicht zur [Hauptoberfläche](mainUI.md) und zeigt alle Daten an. - -!!! Info "Info: Gelöschte Apparate" - Gelöschte Apparate können nicht angezeigt werden, die Doppelklick Funktion ist hier deaktiviert. - -#### Diagramm -Das Diagramm zeigt die Anzahl der erstellten und gelöschten Apparate in einem Liniendiagramm an. - -![Diagramm](images/ss_stats_graph.png) - -## Suchbereich - -Der Suchbereich kann verwendet werden, um zu prüfen, ob ein Exemplar in einem Apparat vorhanden ist. Mögliche Suchkriterien sind: - -- **Titel**: Der Titel des Exemplars (Trunkierung wurd automaticsh durchgeführt) -- **Signatur**: Die Signatur des Exemplars (Trunkierung wurd automaticsh durchgeführt) - -Über den Knopf **Suchen** wird die Suche gestartet. Die Ergebnisse werden in der Tabelle darunter angezeigt. - -!!! Info "Info: Exemplarsuche" - Im Vergleich zur Apparatssuche kann hier keine Leere Suche durchgeführt werden, da ggf. zu viele Ergebnisse angezeigt werden können. - diff --git a/docs/semesterapparat/anlegen.md b/docs/semesterapparat/anlegen.md index 9504c4a..45c1a3c 100644 --- a/docs/semesterapparat/anlegen.md +++ b/docs/semesterapparat/anlegen.md @@ -55,10 +55,11 @@ Dem Apparat können Dokumente hinzugefügt werden. Besonders hilfreich ist das H ```mermaid graph LR - A[Dokument hinzufügen] --> B{Antragsformular?} - B -->|Ja| C[Medien aus Dokument hinzufügen] + A[Dokument hinzufügen] --> B{Nächste Aktion} + B -->|"Daten [...] übernehmen"| C[Medien aus Dokument hinzufügen] B -->|Nein| D[Als Referenz speichern] C --> E[Medien werden automatisch erkannt] + ``` 1. Klicken Sie auf **Dokumente hinzufügen** diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index b5ab1e1..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,30 +0,0 @@ -site_name: SemesterapparatsManager -theme: - features: - - search.suggest - - search.highlight - name: material - icon: - admonition: - note: fontawesome/solid/note-sticky - abstract: fontawesome/solid/book - info: fontawesome/solid/circle-info - tip: fontawesome/solid/bullhorn - success: fontawesome/solid/check - question: fontawesome/solid/circle-question - warning: fontawesome/solid/triangle-exclamation - failure: fontawesome/solid/bomb - danger: fontawesome/solid/skull - bug: fontawesome/solid/robot - example: fontawesome/solid/flask - quote: fontawesome/solid/quote-left - -markdown_extensions: - - admonition - - pymdownx.details - - pymdownx.superfences - - tables -extra_css: - - stylesheets/extra.css -plugins: - - search \ No newline at end of file diff --git a/src/core/models.py b/src/core/models.py index a7d4688..cb233f3 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -38,13 +38,13 @@ class Prof: # add function that sets the data from a tuple def from_tuple(self, data: tuple[Union[str, int], ...]) -> "Prof": - setattr(self, "id", data[0]) - setattr(self, "_title", data[1]) - setattr(self, "firstname", data[2]) - setattr(self, "lastname", data[3]) - setattr(self, "fullname", data[4]) - setattr(self, "mail", data[5]) - setattr(self, "telnr", data[6]) + self.id = data[0] + self._title = data[1] + self.firstname = data[2] + self.lastname = data[3] + self.fullname = data[4] + self.mail = data[5] + self.telnr = data[6] return self def name(self, comma: bool = False) -> Optional[str]: @@ -78,7 +78,7 @@ class BookData: in_apparat: bool | None = False adis_idn: str | None = None old_book: Any | None = None - media_type: str | None = None # + media_type: str | None = None in_library: bool | None = None # whether the book is in the library or not medianr: int | None = None # Media number in the library system @@ -140,8 +140,7 @@ class BookData: def get_book_type(self) -> str: if "Online" in self.pages: return "eBook" - else: - return "Druckausgabe" + return "Druckausgabe" def from_string(self, data: str) -> "BookData": ndata = json.loads(data) @@ -264,8 +263,7 @@ class Apparat: def get_semester(self) -> Optional[str]: if self.extend_until is not None: return self.extend_until - else: - return self.created_semester + return self.created_semester @dataclass @@ -329,7 +327,7 @@ class Book: self.publisher == "", self.signature == "", self.internal_notes == "", - ] + ], ) def from_dict(self, data: dict[str, Any]): @@ -369,8 +367,12 @@ class SemapDocument: title_length = 0 title_max_length = 0 - def __post_init__(self): + def __post_init__(self) -> None: + """.""" self.title_suggestions = [] + self.phoneNumber = int( + regex.sub(r"[^\d]", "", str(self.phoneNumber)), + ) @property def nameSetter(self): @@ -389,7 +391,6 @@ class SemapDocument: self.title_suggestions.append(suggestion["shortened_string"]) else: self.title_suggestions = [] - pass @property def renameSemester(self) -> None: @@ -400,7 +401,7 @@ class SemapDocument: self.semester = Semester().from_string(self.semester) else: self.semester = Semester().from_string( - semester_converter(self.semester) + semester_converter(self.semester), ) @property diff --git a/src/ui/dialogs/dialog_sources/elsa_add_table_entry.ui b/src/ui/dialogs/dialog_sources/elsa_add_table_entry.ui index 882c722..55501df 100644 --- a/src/ui/dialogs/dialog_sources/elsa_add_table_entry.ui +++ b/src/ui/dialogs/dialog_sources/elsa_add_table_entry.ui @@ -669,7 +669,7 @@ Nachname, Vorname - + Kopieren @@ -763,7 +763,7 @@ Nachname, Vorname ilias_filename copy_filename copy_ilias_filename - copy_qoute + copy_quote retryButton diff --git a/src/ui/dialogs/elsa_add_entry.py b/src/ui/dialogs/elsa_add_entry.py index 17fc5b7..d66682e 100644 --- a/src/ui/dialogs/elsa_add_entry.py +++ b/src/ui/dialogs/elsa_add_entry.py @@ -18,13 +18,13 @@ class ElsaAddEntry(QtWidgets.QDialog, Ui_Dialog): self.setupUi(self) self.setWindowTitle("Eintrag zitieren") self.buttonBox.button( - QtWidgets.QDialogButtonBox.StandardButton.Ok + QtWidgets.QDialogButtonBox.StandardButton.Ok, ).clicked.connect(self.accept) self.buttonBox.button( - QtWidgets.QDialogButtonBox.StandardButton.Cancel + QtWidgets.QDialogButtonBox.StandardButton.Cancel, ).clicked.connect(self.close) self.buttonBox.button( - QtWidgets.QDialogButtonBox.StandardButton.Discard + QtWidgets.QDialogButtonBox.StandardButton.Discard, ).clicked.connect(self.discard) self.mode = None self.btn_search.clicked.connect(self.search) @@ -33,13 +33,13 @@ class ElsaAddEntry(QtWidgets.QDialog, Ui_Dialog): self.btn_hg.clicked.connect(self.stack) self.make_quote.clicked.connect(self.display_data) self.copy_filename.clicked.connect( - lambda: self.copy_to_clipboard(self.filename_edit) + lambda: self.copy_to_clipboard(self.filename_edit), ) self.copy_ilias_filename.clicked.connect( - lambda: self.copy_to_clipboard(self.ilias_filename) + lambda: self.copy_to_clipboard(self.ilias_filename), ) - self.copy_qoute.clicked.connect( - lambda: self.copy_to_clipboard(self.file_desc_edit) + self.copy_quote.clicked.connect( + lambda: self.copy_to_clipboard(self.file_desc_edit), ) self.setWindowIcon(Icon("edit").icon) self.stackedWidget.setEnabled(False) @@ -61,11 +61,11 @@ class ElsaAddEntry(QtWidgets.QDialog, Ui_Dialog): def check_pages(self): if self.source_pages: - if self.book_pages.text() != self.source_pages: - self.make_quote.setEnabled(True) - elif self.hg_pages.text() != self.source_pages: - self.make_quote.setEnabled(True) - elif self.zs_pages.text() != self.source_pages: + if ( + self.book_pages.text() != self.source_pages + or self.hg_pages.text() != self.source_pages + or self.zs_pages.text() != self.source_pages + ): self.make_quote.setEnabled(True) def copy_to_clipboard(self, field): diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index 3bf6da8..d59d931 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -1,4 +1,5 @@ -# encoding: utf-8 +from __future__ import annotations + import atexit import os import sys @@ -6,7 +7,7 @@ import tempfile import time import webbrowser from pathlib import Path -from typing import Any, List, Optional, Tuple, Union +from typing import Any, Optional, Union from natsort import natsorted from PySide6 import QtCore, QtGui, QtWidgets @@ -15,14 +16,13 @@ from PySide6.QtGui import QRegularExpressionValidator from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer from src import Icon -from src.database import Database from src.background import ( AvailChecker, BookGrabber, DocumentationThread, NewEditionCheckerThread, ) -from src.utils.files import recreateFile, delete_temp_contents as tempdelete +from src.core.constants import APP_NRS from src.core.models import ( Apparat, ApparatData, @@ -31,15 +31,15 @@ from src.core.models import ( SemapDocument, Semester, ) -from src.core.constants import APP_NRS -from src.parsers import ( - csv_to_list, -) +from src.database import Database from src.logic import ( eml_to_semap, pdf_to_semap, word_to_semap, ) +from src.parsers import ( + csv_to_list, +) from src.shared.logging import log from src.ui import Ui_Semesterapparat from src.ui.dialogs import ( @@ -71,6 +71,8 @@ from src.ui.widgets import ( UpdateSignatures, UserCreate, ) +from src.utils.files import delete_temp_contents as tempdelete +from src.utils.files import recreateFile log.success("UI started") valid_input = (0, 0, 0, 0, 0, 0) @@ -86,17 +88,17 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.MainWindow = MainWindow # type:ignore # set the window title MainWindow.setWindowTitle( - f"Semesterapparatsmanagement Semester: {Semester().value}" + f"Semesterapparatsmanagement Semester: {Semester().value}", ) # type:ignore MainWindow.setWindowIcon(Icon("logo").icon) # type:ignore self.db = Database() self.btn_add_document.clicked.connect(self.add_document) # type:ignore self.check_file.clicked.connect( # type:ignore - self.btn_check_file_threaded + self.btn_check_file_threaded, ) # default: self.add_media_from_file self.btn_extract_data_from_document.clicked.connect( # type:ignore - self.import_data_from_document + self.import_data_from_document, ) self.create_new_app.clicked.connect(self.btn_create_new_apparat) # type:ignore self.btn_apparat_save.clicked.connect(lambda: self.btn_save_apparat(True)) # type:ignore @@ -109,17 +111,17 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.calendarWidget = MessageCalendar(self.calendar_frame) self.calendarWidget.setGridVisible(True) self.calendarWidget.setVerticalHeaderFormat( - QtWidgets.QCalendarWidget.VerticalHeaderFormat.NoVerticalHeader + QtWidgets.QCalendarWidget.VerticalHeaderFormat.NoVerticalHeader, ) self.calendarWidget.setObjectName("MessageCalendar") self.calendarWidget.clicked.connect(self.open_reminder) # type:ignore # assign a context menu to the calendar self.calendarlayout.addWidget(self.calendarWidget) self.tableWidget_apparat_media.horizontalHeader().setSectionResizeMode( # type:ignore - QtWidgets.QHeaderView.ResizeMode.Stretch + QtWidgets.QHeaderView.ResizeMode.Stretch, ) self.tableWidget_apparate.horizontalHeader().setSectionResizeMode( # type:ignore - QtWidgets.QHeaderView.ResizeMode.Stretch + QtWidgets.QHeaderView.ResizeMode.Stretch, ) self.saveandcreate.hide() @@ -140,29 +142,30 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.prof_mail.setValidator( QRegularExpressionValidator( QtCore.QRegularExpression( - r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}" - ) - ) + r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", + ), + ), ) self.prof_tel_nr.setValidator(QtGui.QIntValidator()) self.prof_tel_nr.setValidator( - QtGui.QRegularExpressionValidator(QtCore.QRegularExpression(r"^\d{3,14}")) + QtGui.QRegularExpressionValidator(QtCore.QRegularExpression(r"^\d{3,14}")), ) # #log.debug(self.prof_tel_nr.maxLength()) self.app_fach.setValidator( # validator to allow typing in the app_fach field QtGui.QRegularExpressionValidator( - QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+") - ) + QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+"), + ), ) # allow only letters, numbers, whitespaces, symbols for the apparat name self.app_name.setValidator( QtGui.QRegularExpressionValidator( - QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+") - ) + QtCore.QRegularExpression(r"[a-zA-Z0-9\s\W]+"), + ), ) self.tableWidget_apparate.addScrollBarWidget( - QtWidgets.QScrollBar(), QtCore.Qt.AlignmentFlag.AlignRight + QtWidgets.QScrollBar(), + QtCore.Qt.AlignmentFlag.AlignRight, ) self.tableWidget_apparate.doubleClicked.connect(self.load_app_data) # type:ignore @@ -229,16 +232,16 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): # Context Menus self.tableWidget_apparate.setContextMenuPolicy( - QtCore.Qt.ContextMenuPolicy.CustomContextMenu + QtCore.Qt.ContextMenuPolicy.CustomContextMenu, ) self.tableWidget_apparat_media.setContextMenuPolicy( - QtCore.Qt.ContextMenuPolicy.CustomContextMenu + QtCore.Qt.ContextMenuPolicy.CustomContextMenu, ) self.tableWidget_apparate.customContextMenuRequested.connect( # type:ignore - self.open_context_menu # type:ignore + self.open_context_menu, # type:ignore ) self.tableWidget_apparat_media.customContextMenuRequested.connect( # type:ignore - self.media_context_menu # type:ignore + self.media_context_menu, # type:ignore ) # admin buttons @@ -252,7 +255,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.bookGrabber: list[QThread] = [] self.availChecker = None self.mail_thread = None - self.autoGrabber = None + self.auto_grabber = None self.newEditionChecker = NewEditionCheckerThread() self.elsatab.setLayout(QtWidgets.QVBoxLayout()) @@ -278,7 +281,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): # allow files to be dragged into document_list self.document_list.setDragDropMode( - QtWidgets.QAbstractItemView.DragDropMode.DropOnly + QtWidgets.QAbstractItemView.DragDropMode.DropOnly, ) self.document_list.setAcceptDrops(True) self.document_list.viewport().setAcceptDrops(True) @@ -295,24 +298,18 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): # 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 et == QtCore.QEvent.Type.DragEnter or 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.DragMove: - if event.mimeData().hasUrls(): - event.setDropAction(QtCore.Qt.CopyAction) - event.acceptProposedAction() - else: - event.ignore() - return True - elif et == QtCore.QEvent.Type.Drop: + if et == QtCore.QEvent.Type.Drop: if not self.app_group_box.isEnabled(): self.confirm_popup( - "Bitte öffnen Sie zuerst einen Apparat!", title="Fehler" + "Bitte öffnen Sie zuerst einen Apparat!", + title="Fehler", ) return True if event.mimeData().hasUrls(): @@ -474,15 +471,16 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): elsa_layout.addWidget(ElsaDialog()) # log.debug("added") - pass def generateSemester(self, today=False): """Generates the current semester. Args: today (bool, optional): If True, the current semester is generated. Defaults to False. + Returns: str: The current semester + """ if today: return Semester() @@ -492,8 +490,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): semester = "SoSe" if self.sem_sommer.isChecked() else "WiSe" if semester == "SoSe": return "SoSe " + str(currentYear) - else: - return f"WiSe {currentYear}/{currentYear + 1}" + return f"WiSe {currentYear}/{currentYear + 1}" def open_apparat(self, apparat: Union[int, str]): if self.load_app_data(apparat): @@ -647,7 +644,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): def validate_semester(self): valid = (self.sem_sommer.isChecked() or self.sem_winter.isChecked()) and len( - self.sem_year.text() + self.sem_year.text(), ) >= 2 if valid or self.check_eternal_app.isChecked(): self.__setValidState(self.valid_check_semester, 1, self._mand, 5) @@ -749,7 +746,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): def update_document_list(self): app_id = self.active_apparat prof_id = self.db.getProfByName( - self.drpdwn_prof_name.currentText().replace(",", "") + self.drpdwn_prof_name.currentText().replace(",", ""), ).id files = self.db.getFiles(app_id, prof_id) for file in files: @@ -827,7 +824,8 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.progress_label.setText("Bitte warten...") if data == []: self.confirm_popup( - "Bitte mindestens ein Medium hinzufügen!", title="Fehler" + "Bitte mindestens ein Medium hinzufügen!", + title="Fehler", ) app_nr = self.db.getId(self.app_name.text()) @@ -866,7 +864,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): def check_availability(self): def _update_progress(current, all_titles): - self.avail_status.setText("{}/{}".format(current, all_titles)) + self.avail_status.setText(f"{current}/{all_titles}") def _hide_progress_label(): self.label_20.hide() @@ -885,8 +883,9 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): else: links = [ self.tableWidget_apparat_media.item( - self.tableWidget_apparat_media.currentRow(), 1 - ).text() + self.tableWidget_apparat_media.currentRow(), + 1, + ).text(), ] # get the number of selected rows from the table items = self.tableWidget_apparat_media.rowCount() @@ -961,7 +960,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): # # #log.debug(bd, type(bd)) # create a new row below the last one self.tableWidget_apparat_media.insertRow( - self.tableWidget_apparat_media.rowCount() + self.tableWidget_apparat_media.rowCount(), ) # #set the data self.tableWidget_apparat_media.setItem( @@ -989,10 +988,12 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): label.setTextFormat(QtCore.Qt.TextFormat.RichText) label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) label.setTextInteractionFlags( - QtCore.Qt.TextInteractionFlag.TextBrowserInteraction + QtCore.Qt.TextInteractionFlag.TextBrowserInteraction, ) self.tableWidget_apparat_media.setCellWidget( - self.tableWidget_apparat_media.rowCount() - 1, 6, label + self.tableWidget_apparat_media.rowCount() - 1, + 6, + label, ) if availability == 1: # display green checkmark at column 4 in the row @@ -1003,7 +1004,8 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): ) # set tooltip self.tableWidget_apparat_media.item( - self.tableWidget_apparat_media.rowCount() - 1, 4 + self.tableWidget_apparat_media.rowCount() - 1, + 4, ).setToolTip("Das Medium wurde im Apparat gefunden") else: self.tableWidget_apparat_media.setItem( @@ -1012,21 +1014,21 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): QtWidgets.QTableWidgetItem("❌"), ) self.tableWidget_apparat_media.item( - self.tableWidget_apparat_media.rowCount() - 1, 4 + self.tableWidget_apparat_media.rowCount() - 1, + 4, ).setToolTip("Das Medium wurde nicht im Apparat gefunden") def open_link(self, item): def __openLink(link): if link == "": - return + return None if "http" not in link: link = "https://" + link return link - # # get the name of the column columnname = self.tableWidget_apparat_media.horizontalHeaderItem( - item.column() + item.column(), ).text() if columnname == "Link": link = __openLink(item.text()) @@ -1086,18 +1088,21 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): _selected_doc_filetype = "" try: _selected_doc_name = self.document_list.item( - self.document_list.currentRow(), 0 + self.document_list.currentRow(), + 0, ).text() _selected_doc_location = self.document_list.item( - self.document_list.currentRow(), 3 + self.document_list.currentRow(), + 3, ).text() _selected_doc_filetype = self.document_list.item( - self.document_list.currentRow(), 1 + self.document_list.currentRow(), + 1, ).text() except AttributeError: self.confirm_popup("Bitte erst ein Dokument auswählen!", title="Fehler") return - if not _selected_doc_location == "Database": + if _selected_doc_location != "Database": path = Path(_selected_doc_location) # path: Path = path.resolve() # path. @@ -1110,7 +1115,9 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): os.system(f"open {path}") else: recreateFile( - _selected_doc_name, self.active_apparat, filetype=_selected_doc_filetype + _selected_doc_name, + self.active_apparat, + filetype=_selected_doc_filetype, ) def add_media_from_file(self): @@ -1138,78 +1145,87 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): # if files are in the table, and are selected, check for books in the file if self.document_list.rowCount() == 0: return - else: - # if file is selected, check for books in the file - if self.document_list.currentRow() != -1: - # #log.debug("File selected") - file = self.document_list.item( - self.document_list.currentRow(), 3 - ).text() + # if file is selected, check for books in the file + if self.document_list.currentRow() != -1: + # #log.debug("File selected") + file = self.document_list.item( + self.document_list.currentRow(), + 3, + ).text() - file_type = self.document_list.item( - self.document_list.currentRow(), 1 - ).text() - file_location = self.document_list.item( - self.document_list.currentRow(), 3 - ).text() - file_name = self.document_list.item( - self.document_list.currentRow(), 0 - ).text() - if file_location == "Database": - # create a temporaty file to use, delete it after use - temp_file = tempfile.NamedTemporaryFile( - delete=False, suffix="." + file_type + file_type = self.document_list.item( + self.document_list.currentRow(), + 1, + ).text() + file_location = self.document_list.item( + self.document_list.currentRow(), + 3, + ).text() + file_name = self.document_list.item( + self.document_list.currentRow(), + 0, + ).text() + if file_location == "Database": + # create a temporaty file to use, delete it after use + temp_file = tempfile.NamedTemporaryFile( + delete=False, + suffix="." + file_type, + ) + temp_file.write(self.db.getBlob(file_name, int(app_id))) + temp_file.close() + file = temp_file.name + if file_type == "pdf": + data = pdf_to_semap(file) + signatures = data.signatures + data = __open_dialog(signatures) + # if no data was returned, 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, ) - temp_file.write(self.db.getBlob(file_name, int(app_id))) - temp_file.close() - file = temp_file.name - if file_type == "pdf": - data = pdf_to_semap(file) - signatures = data.signatures - data = __open_dialog(signatures) - # if no data was returned, 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": - signatures = csv_to_list(file) - data = __open_dialog(signatures) - # add the data to the database - 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 == "docx": - data = word_to_semap(file) - signatures = data.signatures - data = __open_dialog(signatures) - # if no data was returned, 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 == "eml": - data = eml_to_semap(file) - self.update_app_media_list() - # #log.debug(len(signatures)) + if file_type == "csv": + signatures = csv_to_list(file) + data = __open_dialog(signatures) + # add the data to the database + 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 == "docx": + data = word_to_semap(file) + signatures = data.signatures + data = __open_dialog(signatures) + # if no data was returned, 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 == "eml": + data = eml_to_semap(file) + self.update_app_media_list() + # #log.debug(len(signatures)) - def extract_document_data(self) -> Union[list[str], SemapDocument]: + def extract_document_data(self) -> list[str] | SemapDocument: file_type = self.document_list.item(self.document_list.currentRow(), 1).text() file_location = self.document_list.item( - self.document_list.currentRow(), 3 + self.document_list.currentRow(), + 3, ).text() file_name = self.document_list.item(self.document_list.currentRow(), 0).text() file = file_location @@ -1217,10 +1233,12 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): if file_location == "Database": # create warning, then return file = self.db.recreateFile( - file_name, self.active_apparat, filetype=file_type + file_name, + self.active_apparat, + filetype=file_type, ) if file_type == "pdf": - # Todo: implement parser here + # TODO: implement parser here self.confirm_popup("PDF Dateien werden nicht unterstützt!", title="Fehler") return [""] if file_type == "csv": @@ -1233,8 +1251,14 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): log.debug("Got the data: {}", data) return data - else: - raise ValueError("Dateityp wird nicht unterstützt") + if file_type == "eml": + data = eml_to_semap(file) + log.info("Converted data from eml file") + log.debug("Got the data: {}", data) + + return data + error = "Dateityp wird nicht unterstützt" + raise ValueError(error) def import_data_from_document(self): global valid_input @@ -1254,7 +1278,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): dialog.setModal(True) layout = QtWidgets.QVBoxLayout() label = QtWidgets.QLabel( - f"Bitte wählen Sie einen Titel aus:/nDer Titel darf max. {data.title_max_length} Zeichen lang sein." + f"Bitte wählen Sie einen Titel aus:/nDer Titel darf max. {data.title_max_length} Zeichen lang sein.", ) layout.addWidget(label) dropdown = QtWidgets.QComboBox() @@ -1264,7 +1288,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): button_box = QtWidgets.QDialogButtonBox() button_box.setStandardButtons( QtWidgets.QDialogButtonBox.StandardButton.Cancel - | QtWidgets.QDialogButtonBox.StandardButton.Ok + | QtWidgets.QDialogButtonBox.StandardButton.Ok, ) button_box.accepted.connect(dialog.accept) button_box.rejected.connect(dialog.reject) @@ -1306,7 +1330,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): # get active app_id and prof_id self.tableWidget_apparate.setEnabled(False) self.tableWidget_apparate.setToolTip( - "Bitte warten, bis alle Medien hinzugefügt wurden" + "Bitte warten, bis alle Medien hinzugefügt wurden", ) app_id = self.active_apparat log.debug(self.profdata) @@ -1336,50 +1360,48 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.tableWidget_apparate.setEnabled(True) self.tableWidget_apparate.setToolTip("") return + # if file is selected, check for books in the file + # #log.debug("File selected") + + if prof_id is None: + prof_id = self.db.getProfId(self.profdata) + + # log.debug("Prof ID is None", prof_id) + document = None + + if c_document is None or not isinstance(c_document, SemapDocument): + document = self.extract_document_data() + if document is None: + log.error("Document is None") + elif isinstance(document, SemapDocument): + signatures = document.signatures else: - # if file is selected, check for books in the file - # #log.debug("File selected") + signatures = document + if isinstance(signatures, SemapDocument): + signatures = signatures.signatures + auto_grabber = BookGrabber() + auto_grabber.add_values( + mode="ARRAY", + app_id=app_id, + prof_id=int(prof_id), + data=signatures, + any_book=True, + exact=True, + ) + self.label_info.show() + self.progress_label.show() + self.line_2.show() + auto_grabber.finished.connect(self.hide_progress_label) + auto_grabber.finished.connect(self.unlock_apparate) + auto_grabber.updateSignal.connect(self.update_progress_label) - if prof_id is None: - prof_id = self.db.getProfId(self.profdata) - - # log.debug("Prof ID is None", prof_id) - document = None - - if c_document is None or not isinstance(c_document, SemapDocument): - document = self.extract_document_data() - if document is None: - log.error("Document is None") - elif isinstance(document, SemapDocument): - signatures = document.signatures - else: - signatures = document - autoGrabber = BookGrabber() - autoGrabber.add_values( - mode="ARRAY", - app_id=app_id, - prof_id=int(prof_id), - data=signatures, - any_book=True, - exact=True, - ) - self.label_info.show() - self.progress_label.show() - self.line_2.show() - # grabber.finished.connect(thread.quit) - # self.autoGrabber.finished.connect(self.autoGrabber.deleteLater) - autoGrabber.finished.connect(self.hide_progress_label) - autoGrabber.finished.connect(self.unlock_apparate) - autoGrabber.updateSignal.connect(self.update_progress_label) - # worker.finished.connect(worker.deleteLater) - - autoGrabber.start() - self.bookGrabber.append(autoGrabber) - # refresh book table - # end of thread - # self.autoGrabber.exit() - # self.__clear_fields() - # self.btn_cancel_active_selection() + auto_grabber.start() + self.bookGrabber.append(auto_grabber) + # refresh book table + # end of thread + # self.auto_grabber.exit() + # self.__clear_fields() + # self.btn_cancel_active_selection() def unlock_apparate(self): self.tableWidget_apparate.setEnabled(True) @@ -1442,12 +1464,12 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.add_files() if error is not None: self.confirm_popup(error.__str__(), title="Fehler") - return + return None appdata = self.db.getAllAparats() # merge self.appdata and appdata, remove duplicates 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.appnr, reverse=True) self.update_apparat_list() @@ -1465,7 +1487,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): self.__clear_fields() return True - def __uniques(self, list1, list2): + def __uniques(self, list1: list[Apparat], list2: list[Apparat]) -> list[Apparat]: seen = set() unique_list = [] for item in list1 + list2: @@ -1475,7 +1497,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): unique_list.append(item) return unique_list - def send_mail_preview(self): + def send_mail_preview(self) -> None: pass @property @@ -1491,14 +1513,14 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): "prof_tel": self.prof_tel_nr.text(), } - def add_files(self): - """ - Add Files to the associated prof in the database + def add_files(self) -> None: + """Add Files to the associated prof in the database. Parameters ---------- prof_id : int, optional The ID associated to the prof, by default None + """ files: list[dict[str, Any]] = [] for i in range(self.document_list.rowCount()): @@ -1508,7 +1530,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): "type": self.document_list.item(i, 1).text(), "date": self.document_list.item(i, 2).text(), "path": self.document_list.item(i, 3).text(), - } + }, ) self.document_list.item(i, 2).setText("") @@ -1521,7 +1543,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): "profname": self.drpdwn_prof_name.currentText(), "prof_mail": self.prof_mail.text(), "prof_tel": self.prof_tel_nr.text(), - } + }, ), ) @@ -1530,7 +1552,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): for apparat in self.apparats: self.insert_apparat_into_table(apparat) - log.info("Inserted {} apparats into table".format(len(self.apparats))) + log.info(f"Inserted {len(self.apparats)} apparats into table") def insert_apparat_into_table(self, apparat: Apparat): # log.debug(apparat) @@ -1544,16 +1566,20 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): ) self.tableWidget_apparate.insertRow(0) self.tableWidget_apparate.setItem( - 0, 0, QtWidgets.QTableWidgetItem(str(apparat.appnr)) + 0, + 0, + QtWidgets.QTableWidgetItem(str(apparat.appnr)), ) self.tableWidget_apparate.setItem( - 0, 1, QtWidgets.QTableWidgetItem(str(apparat.name)) + 0, + 1, + QtWidgets.QTableWidgetItem(str(apparat.name)), ) self.tableWidget_apparate.setItem( 0, 2, QtWidgets.QTableWidgetItem( - self.db.getProfNameById(apparat.prof_id, add_title=False) + self.db.getProfNameById(apparat.prof_id, add_title=False), ), ) self.tableWidget_apparate.setItem( @@ -1562,10 +1588,14 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): QtWidgets.QTableWidgetItem(str(semester)), ) self.tableWidget_apparate.setItem( - 0, 4, QtWidgets.QTableWidgetItem(__dauer_check(apparat)) + 0, + 4, + QtWidgets.QTableWidgetItem(__dauer_check(apparat)), ) self.tableWidget_apparate.setItem( - 0, 5, QtWidgets.QTableWidgetItem(str(apparat.konto)) + 0, + 5, + QtWidgets.QTableWidgetItem(str(apparat.konto)), ) def open_context_menu(self, position): @@ -1584,7 +1614,7 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): remind_action, new_edition_check, order_newedition_action, - ] + ], ) # convert point to row and column row = self.tableWidget_apparate.rowAt(position.y()) @@ -1599,12 +1629,12 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): 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) + lambda: self.order_new_editions(app_id, pid), ) delete_action.triggered.connect(lambda: self.delete_apparat(pos)) # pass pos to contact_prof contact_action.triggered.connect( - lambda: self.contact_prof(pid=pid, apparat=app_id) + lambda: self.contact_prof(pid=pid, apparat=app_id), ) menu.exec(self.tableWidget_apparate.mapToGlobal(position)) @@ -1641,20 +1671,23 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat): pick = selector.selection app_id = self.tableWidget_apparate.item( - self.tableWidget_apparate.currentRow(), 0 + 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 + self.tableWidget_apparate.currentRow(), + 1, ).text() subject = self.tableWidget_apparate.item( - self.tableWidget_apparate.currentRow(), 4 + 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)] + [str(app.appnr) for app in self.db.getApparatsByProf(prof_id)], ) else: apparats_id = self.db.getId(app_name) @@ -1702,10 +1735,9 @@ WHERE m.id = ?""", 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.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"]), @@ -1799,7 +1831,7 @@ WHERE m.id = ?""", apparat_copy_action, apparat_move_action, replace_old_editions, - ] + ], ) generalmenu.addActions([edit_action, delete_action, update_data_action]) # type: ignore # disable apparat_add_action @@ -1848,7 +1880,7 @@ WHERE m.id = ?""", ] # 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]] = [] + books: list[tuple[int, BookData]] = [] for signature in signatures: book = self.db.getBookBasedOnSignature( app_id=app_id, @@ -1894,7 +1926,7 @@ WHERE m.id = ?""", ) signatures.append(book_id) # type: ignore result, apparat = self.confirm_action_dialog( # type: ignore - "In welchen Apparat sollen die Medien kopiert werden?" + "In welchen Apparat sollen die Medien kopiert werden?", ) if result == 1: for book_id in signatures: @@ -1914,7 +1946,7 @@ WHERE m.id = ?""", ) signatures.append(book_id) result, apparat = self.confirm_action_dialog( - "In welchen Apparat sollen die Medien verschoben werden?" + "In welchen Apparat sollen die Medien verschoben werden?", ) if result == 1: for book_id in signatures: @@ -1957,13 +1989,14 @@ WHERE m.id = ?""", dialog.setLayout(layout) return dialog.exec(), self.db.getApparatId( - self.db.getApparatNameByAppNr(drpdwn.currentText()) + self.db.getApparatNameByAppNr(drpdwn.currentText()), ) def add_to_apparat(self): - """use playwright in background to add medium to apparat""" + """Use playwright in background to add medium to apparat""" signature = self.tableWidget_apparat_media.item( - self.tableWidget_apparat_media.currentRow(), 1 + self.tableWidget_apparat_media.currentRow(), + 1, ).text() self.db.getBookBasedOnSignature( self.drpdwn_app_nr.currentText(), @@ -1974,7 +2007,8 @@ WHERE m.id = ?""", def edit_medium(self): book = self.tableWidget_apparat_media.item( - self.tableWidget_apparat_media.currentRow(), 1 + self.tableWidget_apparat_media.currentRow(), + 1, ).text() prof_id = self.db.getProfId(self.profdata) data = self.db.getBookBasedOnSignature( @@ -2004,7 +2038,6 @@ WHERE m.id = ?""", self.update_app_media_list() else: return - pass def delete_medium(self): selected_apparat_id = self.active_apparat @@ -2013,7 +2046,8 @@ WHERE m.id = ?""", selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() if len(selected_rows) == 1: signature = self.tableWidget_apparat_media.item( - self.tableWidget_apparat_media.currentRow(), 1 + self.tableWidget_apparat_media.currentRow(), + 1, ).text() book_id = self.db.getBookIdBasedOnSignature( selected_apparat_id, @@ -2026,7 +2060,6 @@ WHERE m.id = ?""", if state == 1: self.db.deleteBook(book_id) self.update_app_media_list() - pass else: # get all selected rows ranges = self.tableWidget_apparat_media.selectedRanges() @@ -2057,12 +2090,15 @@ WHERE m.id = ?""", # #log.debug(data) # return data selected_apparat_id = self.tableWidget_apparate.item( - self.tableWidget_apparate.currentRow(), 0 + self.tableWidget_apparate.currentRow(), + 0, ).text() # #log.debug(selected_apparat_id) self.db.setNewSemesterDate( - selected_apparat_id, data["semester"], dauerapp=data["dauerapp"] + selected_apparat_id, + data["semester"], + dauerapp=data["dauerapp"], ) # update the table self.get_apparats() @@ -2079,9 +2115,7 @@ WHERE m.id = ?""", app_id="", ): log.debug( - "Got these values apparat: {}, location: {}, mail: {}, pid: {}, accepted_books: {}, app_id: {}".format( - apparat, location, mail, pid, accepted_books, app_id - ) + f"Got these values apparat: {apparat}, location: {location}, mail: {mail}, pid: {pid}, accepted_books: {accepted_books}, app_id: {app_id}", ) active_apparat_id = ( @@ -2126,7 +2160,8 @@ WHERE m.id = ?""", if self.active_apparat == "": if apparat is False: self.confirm_popup( - "Bitte erst einen Apparat auswählen!", title="Apparat auswählen" + "Bitte erst einen Apparat auswählen!", + title="Apparat auswählen", ) return @@ -2134,7 +2169,8 @@ WHERE m.id = ?""", def delete_apparat(self, position): selected_apparat_id = self.tableWidget_apparate.item( - self.tableWidget_apparate.currentRow(), 0 + self.tableWidget_apparate.currentRow(), + 0, ).text() message = f"Soll der Apparat {selected_apparat_id} wirklich gelöscht werden?" state = self.confirm_popup(message, title="Löschen?") @@ -2146,7 +2182,8 @@ WHERE m.id = ?""", apparat = Apparat( appnr=int(selected_apparat_id), name=self.tableWidget_apparate.item( - self.tableWidget_apparate.currentRow(), 1 + self.tableWidget_apparate.currentRow(), + 1, ).text(), ) self.db.deleteApparat(apparat=apparat, semester=Semester().value) @@ -2189,7 +2226,7 @@ def launch_gui(): MainWindow.show() # atexit.register() app.aboutToQuit.connect( - aui.validate_thread.quit + aui.validate_thread.quit, ) # if that thread uses an event loop app.aboutToQuit.connect(aui.docu.terminate) # our new slot app.aboutToQuit.connect(aui.docu.wait) diff --git a/src/ui/widgets/welcome_wizard.py b/src/ui/widgets/welcome_wizard.py index c9a1700..54f9231 100644 --- a/src/ui/widgets/welcome_wizard.py +++ b/src/ui/widgets/welcome_wizard.py @@ -30,38 +30,41 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): # allow user to toggle password visibility self.settings_mail_password.setContextMenuPolicy( - QtCore.Qt.ContextMenuPolicy.CustomContextMenu + QtCore.Qt.ContextMenuPolicy.CustomContextMenu, ) self.settings_zotero_api_key.setContextMenuPolicy( - QtCore.Qt.ContextMenuPolicy.CustomContextMenu + QtCore.Qt.ContextMenuPolicy.CustomContextMenu, ) self.settings_openai_api_key.setContextMenuPolicy( - QtCore.Qt.ContextMenuPolicy.CustomContextMenu + QtCore.Qt.ContextMenuPolicy.CustomContextMenu, ) self.sam_password.setContextMenuPolicy( - QtCore.Qt.ContextMenuPolicy.CustomContextMenu + QtCore.Qt.ContextMenuPolicy.CustomContextMenu, ) self.settings_mail_password.customContextMenuRequested.connect( lambda pos: self.toggle_password_visibility( - pos, self.settings_mail_password - ) + pos, + self.settings_mail_password, + ), ) self.settings_zotero_api_key.customContextMenuRequested.connect( lambda pos: self.toggle_password_visibility( - pos, self.settings_zotero_api_key - ) + pos, + self.settings_zotero_api_key, + ), ) self.settings_openai_api_key.customContextMenuRequested.connect( lambda pos: self.toggle_password_visibility( - pos, self.settings_openai_api_key - ) + pos, + self.settings_openai_api_key, + ), ) self.sam_password.customContextMenuRequested.connect( - lambda pos: self.toggle_password_visibility(pos, self.sam_password) + lambda pos: self.toggle_password_visibility(pos, self.sam_password), ) # if button for next page is clicked, run function to store settings self.button(QtWidgets.QWizard.WizardButton.NextButton).clicked.connect( - self.store_settings + self.store_settings, ) self.settings_mail_use_user_name.toggled.connect(self.set_check_text) # set initial values for checkbox, database @@ -69,13 +72,13 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): self.settings_database.setText( str(settings.database.path) if settings.database.path is not None - else str(appdirs.user_data_dir) + else str(appdirs.user_data_dir), ) self.settings_temp.setText( str(settings.database.temp) if settings.database.temp is not None - else str(appdirs.user_cache_dir) + else str(appdirs.user_cache_dir), ) self.settings_database_name.setText("semesterapparate.db") @@ -199,7 +202,7 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory) file_dialog.setViewMode(QtWidgets.QFileDialog.ViewMode.List) file_dialog.setWindowFlags( - file_dialog.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint + file_dialog.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint, ) # set start dir to appdir.user_data_dir file_dialog.setDirectory(str(appdirs.user_data_dir)) @@ -230,8 +233,6 @@ def launch_wizard(): if not app: app = QtWidgets.QApplication([]) wizard = WelcomeWizard() - # wizard.setWindowTitle("Welcome to the Semester Apparatus Manager") - # wizard.setWindowIcon(settings.Icon("welcome").icon) wizard.setWizardStyle(QtWidgets.QWizard.WizardStyle.ModernStyle) wizard.setStartId(0) wizard.show() diff --git a/zensical.toml b/zensical.toml index bee46f0..0a84690 100644 --- a/zensical.toml +++ b/zensical.toml @@ -69,6 +69,10 @@ nav = [ { "Anlegen" = "elsa/anlegen.md" }, { "Zitieren" = "elsa/zitieren.md" }, ] }, + { "Adminbereich" = [ + { "Übersicht" = "admin/index.md" }, + { "Benutzerverwaltung" = "admin/benutzerverwaltung.md" }, + ] }, { "Sonstiges" = [ { "Übersicht" = "sonstiges/index.md" }, { "Bilder" = "sonstiges/bilder.md" },