Add Adminbereich section with overview and user management pages to navigation, other changes

This commit is contained in:
2025-12-01 12:57:15 +01:00
parent c7304b484a
commit dbfcdbd013
27 changed files with 360 additions and 818 deletions

View File

@@ -94,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 \
@@ -172,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: |

View File

@@ -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"
},
}

1
docs/admin/uebersicht.md Normal file
View File

@@ -0,0 +1 @@
# Übersicht

View File

@@ -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)
</div>
---

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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
<div class="grid cards" markdown>
- :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)
</div>
---
## :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
<div class="grid" markdown>
!!! 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
</div>
---
!!! tip "Tipp: Suche"
Nutzen Sie die **Suchfunktion** oben rechts ++ctrl+k++, um schnell zu den gewünschten Informationen zu gelangen.

View File

@@ -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)

View File

@@ -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.

View File

@@ -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.

View File

@@ -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**

View File

@@ -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

View File

@@ -1,6 +1,8 @@
from .utils.icon import Icon
__version__ = "1.0.0"
__author__ = "Alexander Kirchner"
__all__ = ["__version__", "__author__", "Icon", "settings"]
__all__ = ["Icon", "__author__", "__version__", "settings"]
import os
from pathlib import Path
@@ -13,9 +15,9 @@ from config import Config
app = AppDirs("SemesterApparatsManager", "SAM")
LOG_DIR: str = app.user_log_dir # type: ignore
CONFIG_DIR: str = app.user_config_dir # type: ignore
if not os.path.exists(LOG_DIR): # type: ignore
if not Path(LOG_DIR).exists(): # type: ignore
os.makedirs(LOG_DIR) # type: ignore
if not os.path.exists(CONFIG_DIR): # type: ignore
if not Path(CONFIG_DIR).exists(): # type: ignore
os.makedirs(CONFIG_DIR) # type: ignore
@@ -23,13 +25,12 @@ settings = Config(f"{CONFIG_DIR}/config.yaml")
DATABASE_DIR: Union[Path, str] = ( # type: ignore
app.user_config_dir if settings.database.path is None else settings.database.path # type: ignore
)
if not os.path.exists(DATABASE_DIR): # type: ignore
if not Path(DATABASE_DIR).exists(): # type: ignore
os.makedirs(DATABASE_DIR) # type: ignore
first_launch = settings.exists
if not os.path.exists(settings.database.temp.expanduser()): # type: ignore
if not Path(settings.database.temp.expanduser()).exists(): # type: ignore
settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True) # type: ignore
from .utils.icon import Icon
if not os.path.exists("logs"):
os.mkdir("logs")
if not Path("logs").exists():
Path("logs").mkdir(exist_ok=True)
# open and close the file to create it

View File

@@ -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

View File

@@ -669,7 +669,7 @@ Nachname, Vorname</string>
</spacer>
</item>
<item>
<widget class="QToolButton" name="copy_qoute">
<widget class="QToolButton" name="copy_quote">
<property name="text">
<string>Kopieren</string>
</property>
@@ -763,7 +763,7 @@ Nachname, Vorname</string>
<tabstop>ilias_filename</tabstop>
<tabstop>copy_filename</tabstop>
<tabstop>copy_ilias_filename</tabstop>
<tabstop>copy_qoute</tabstop>
<tabstop>copy_quote</tabstop>
<tabstop>retryButton</tabstop>
</tabstops>
<resources/>

View File

@@ -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):

View File

@@ -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)

View File

@@ -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()

View File

@@ -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" },