Merge pull request 'Update Main to reflect latest dev changes' (#5) from dev into main
Reviewed-on: WorldTeacher/LibrarySystem#5
2
.gitignore
vendored
@@ -218,3 +218,5 @@ compile_commands.json
|
||||
|
||||
.history/*
|
||||
**/tempCodeRunnerFile.py
|
||||
|
||||
output/**
|
||||
111
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build LibrarySystem (Debug)",
|
||||
"type": "shell",
|
||||
"command": "pyinstaller",
|
||||
"args": [
|
||||
"--noconfirm",
|
||||
"--onedir",
|
||||
"--console",
|
||||
"--icon",
|
||||
"'${config:ApplicationIconPath}'",
|
||||
"--name",
|
||||
"LibrarySystem-debug",
|
||||
"--contents-directory",
|
||||
".",
|
||||
"--clean",
|
||||
"--add-data",
|
||||
"'${config:configPath};config/'",
|
||||
"--add-data",
|
||||
"'${config:iconsPath};icons/'",
|
||||
"'${workspaceFolder}/main_dev.py'"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
"focus": false
|
||||
},
|
||||
"dependsOn": "Compress dist Folder Debug",
|
||||
"problemMatcher": "$pyinstaller"
|
||||
},
|
||||
{
|
||||
"label": "Build LibrarySystem (Release)",
|
||||
"type": "shell",
|
||||
"command": "pyinstaller",
|
||||
"args": [
|
||||
"--noconfirm",
|
||||
"--onedir",
|
||||
"--windowed",
|
||||
"--name",
|
||||
"LibrarySystem",
|
||||
"--contents-directory",
|
||||
".",
|
||||
"--clean",
|
||||
"--add-data",
|
||||
"'${config:configPath};config/'",
|
||||
"--add-data",
|
||||
"'${config:iconsPath};icons/'",
|
||||
"--icon",
|
||||
"'${config:ApplicationIconPath}'",
|
||||
"'${workspaceFolder}/main.py'"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": false
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
"focus": false
|
||||
},
|
||||
"dependsOn": "Compress dist Folder Release",
|
||||
"problemMatcher": "$pyinstaller"
|
||||
},
|
||||
{
|
||||
"label": "Run LibrarySystem (live)",
|
||||
"type": "shell",
|
||||
"command": "c:/Users/aky547/GitHub/LibrarySystem/.venv/Scripts/python.exe",
|
||||
"args": [
|
||||
"'c:/Users/aky547/GitHub/LibrarySystem/main_dev.py'",
|
||||
"--ic-logging"
|
||||
],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": "$python"
|
||||
},
|
||||
{
|
||||
"label": "Compress dist Folder Debug",
|
||||
"type": "shell",
|
||||
"command": "Compress-Archive",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${workspaceFolder}/dist/LibrarySystem-debug/",
|
||||
"-DestinationPath",
|
||||
"${workspaceFolder}/output/LibrarySystem-debug.zip",
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": "panel"
|
||||
},
|
||||
{
|
||||
"label": "Compress dist Folder Release",
|
||||
"type": "shell",
|
||||
"command": "Compress-Archive",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${workspaceFolder}/dist/LibrarySystem/",
|
||||
"-DestinationPath",
|
||||
"${workspaceFolder}/output/LibrarySystem.zip",
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": "panel"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
ATTRIBUTIONS.MD
Normal file
@@ -0,0 +1,5 @@
|
||||
## Icons
|
||||
if not stated elsewhere, all Icons are part of the Material Symbols Package provided by Google
|
||||
### Specific Icons
|
||||
- borrow_boks.svg Credits to Maxicons, Icon downloaded frmo thenounproject.com [Link](https://thenounproject.com/icon/borrow-book-3317975/)
|
||||
- borrow
|
||||
29
README.md
@@ -1,3 +1,30 @@
|
||||
# LibrarySystem
|
||||
|
||||
universal library system for facilities in the university
|
||||
universal library system for facilities in the university
|
||||
|
||||
|
||||
## What is this?
|
||||
|
||||
This is a library system for the different facilities in the university. Because some facilities lend their media, some wanted a software that allows them to keep track of who had what when.
|
||||
|
||||
### Installation
|
||||
either clone the Repo
|
||||
```
|
||||
git clone https://git.theprivateserver.de/WorldTeacher/LibrarySystem.git
|
||||
cd LibrarySystem
|
||||
./build(.exe)
|
||||
```
|
||||
|
||||
or head over to [releases](https://git.theprivateserver.de/WorldTeacher/LibrarySystem/releases)
|
||||
and download the latest version
|
||||
|
||||
### Configuration
|
||||
|
||||
the software contains a config file in configs, as well as a config for icons, located in icons.
|
||||
|
||||
#### config
|
||||
tbd
|
||||
|
||||
#### icons
|
||||
- color is a value to re-paint the icons
|
||||
- icons/* a dict of the icons structured like: icon_Name_in_Application : icon_File_name
|
||||
77
build.app.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"version": "auto-py-to-exe-configuration_v1",
|
||||
"pyinstallerOptions": [
|
||||
{
|
||||
"optionDest": "noconfirm",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "filenames",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/main.py"
|
||||
},
|
||||
{
|
||||
"optionDest": "onefile",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "console",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "icon_file",
|
||||
"value": "C:/Users/aky547/Downloads/1490971308-map-icons-7_82746.ico"
|
||||
},
|
||||
{
|
||||
"optionDest": "name",
|
||||
"value": "LibrarySystem"
|
||||
},
|
||||
{
|
||||
"optionDest": "contents_directory",
|
||||
"value": "."
|
||||
},
|
||||
{
|
||||
"optionDest": "clean_build",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "strip",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "noupx",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "disable_windowed_traceback",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_admin",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_uiaccess",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "argv_emulation",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "bootloader_ignore_signals",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "datas",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/config;config/"
|
||||
},
|
||||
{
|
||||
"optionDest": "datas",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/icons;icons/"
|
||||
}
|
||||
],
|
||||
"nonPyinstallerOptions": {
|
||||
"increaseRecursionLimit": true,
|
||||
"manualArguments": ""
|
||||
}
|
||||
}
|
||||
77
build.debug.app.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"version": "auto-py-to-exe-configuration_v1",
|
||||
"pyinstallerOptions": [
|
||||
{
|
||||
"optionDest": "noconfirm",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "filenames",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/main_dev.py"
|
||||
},
|
||||
{
|
||||
"optionDest": "onefile",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "console",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "icon_file",
|
||||
"value": "C:/Users/aky547/Downloads/1490971308-map-icons-7_82746.ico"
|
||||
},
|
||||
{
|
||||
"optionDest": "name",
|
||||
"value": "LibrarySystem-debug"
|
||||
},
|
||||
{
|
||||
"optionDest": "contents_directory",
|
||||
"value": "."
|
||||
},
|
||||
{
|
||||
"optionDest": "clean_build",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "strip",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "noupx",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "disable_windowed_traceback",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_admin",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_uiaccess",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "argv_emulation",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "bootloader_ignore_signals",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "datas",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/config;config/"
|
||||
},
|
||||
{
|
||||
"optionDest": "datas",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/icons;icons/"
|
||||
}
|
||||
],
|
||||
"nonPyinstallerOptions": {
|
||||
"increaseRecursionLimit": true,
|
||||
"manualArguments": ""
|
||||
}
|
||||
}
|
||||
77
build.release.app.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"version": "auto-py-to-exe-configuration_v1",
|
||||
"pyinstallerOptions": [
|
||||
{
|
||||
"optionDest": "noconfirm",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "filenames",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/main.py"
|
||||
},
|
||||
{
|
||||
"optionDest": "onefile",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "console",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "icon_file",
|
||||
"value": "C:/Users/aky547/Downloads/1490971308-map-icons-7_82746.ico"
|
||||
},
|
||||
{
|
||||
"optionDest": "name",
|
||||
"value": "LibrarySystem"
|
||||
},
|
||||
{
|
||||
"optionDest": "contents_directory",
|
||||
"value": "."
|
||||
},
|
||||
{
|
||||
"optionDest": "clean_build",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"optionDest": "strip",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "noupx",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "disable_windowed_traceback",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_admin",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "uac_uiaccess",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "argv_emulation",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "bootloader_ignore_signals",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"optionDest": "datas",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/config;config/"
|
||||
},
|
||||
{
|
||||
"optionDest": "datas",
|
||||
"value": "C:/Users/aky547/GitHub/LibrarySystem/icons;icons/"
|
||||
}
|
||||
],
|
||||
"nonPyinstallerOptions": {
|
||||
"increaseRecursionLimit": true,
|
||||
"manualArguments": ""
|
||||
}
|
||||
}
|
||||
1
config/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .config import Config
|
||||
176
config/config.py
Normal file
@@ -0,0 +1,176 @@
|
||||
from typing import Optional
|
||||
|
||||
import omegaconf
|
||||
class Config:
|
||||
_config: Optional[omegaconf.DictConfig] = None
|
||||
|
||||
def __init__(self, config_path: str):
|
||||
"""
|
||||
Loads the configuration file and stores it for future access.
|
||||
|
||||
Args:
|
||||
config_path (str): Path to the YAML configuration file.
|
||||
"""
|
||||
self._config = omegaconf.OmegaConf.load(config_path)
|
||||
|
||||
@property
|
||||
def institution_name(self)->str:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.institution_name
|
||||
@institution_name.setter
|
||||
def institution_name(self, value: str):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.institution_name = value
|
||||
@property
|
||||
def loan_duration(self)->int:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.default_loan_duration
|
||||
@loan_duration.setter
|
||||
def loan_duration(self, value: int):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.default_loan_duration = value
|
||||
@property
|
||||
def delete_inactive_user_duration(self)->int:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.inactive_user_deletion
|
||||
@delete_inactive_user_duration.setter
|
||||
def delete_inactive_user_duration(self, value: int):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.inactive_user_deletion = value
|
||||
@property
|
||||
def catalogue(self)->bool:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.catalogue
|
||||
@catalogue.setter
|
||||
def catalogue(self, value: bool):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.catalogue = value
|
||||
|
||||
@property
|
||||
def shortcuts(self)->omegaconf.DictConfig:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.shortcuts
|
||||
@shortcuts.setter
|
||||
def shortcuts(self, value: omegaconf.DictConfig):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.shortcuts = value
|
||||
|
||||
@property
|
||||
def advanced_refresh(self)->omegaconf.DictConfig:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.advanced_refresh
|
||||
|
||||
@advanced_refresh.setter
|
||||
def advanced_refresh(self, value: omegaconf.DictConfig):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.advanced_refresh = value
|
||||
|
||||
@property
|
||||
def database(self)->omegaconf.DictConfig:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.database
|
||||
@database.setter
|
||||
def database(self, value: omegaconf.DictConfig):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.database = value
|
||||
@property
|
||||
def report(self)->omegaconf.DictConfig:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.report
|
||||
@report.setter
|
||||
def report(self, value: omegaconf.DictConfig):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.report = value
|
||||
@property
|
||||
def debug(self)->bool:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.debug
|
||||
@debug.setter
|
||||
def debug(self, value: bool):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.debug = value
|
||||
@property
|
||||
def log_debug(self)->bool:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.log_debug
|
||||
@log_debug.setter
|
||||
def log_debug(self, value: bool):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.log_debug = value
|
||||
@property
|
||||
def ic_logging(self)->bool:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.ic_logging
|
||||
@ic_logging.setter
|
||||
def ic_logging(self, value:bool):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.ic_logging = value
|
||||
def save(self):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
omegaconf.OmegaConf.save(self._config, "config/settings.yaml")
|
||||
|
||||
def apply_options(self, options:list):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
for option in options:
|
||||
if option in self._config:
|
||||
self._config[option] = True
|
||||
else:
|
||||
raise KeyError(f"Option {option} not found in configuration")
|
||||
|
||||
@property
|
||||
def documentation(self)->bool:
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
return self._config.documentation
|
||||
@documentation.setter
|
||||
def documentation(self, value: bool):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
self._config.documentation = value
|
||||
|
||||
|
||||
def to_Omegaconf(self):
|
||||
return omegaconf.OmegaConf.create(self._config)
|
||||
|
||||
def updateValue(self, key:str, value):
|
||||
if self._config is None:
|
||||
raise RuntimeError("Configuration not loaded")
|
||||
if "." in key:
|
||||
keys = key.split(".")
|
||||
if keys[0] in self._config:
|
||||
self._config[keys[0]][keys[1]] = value
|
||||
else:
|
||||
raise KeyError(f"Option {keys[0]} not found in configuration")
|
||||
else:
|
||||
self._config[key] = value
|
||||
if __name__ == "__main__":
|
||||
cfg = Config("config/settings.yaml")
|
||||
#print(cfg.database.path)
|
||||
cfg.database.path = "nopathhere"
|
||||
#print(cfg.database.path)
|
||||
cfg.save()
|
||||
#cfg.updateValue("database.path", "Test")
|
||||
@@ -1,8 +1,34 @@
|
||||
institution_name: HB Testbibliothek Psychologie
|
||||
institution_name: Test
|
||||
default_loan_duration: 7
|
||||
inactive_user_deletion: 365
|
||||
database:
|
||||
path: C:/db
|
||||
path: ./database
|
||||
name: library.db
|
||||
backupLocation: ./backup
|
||||
library_id: 20735
|
||||
|
||||
do_backup: true
|
||||
report:
|
||||
generate_report: true
|
||||
path: ./report
|
||||
report_day: 0
|
||||
shortcuts:
|
||||
- name: Rueckgabemodus
|
||||
default: F5
|
||||
current: F5
|
||||
- name: Nutzer
|
||||
default: F6
|
||||
current: F6
|
||||
- name: Hilfe
|
||||
default: F1
|
||||
current: F1
|
||||
- name: Bericht_erstellen
|
||||
default: F7
|
||||
current: F7
|
||||
- name: Ausleihhistorie
|
||||
default: F8
|
||||
current: F8
|
||||
advanced_refresh: false
|
||||
catalogue: true
|
||||
debug: false
|
||||
log_debug: false
|
||||
ic_logging: false
|
||||
documentation: true
|
||||
16
docs/Ausleihe_Sonderfälle.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Sonderfälle bei der Ausleihe
|
||||
|
||||
In einigen Fällen kann es bei der Ausleihe zu Problemen kommen. Diese werden hier aufgeführt.
|
||||
|
||||
## Medium bereits entliehen
|
||||
|
||||
Diese Meldung erscheint, wenn ein Medium bereits entliehen ist und erneut ausgeliehen werden soll.
|
||||
Es erscheint folgende Meldung:
|
||||
|
||||

|
||||
|
||||
Wenn "Yes" geklickt wird, erscheint ein neues Fenster zum anlegen eines neuen Exemplars.
|
||||
|
||||

|
||||
|
||||
Über das + Symbol können neue Exemplare hinzugefügt werden. Es ist empfohlen, die Signatur jeweils um +1 zu erhöhen, um eine eindeutige Zuordnung zu gewährleisten.
|
||||
15
docs/Ausleihhistorie.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Ausleihhistorie
|
||||

|
||||
|
||||
!!! info
|
||||
|
||||
Die Ausleihhistorie kann vom Ausleihsystem immer aufgerufen werden. Dazu kann man entweder das Menu öffnen (Fenster -> Ausleihhistorie) oder den festgelegten Shortcut verwenden.
|
||||
|
||||
# Bedienung
|
||||
Hier werden alle Medien angezeigt.
|
||||
Über die Filter kann man gezielt
|
||||
- alle Ausleihen
|
||||
- aktuell entliehene Medien
|
||||
- überzogene Medien
|
||||
anzeigen.
|
||||
Zudem kann man mithilfe der Eingabezeile unter den Filteroptionen gezielt nach einem Titel, Nutzer oder einer Signatur gesucht werden.
|
||||
83
docs/Ausleihsystem.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Ausleihsystem
|
||||
|
||||
|
||||
|
||||
## Oberfläche
|
||||
|
||||

|
||||
|
||||
Die Oberfläche kann generell in drei Bereiche unterteilt werden:
|
||||
|
||||
- Konto und Ausleihe
|
||||
- Nutzerdaten
|
||||
- Historie
|
||||
|
||||
### Konto und Ausleihe
|
||||
|
||||
Hier werden die Kontodaten und die Ausleihen angezeigt.
|
||||
Der Bereich beschränkt sich auf folgende Inhalte:
|
||||

|
||||
|
||||
Hier werden folgende Daten angezeigt
|
||||
|
||||
- Modus: entweder "Ausleihe" oder "Rückgabe" (1) Kann entweder durch anklicken des Knopfes oder mit dem Shortcut (Standard: `F5`) geändert werden
|
||||
- Matrikelnummer: die Matrikelnummer des Nutzers, um das Konto zu öffnen (3)
|
||||
- Benutzername: der Benutzername des Nutzers (3)
|
||||
- Signatur: die Signatur des Mediums, welches entliehen oder zurückgegeben wird (4)
|
||||
- Ausleihe bis: bis wann das Medium ausgeliehen wird, Zeitraum wird in den Einstellungen angepasst
|
||||
- Nutzer anlegen: ein Knopf, um einen neuen Nutzer anzulegen (2)
|
||||
|
||||
### Nutzerdaten
|
||||
Hier werden die Nutzerdaten angezeigt. Vorraussetzung ist, dass ein Nutzer angelegt und geöffnet wurde, oder dass ein Medium zurückgegeben wurde.
|
||||
Dieser Bereich beschränkt sich auf folgende Inhalte:
|
||||
|
||||

|
||||
|
||||
- Das Feld Nutzerdaten (6)beinhaltet
|
||||
- Namen
|
||||
- Matrikelnummer
|
||||
- E-Mail
|
||||
- Das Feld Ausleihdaten beinhaltet:
|
||||
- Anzahl Ausleihen (5) : ein klickbares Feld, welches die Anzahl der Ausleihen anzeigt. Beim Klick wird die Übersicht des aktiven Nutzers angezeigt.
|
||||
- Nächstes Rückgabedatum (wird angezeigt wenn ein Nutzer (mehr als) eine Ausleihe hat und ein Medium zurückgegeben wurde oder ein Nutzer geöffnet wird)
|
||||
|
||||
### Historie (7)
|
||||
Das Feld der Historie listet alle Medien auf, die im aktiven Prozess ausgeliehen oder zurückgegeben wurden.
|
||||
|
||||
## Ausleihen
|
||||
|
||||
Um ein Medium auszuleihen, muss zuerst ein Nutzer geöffnet sein. Dazu entweder den Knopf neben Modus drücken, oder den Shortcut (Standard: `F5`) verwenden.
|
||||
Die Oberfläche sieht dann wie folgt aus:
|
||||
|
||||

|
||||
|
||||
|
||||
Der Cursor wird automatisch auf die Matrikelnummer gesetzt. Hier kann entweder die vollständige Nummer, oder ein Teil eingegeben werden. Sollten mehrere Nummern dem Teilfilter ensprechen, wird eine Auswahl angezeigt. (s. [MultiUser](MultiUser.md))
|
||||
Nach der Auswahl wird das entsprechende Konto geöffnet und die Ausleihe kann durchgeführt werden.
|
||||
|
||||

|
||||
|
||||
|
||||
Sollte der aktive Nutzer aktive Ausleihen haben, so wird die Anzahl der Medien neben "Anzahl Ausleihen" angezeigt. Das nächste Rückgabedatum wird ebenfalls angezeigt. Über einen Klick auf die Zahl der Ausleihen gelangen Sie zur [Übersicht der Ausleihen des Nutzers](Nutzeroberfläche.md).
|
||||
|
||||
Um ein Medium auszuleihen, muss die Signatur in die entsprechende Zeile eingegeben werden. Sollte die Signatur nicht in der Datenbank existieren, wird der definierte Katalog geprüft. Wird das Medium gefunden, wird es in die Datenbank übernommen und die Ausleihe wird durchgeführt. Sollte das Medium nicht gefunden werden, wird eine Fehlermeldung angezeigt.
|
||||
|
||||

|
||||
|
||||
!!! info
|
||||
|
||||
Die Signatur muss mindestens ein Leerzeichen enthalten, ansonsten wird eine Fehlermeldung angezeigt.
|
||||
|
||||
Sonderfälle siehe [AusleiheSonderfälle](Ausleihe_Sonderfälle.md)
|
||||
|
||||
|
||||
## Rückgaben
|
||||
|
||||
!!! info
|
||||
Rückgaben funktionieren nur, wenn kein Nutzer offen ist. Vor einer Rückgabe bitte sicherstellen, dass der Modus auf "Rückgabe" gesetzt ist.
|
||||
|
||||
Um ein Medium zurückzugeben, muss die Signatur in die entsprechende Zeile eingegeben werden. Sollte die Signatur nicht existieren, wird eine Fehlermeldung angezeigt.
|
||||
|
||||

|
||||
|
||||
Existiert das Medium in der Datenbank und ist entliehen, wird die Rückgabe durchgeführt. Die Rückgabe wird in der Historie gespeichert und das Medium wird wieder als verfügbar markiert. Die Daten des Nutzers werden in der Oberfläche angezeigt.
|
||||
27
docs/Bericht erstellen.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Bericht erstellen
|
||||
|
||||

|
||||
|
||||
## Information
|
||||
Diese Oberfläche kann immer von der [Hauptoberfläche](Ausleihsystem.md) geöffnet werden. Hierzu entweder
|
||||
`Fenster -> Bericht erstellen` oder den Shortcut F7 verwenden
|
||||
|
||||
|
||||
## Bericht generieren
|
||||
|
||||
Um einen Bericht zu erstellen, müssen folgende Kriterien erfüllt sein:
|
||||
|
||||
- Zeitspanne festgelegt (Entweder über den Slider, oder über die Woche / Monat / Jahr Knöpfe)
|
||||
- Datenformat ausgewählt
|
||||
|
||||
Nachdem diese Kriterien erfüllt sind, kann der Bericht über den Knopf `Bericht erstellen` erstellt werden. Der Bericht wird erstellt, bei größeren Datensätzen kann es länger dauern, eine Fortschrittsanzeige gibt an, wie weit der Prozess ist.
|
||||
|
||||
Wurde der Bericht erfolgreich erstellt, sieht die Oberfläche wie folgt aus:
|
||||
|
||||

|
||||
|
||||
Über einen Klick auf `Report` wird die entsprechende Datei geöffnet.
|
||||
|
||||
!!! info
|
||||
|
||||
Text öffnet das Notepad, Excel öffnet Excel
|
||||
21
docs/Einstellungen.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Einstellungen
|
||||

|
||||
|
||||
## Bedienung
|
||||
Hier werden die Einstellungen geändert. Sobald ein Wert geändert wird, ist es möglich die Einstellungen rückgängig zu machen, oder die Änderungen zu speichern.
|
||||
|
||||
## Besonderheiten
|
||||
Der Knopf neben Speicherort, Datenbankname, Sicherungspfad und Speicherpfad kann verwendet werden, um den Pfad gezielt zu setzen.
|
||||
|
||||
Beim Datenbanknamen wird allerdings nur der Name der Datenbank übernommen
|
||||
|
||||
## Hinweis
|
||||
|
||||
Einige Aktionen (bspw. Änderungen der Shortcuts) erfordern einen Neustart der Anwendung. Dies wird beim Speichern der Einstellungen mit folgendem Dialog dargestelt:
|
||||
|
||||

|
||||
|
||||
Im Anschluss an diesen Dialog erscheint ein neuer Dialog:
|
||||
|
||||

|
||||
|
||||
5
docs/MultiUser.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Mehrere Nutzer gefunden
|
||||

|
||||
|
||||
!!! info
|
||||
Es wurden mehrere Nutzer gefunden, die auf die Suchanfrage passen. Bitte wählen Sie einen Nutzer aus der Liste aus, und bestätigen Sie mit `OK`.
|
||||
18
docs/Nutzer anlegen.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Nutzer anlegen
|
||||
|
||||

|
||||
|
||||
## Information
|
||||
Diese Oberfläche kann nur geöffnet werden, wenn die [Hauptoberfläche](Ausleihsystem.md) geöffnet ist. Hierzu muss bei dieser auf folgenden Knopf gedrückt werden:
|
||||

|
||||
|
||||
## Bedienung
|
||||
Um einen Nutzer anzulegen, müssen alle Angaben korrekt ausgefüllt sein.
|
||||
### Limitierungen
|
||||
Folgende Regeln sind zwingend einzuhalten:
|
||||
- Nachname, Vorname muss mit `, ` getrennt sein
|
||||
- Matrikelnummer darf nicht länger als 20 Zeichen sein und nur Nummern enthalten
|
||||
- Mail muss einem validen Schema entsprechen (s. Bild)
|
||||
|
||||
Nachdem alle Kriterien erfüllt sind, kann der Knopf `Save` angeklickt werden. Der Nutzer wird gespeichert und in der [Hauptoberfläche](Ausleihsystem.md) geöffnet.
|
||||
|
||||
31
docs/Nutzeroberfläche.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Nutzerdatenfenster
|
||||

|
||||
|
||||
!!! info
|
||||
Die Nutzeroberfläche kann nur geöffnet werden, wenn ein Nutzer offen ist. Ansonsten wird ein Fehler angezeigt. (s. unten)
|
||||
|
||||

|
||||
|
||||
# Bedeutung der Felder
|
||||
- (1) Nutzerdaten:
|
||||
|
||||
Name des Nutzers, Matrikelnummer, E-Mail
|
||||
Wird eine Angabe geändert, erscheint Feld (3) um diese Angaben entweder zu speichern oder um die Änderungen rückgängig zu machen.
|
||||
|
||||
- (2) Nutzer Löschen:
|
||||
|
||||
Mit dem Klick auf den Mülleimer wird der Nutzer gelöscht. Alle zugewiesenen Ausleihen werden in der [Ausleihhistorie](Ausleihhistorie.md) mit den Nutzerkonto `gelöscht` angezeigt. Diese Option ist nur vorhanden, wenn keine Ausleihen aktiv sind.
|
||||
|
||||
- (4) Medien:
|
||||
|
||||
Umfasst unter anderem Feld (5), welches ein neues Fenster zum [verlängern](ausleihe verlängern.md) der Ausleihe öffnet.
|
||||
Mit einem Klick auf
|
||||
- Alle Ausleihen
|
||||
- Aktuell entliehen
|
||||
- Überzogen
|
||||
werden die Einträge der Tabelle gefiltert. Zusätzlich kann mithilfe der Eingabezeile unter den Filteroptionen gezielt nach einem Titel oder einer Signatur gesucht werden.
|
||||
|
||||
- (5) Verlängern:
|
||||
|
||||
Um Medien zu verlängern, müssen diese in der Tabelle angeklickt werden. Mit Strg können mehrere Medien gleichzeitig ausgewählt und verlängert werden. Hierzu wird ein neues Fenster geöffnet, siehe [Ausleihe verlängern](ausleihe verlängern.md).
|
||||
|
||||
9
docs/ausleihe verlängern.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Medien verlängern
|
||||

|
||||
|
||||
## Information
|
||||
Die Ausleihe verlängern kann nur geöffnet werden, wenn ein Nutzer offen ist.
|
||||
|
||||
Diese Oberfläche erlaubt es, eine oder mehrere Medien zu verlängern. Hierzu muss ein neues, in der Zukunft liegendes Datum ausgewählt und mit OK bestätigt werden.
|
||||
|
||||
In der Datenbank wird nun das neue Datum gespeichert und die Einträge in der Tabelle werden aktualisiert.
|
||||
BIN
docs/images/activeLoan.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/images/add_user.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
docs/images/book_addNew.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
docs/images/book_loaned.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
docs/images/createUser.png
Normal file
|
After Width: | Height: | Size: 757 B |
BIN
docs/images/err_noBook.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
docs/images/err_nouser.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
docs/images/err_return.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
docs/images/extend.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/images/generateReport.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
docs/images/generatedReport.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
docs/images/loanhistory.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/images/main_ktoarea.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
docs/images/main_loan_active.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
docs/images/main_marked areas.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/images/main_no_user.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/images/main_user_active.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/images/main_userdata.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
docs/images/multiUser.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
docs/images/restart.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
docs/images/settings.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/settings_changed_restart.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
docs/images/user_main.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
21
docs/index.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Übersicht
|
||||
|
||||
!!! info
|
||||
|
||||
Einige Knöpfe sind auf Englisch. Diese Einstellung kann ich leider nicht ändern, da diese Übersetzung von der Anwendung automatisch ausgeführt werden
|
||||
|
||||
|
||||
## [Hauptoberfläche](Ausleihsystem.md)
|
||||
Die Hauptoberfläche wird angezeigt, wenn die Anwendung gestartet wird.
|
||||
Von hier aus können Nutzer angelegt und bearbeitet werden, Medien ausgeliehen und zurückgegeben werden, sowie verschiedene Unterbereiche anzeigen.
|
||||
|
||||
Unterbereiche umfassen:
|
||||
|
||||
- [Hauptoberfläche](Ausleihsystem.md)
|
||||
- [Nutzerdaten](Nutzeroberfläche.md) - Daten für den aktuellen Nutzer anzeigen und bearbeiten
|
||||
- [Nutzer anlegen](Nutzer anlegen.md) - Neuen Nutzer anlegen
|
||||
- [Ausleihhistorie](Ausleihhistorie.md) - Historie der Ausleihen
|
||||
- [Bericht erstellen](Bericht erstellen.md) - Bericht für einen festgelegten Zeitrahmen erstellen
|
||||
- [Einstellungen](Einstellungen.md) - Einstellungen der Anwendung ändern
|
||||
|
||||
|
||||
16
docs/shortcuts.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Shortcuts
|
||||
|
||||
!!! info
|
||||
|
||||
Die Shortcuts können für die Anwendung manuell festgelegt werden.
|
||||
Dafür in den [Einstellungen](Einstellungen.md) unter Shortcuts den neuen Shortcut eingeben und speichern
|
||||
|
||||
Standardmäßig sind die Shortcuts wie folgt festgelegt:
|
||||
|
||||
| Shortcut | Standard |
|
||||
| ------- | -------- |
|
||||
| Ausleihhistorie | F7 |
|
||||
| Bericht erstellen | F6 |
|
||||
| Hilfe | F1 |
|
||||
| Nutzer | F5 |
|
||||
| Rückgabemodus | F8 |
|
||||
5
docs/stylesheets/extra.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.md-typeset .admonition,
|
||||
.md-typeset details {
|
||||
border-width: 0;
|
||||
border-left-width: 4px;
|
||||
}
|
||||
103
filehandle.py
Normal file
@@ -0,0 +1,103 @@
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
ORIGIN = "C:/testing/source_2"
|
||||
BACKUP = "C:/Databasebackup"
|
||||
FILE = "database.db"
|
||||
def handle_file():
|
||||
"""
|
||||
Handles file creation, movement, and backup based on path and backup existence.
|
||||
|
||||
Args:
|
||||
path (str): The desired path for the file.
|
||||
filename (str): The name of the file.
|
||||
backup_location (str): The location for backups.
|
||||
|
||||
Returns:
|
||||
str: The final path of the file.
|
||||
"""
|
||||
full = Path(ORIGIN) / FILE
|
||||
full_path = str(full)
|
||||
backup = Path(BACKUP) / FILE
|
||||
backup_path = str(backup)
|
||||
origin_reachable = os.path.exists(full_path)
|
||||
backup_reachable = os.path.exists(backup_path)
|
||||
print(origin_reachable, backup_reachable)
|
||||
if not origin_reachable and not backup_reachable:
|
||||
make_dirs(ORIGIN, BACKUP)
|
||||
|
||||
# if not os.path.exists(full_path):
|
||||
# print("File does not exist, creating file")
|
||||
# create_file(full_path)
|
||||
if os.path.exists(backup_path) and ".backup" in os.listdir(BACKUP):
|
||||
print("Used backup previously, overwriting file")
|
||||
#overwrite file at source with backup
|
||||
shutil.copy(backup_path, full_path)
|
||||
os.remove(BACKUP + "/.backup")
|
||||
|
||||
|
||||
def make_dirs(full_path, backup_path):
|
||||
"""
|
||||
Creates directories if they do not exist and moves the file to the desired location.
|
||||
|
||||
Args:
|
||||
full_path (str): The full path of the file.
|
||||
backup_path (str): The backup path of the file.
|
||||
"""
|
||||
if not os.path.exists(full_path):
|
||||
os.makedirs(full_path)
|
||||
if not os.path.exists(backup_path):
|
||||
os.makedirs(backup_path)
|
||||
|
||||
def create_file(full_path):
|
||||
"""
|
||||
Creates a file at the desired location.
|
||||
|
||||
Args:
|
||||
full_path (str): The full path of the file.
|
||||
"""
|
||||
with open(full_path, "w") as f:
|
||||
f.write("")
|
||||
|
||||
def handle_folder_reachability(original_path, backup_path):
|
||||
"""
|
||||
Checks if the original folder is reachable. If not, creates a backup.
|
||||
If the original folder becomes reachable again, restores the backup.
|
||||
|
||||
Args:
|
||||
original_path (str): Path to the original folder.
|
||||
backup_path (str): Path to the backup folder.
|
||||
|
||||
Returns:
|
||||
str: Path to the current accessible folder.
|
||||
"""
|
||||
|
||||
backup_file = os.path.join(backup_path, ".backup")
|
||||
try:
|
||||
os.makedirs(original_path)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
if not os.path.exists(original_path):
|
||||
#original folder not reachable, use backup path and create .backup file
|
||||
if not os.path.exists(backup_path):
|
||||
os.makedirs(backup_path)
|
||||
with open(backup_file, "w") as f:
|
||||
f.write("")
|
||||
|
||||
# Create an empty backup file as a marker
|
||||
return backup_path +"/" + FILE
|
||||
|
||||
else:
|
||||
# Original folder is reachable, check for backup
|
||||
if os.path.exists(backup_file):
|
||||
# Restore backup
|
||||
shutil.rmtree(original_path) # Remove original folder to avoid conflicts
|
||||
shutil.move(backup_path, original_path)
|
||||
#os.remove(backup_file)
|
||||
#remove backup file from original path
|
||||
os.remove(original_path + "/.backup")
|
||||
os.makedirs(backup_path)
|
||||
return original_path +"/" + FILE
|
||||
if __name__=="__main__":
|
||||
print(handle_folder_reachability(ORIGIN, BACKUP)) # should create a new file at C:/newdatabase/database.db)
|
||||
4
icons/add_book.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" fill="#000000">
|
||||
<path d="m17,10c-3.859,0-7,3.141-7,7s3.141,7,7,7,7-3.141,7-7-3.141-7-7-7Zm0,12c-2.757,0-5-2.243-5-5s2.243-5,5-5,5,2.243,5,5-2.243,5-5,5Zm1-6h2v2h-2v2h-2v-2h-2v-2h2v-2h2v2Zm-8.482,6H3c-.552,0-1-.448-1-1s.448-1,1-1h5.523c-.226-.638-.388-1.306-.464-2h-2.059V2h12v6.058c.695.077,1.362.239,2,.464V2c0-1.103-.898-2-2-2H3C1.346,0,0,1.346,0,3v18c0,1.654,1.346,3,3,3h8.349c-.706-.571-1.325-1.244-1.831-2ZM2,3c0-.552.448-1,1-1h1v16h-1c-.352,0-.686.072-1,.184V3Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 612 B |
1
icons/add_user.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M720-400v-120H600v-80h120v-120h80v120h120v80H800v120h-80Zm-360-80q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm80-80h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0-80Zm0 400Z"/></svg>
|
||||
|
After Width: | Height: | Size: 603 B |
1
icons/backup.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M480-400q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm0 280q-139 0-241-91.5T122-440h82q14 104 92.5 172T480-200q117 0 198.5-81.5T760-480q0-117-81.5-198.5T480-760q-69 0-129 32t-101 88h110v80H120v-240h80v94q51-64 124.5-99T480-840q75 0 140.5 28.5t114 77q48.5 48.5 77 114T840-480q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T480-120Z"/></svg>
|
||||
|
After Width: | Height: | Size: 499 B |
1
icons/book.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#c0c0c0"><path d="M240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h480q33 0 56.5 23.5T800-800v640q0 33-23.5 56.5T720-80H240Zm0-80h480v-640h-80v280l-100-60-100 60v-280H240v640Zm0 0v-640 640Zm200-360 100-60 100 60-100-60-100 60Z"/></svg>
|
||||
|
After Width: | Height: | Size: 344 B |
1
icons/calendar_event.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#c0c0c0"><path d="M580-240q-42 0-71-29t-29-71q0-42 29-71t71-29q42 0 71 29t29 71q0 42-29 71t-71 29ZM200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0 0v-80 80Z"/></svg>
|
||||
|
After Width: | Height: | Size: 405 B |
4
icons/db_backup.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" fill="#000000">
|
||||
<path d="m16,14c-.795,0-.951.69-.956.707-.295.961-2.733,2.293-6.544,2.293-3.968,0-6.5-1.48-6.5-2.5v-1.908c1.64.929,4.077,1.408,6.5,1.408.177,0,.353-.003.527-.007.552-.015.987-.475.973-1.027-.015-.552-.454-1.012-1.026-.973-.156.004-.314.006-.473.006-4.206,0-6.5-1.463-6.5-2.214v-2.193c1.64.929,4.077,1.408,6.5,1.408.177,0,.353-.002.527-.007.552-.015.987-.474.973-1.026-.015-.552-.454-1.012-1.026-.973-.156.004-.314.006-.473.006-4.206,0-6.5-1.463-6.5-2.214v-.571c0-.751,2.294-2.214,6.5-2.214.642,0,1.275.037,1.882.109.547.056,1.046-.327,1.111-.875.065-.548-.327-1.046-.875-1.111C5.912-.437,0,.976,0,4.214v15.572c0,2.767,4.276,4.214,8.5,4.214s8.5-1.447,8.5-4.214v-4.786c0-.018-.051-1-1-1Zm-7.5,8c-4.206,0-6.5-1.463-6.5-2.214v-2.327c1.535.954,3.835,1.541,6.5,1.541,2.626,0,4.939-.568,6.5-1.505v2.291c0,.751-2.294,2.214-6.5,2.214ZM24,1.22v3.06c0,.398-.322.72-.72.72h-.693s-.002,0-.002,0c-.001,0-.002,0-.003,0h-2.361c-.291,0-.554-.175-.665-.444-.112-.269-.05-.579.156-.785l.842-.842c-.705-.585-1.6-.929-2.553-.929-1.591,0-3.03.942-3.668,2.4-.22.505-.805.738-1.316.516-.506-.221-.737-.811-.516-1.317.956-2.187,3.115-3.6,5.5-3.6,1.494,0,2.899.555,3.976,1.506l.795-.796c.206-.206.515-.267.785-.156.269.111.444.374.444.665Zm-.5,7.181c-.955,2.187-3.114,3.6-5.5,3.6-1.494,0-2.899-.555-3.976-1.506l-.795.795c-.206.206-.515.267-.785.156-.269-.111-.444-.374-.444-.665v-3.06c0-.398.322-.72.72-.72h3.06c.291,0,.554.175.665.444.112.269.05.579-.156.785l-.842.842c.705.585,1.6.929,2.553.929,1.591,0,3.03-.942,3.668-2.4.221-.504.806-.735,1.316-.516.506.221.737.811.516,1.317Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
icons/delete.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m376-300 104-104 104 104 56-56-104-104 104-104-56-56-104 104-104-104-56 56 104 104-104 104 56 56Zm-96 180q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520Zm-400 0v520-520Z"/></svg>
|
||||
|
After Width: | Height: | Size: 367 B |
1
icons/duplicate.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#c0c0c0"><path d="M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360Zm0-80h360v-480H360v480ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Zm160-240v-480 480Z"/></svg>
|
||||
|
After Width: | Height: | Size: 340 B |
1
icons/error.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#c0c0c0"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 537 B |
1
icons/group.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm720 0v-120q0-44-24.5-84.5T666-434q51 6 96 20.5t84 35.5q36 20 55 44.5t19 53.5v120H760ZM360-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47Zm400-160q0 66-47 113t-113 47q-11 0-28-2.5t-28-5.5q27-32 41.5-71t14.5-81q0-42-14.5-81T544-792q14-5 28-6.5t28-1.5q66 0 113 47t47 113ZM120-240h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0 320Zm0-400Z"/></svg>
|
||||
|
After Width: | Height: | Size: 766 B |
BIN
icons/icon.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
18
icons/icons.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
color: '#B89230' #Hex code of the color
|
||||
icons:
|
||||
addBook: add_book.svg
|
||||
add_user: add_user.svg
|
||||
backup: db_backup.svg
|
||||
borrow: book.svg
|
||||
duplicate: duplicate.svg
|
||||
error: error.svg
|
||||
loan_extend: calendar_event.svg
|
||||
main: library.svg
|
||||
multiuser: multiple_user.svg
|
||||
newentry: library_add.svg
|
||||
report: report.svg
|
||||
settings: settings.svg
|
||||
user: user.svg
|
||||
warning: warning.svg
|
||||
delete: delete.svg
|
||||
restart: restart.svg
|
||||
1
icons/library.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-library-big"><rect width="8" height="18" x="3" y="3" rx="1"/><path d="M7 3v18"/><path d="M20.4 18.9c.2.5-.1 1.1-.6 1.3l-1.9.7c-.5.2-1.1-.1-1.3-.6L11.1 5.1c-.2-.5.1-1.1.6-1.3l1.9-.7c.5-.2 1.1.1 1.3.6Z"/></svg>
|
||||
|
After Width: | Height: | Size: 410 B |
1
icons/library_add.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#c0c0c0"><path d="M520-400h80v-120h120v-80H600v-120h-80v120H400v80h120v120ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"/></svg>
|
||||
|
After Width: | Height: | Size: 395 B |
BIN
icons/library_shelf.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
1
icons/multiple_user.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M0-240v-63q0-43 44-70t116-27q13 0 25 .5t23 2.5q-14 21-21 44t-7 48v65H0Zm240 0v-65q0-32 17.5-58.5T307-410q32-20 76.5-30t96.5-10q53 0 97.5 10t76.5 30q32 20 49 46.5t17 58.5v65H240Zm540 0v-65q0-26-6.5-49T754-397q11-2 22.5-2.5t23.5-.5q72 0 116 26.5t44 70.5v63H780Zm-455-80h311q-10-20-55.5-35T480-370q-55 0-100.5 15T325-320ZM160-440q-33 0-56.5-23.5T80-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T160-440Zm640 0q-33 0-56.5-23.5T720-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T800-440Zm-320-40q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm0-80q17 0 28.5-11.5T520-600q0-17-11.5-28.5T480-640q-17 0-28.5 11.5T440-600q0 17 11.5 28.5T480-560Zm1 240Zm-1-280Z"/></svg>
|
||||
|
After Width: | Height: | Size: 831 B |
1
icons/report.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#c0c0c0"><path d="M320-600q17 0 28.5-11.5T360-640q0-17-11.5-28.5T320-680q-17 0-28.5 11.5T280-640q0 17 11.5 28.5T320-600Zm0 160q17 0 28.5-11.5T360-480q0-17-11.5-28.5T320-520q-17 0-28.5 11.5T280-480q0 17 11.5 28.5T320-440Zm0 160q17 0 28.5-11.5T360-320q0-17-11.5-28.5T320-360q-17 0-28.5 11.5T280-320q0 17 11.5 28.5T320-280ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h440l200 200v440q0 33-23.5 56.5T760-120H200Zm0-80h560v-400H600v-160H200v560Zm0-560v160-160 560-560Z"/></svg>
|
||||
|
After Width: | Height: | Size: 586 B |
1
icons/restart.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M440-122q-121-15-200.5-105.5T160-440q0-66 26-126.5T260-672l57 57q-38 34-57.5 79T240-440q0 88 56 155.5T440-202v80Zm80 0v-80q87-16 143.5-83T720-440q0-100-70-170t-170-70h-3l44 44-56 56-140-140 140-140 56 56-44 44h3q134 0 227 93t93 227q0 121-79.5 211.5T520-122Z"/></svg>
|
||||
|
After Width: | Height: | Size: 382 B |
1
icons/settings.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>
|
||||
|
After Width: | Height: | Size: 771 B |
1
icons/user.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M234-276q51-39 114-61.5T480-360q69 0 132 22.5T726-276q35-41 54.5-93T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 59 19.5 111t54.5 93Zm246-164q-59 0-99.5-40.5T340-580q0-59 40.5-99.5T480-720q59 0 99.5 40.5T620-580q0 59-40.5 99.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q53 0 100-15.5t86-44.5q-39-29-86-44.5T480-280q-53 0-100 15.5T294-220q39 29 86 44.5T480-160Zm0-360q26 0 43-17t17-43q0-26-17-43t-43-17q-26 0-43 17t-17 43q0 26 17 43t43 17Zm0-60Zm0 360Z"/></svg>
|
||||
|
After Width: | Height: | Size: 751 B |
1
icons/warning.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 537 B |
6
main.py
@@ -1,3 +1,5 @@
|
||||
hello_world = lambda: "Hello, World!"
|
||||
from src.ui.main_ui import launch
|
||||
|
||||
print(hello_world())
|
||||
if __name__ == "__main__":
|
||||
launch()
|
||||
|
||||
|
||||
5
main_dev.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from src.ui.main_ui import launch
|
||||
|
||||
if __name__ == "__main__":
|
||||
launch("--debug")
|
||||
|
||||
30
mkdocs.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
site_name: LibrarySystem
|
||||
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
|
||||
BIN
requirements.txt
Normal file
@@ -1,2 +1,61 @@
|
||||
import omegaconf
|
||||
config = omegaconf.OmegaConf.load("config/settings.yaml")
|
||||
import sys
|
||||
from config import Config
|
||||
__version__ = "0.1.0"
|
||||
__author__ = "Alexander Kirchner"
|
||||
__email__ = "alexander.kirchner@ph-freiburg.de"
|
||||
__license__ = "MIT"
|
||||
docport = 6543
|
||||
|
||||
|
||||
config = Config("config/settings.yaml")
|
||||
valid_args = ["--debug", "--log", "--no-backup", "--ic-logging", "--version", "-h","--no-documentation"]
|
||||
|
||||
args_description = {
|
||||
"--debug": "Enable debug mode",
|
||||
"--log": "Enable logging",
|
||||
"--no-backup": "Disable database backup",
|
||||
"--ic-logging": "Enable icecream logging (not available in production)",
|
||||
"--version": "Show version",
|
||||
"-h": "Show help message and exit",
|
||||
"--no-documentation": "Disable documentation server and shortcut"
|
||||
}
|
||||
|
||||
args = sys.argv[1:]
|
||||
if any(arg not in valid_args for arg in args):
|
||||
print("Invalid argument present")
|
||||
print([arg for arg in args if arg not in valid_args])
|
||||
sys.exit()
|
||||
|
||||
def help():
|
||||
print("Ausleihsystem")
|
||||
print("Ein Ausleihsystem für Handbibliotheken")
|
||||
print("Version: {}".format(__version__))
|
||||
print("Valide Argumente:")
|
||||
print("args")
|
||||
print("--------")
|
||||
print("usage: main.py [-h] [--debug] [--log] [--no-backup] [--ic-logging] [--version] [--no-documentation]")
|
||||
print("options:")
|
||||
for arg in valid_args:
|
||||
print(f"{arg} : {args_description[arg]}")
|
||||
|
||||
# based on the arguments, set the config values
|
||||
if"-h" in args:
|
||||
help()
|
||||
sys.exit()
|
||||
if "--debug" in args:
|
||||
config.debug = True
|
||||
if "--log" in args:
|
||||
config.log_debug = True
|
||||
if "--no-backup" in args:
|
||||
config.no_backup = True
|
||||
if "--ic-logging" in args:
|
||||
config.ic_logging = True
|
||||
if "--no-documentation" in args:
|
||||
config.documentation = False
|
||||
if "--version" in args:
|
||||
print(__version__)
|
||||
sys.exit()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
__help__ = "This package contains the logic of the application."
|
||||
from .database import Database
|
||||
from .catalogue import Catalogue
|
||||
from .catalogue import Catalogue
|
||||
from .backup import Backup
|
||||
29
src/logic/backup.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from src import config
|
||||
|
||||
|
||||
class Backup:
|
||||
def __init__(self):
|
||||
self.source_path = config.database.path + "/" + config.database.name
|
||||
self.backup_path = config.database.backupLocation + "/" + config.database.name
|
||||
self.backup = False
|
||||
if not os.path.exists(config.database.backupLocation):
|
||||
os.makedirs(config.database.backupLocation)
|
||||
if config.database.do_backup == True:
|
||||
self.checkpaths()
|
||||
config.database.do_backup = self.backup
|
||||
|
||||
def checkpaths(self):
|
||||
if os.path.exists(config.database.backupLocation):
|
||||
self.backup = True
|
||||
|
||||
def createBackup(self):
|
||||
if self.backup:
|
||||
if os.path.exists(self.source_path):
|
||||
if os.path.exists(self.backup_path):
|
||||
os.remove(self.backup_path)
|
||||
shutil.copy(self.source_path, self.backup_path)
|
||||
return True
|
||||
return False
|
||||
@@ -2,15 +2,28 @@ import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from src import config
|
||||
from src.schemas import Book
|
||||
|
||||
URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?lookfor={}+&type=AllFields&limit=10&sort=py+desc%2C+title"
|
||||
from src.utils import Log
|
||||
URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND"
|
||||
BASE = "https://rds.ibs-bw.de"
|
||||
|
||||
log = Log("Catalogue")
|
||||
|
||||
class Catalogue:
|
||||
def __init__(self, timeout=5):
|
||||
self.timeout = timeout
|
||||
reachable = self.check_connection()
|
||||
if reachable:
|
||||
config.catalogue = True
|
||||
else:
|
||||
config.catalogue = False
|
||||
|
||||
def check_connection(self):
|
||||
try:
|
||||
response = requests.get("https://www.google.com", timeout=self.timeout)
|
||||
if response.status_code == 200:
|
||||
return True
|
||||
except requests.exceptions.RequestException as e:
|
||||
log.error(f"Could not connect to google.com: {e}")
|
||||
def search_book(self, searchterm: str):
|
||||
response = requests.get(URL.format(searchterm), timeout=self.timeout)
|
||||
return response.text
|
||||
@@ -29,6 +42,8 @@ class Catalogue:
|
||||
return res
|
||||
|
||||
def get_book(self, searchterm: str):
|
||||
log.info(f"Searching for term: {searchterm}")
|
||||
|
||||
links = self.get_book_links(searchterm)
|
||||
for link in links:
|
||||
result = self.search(link)
|
||||
|
||||
@@ -1,72 +1,161 @@
|
||||
import sqlite3 as sql
|
||||
import os
|
||||
import shutil
|
||||
from src import config
|
||||
from pathlib import Path
|
||||
from src.schemas import USERS, MEDIA, LOANS, User, Book
|
||||
|
||||
from src.schemas import USERS, MEDIA, LOANS, User, Book, Loan
|
||||
from src.utils import stringToDate, Log, debugMessage as dbg
|
||||
from PyQt6 import QtCore
|
||||
|
||||
log = Log("Database")
|
||||
FILE = config.database.name
|
||||
class Database:
|
||||
def __init__(self, db_path: str = None):
|
||||
"""
|
||||
'''
|
||||
Default constructor for the database class
|
||||
|
||||
Args:
|
||||
db_path (str, optional): Optional Path for testing / specific purposes. Defaults to None.
|
||||
"""
|
||||
'''
|
||||
if db_path is None:
|
||||
self.db_path = config.database.path + "/" + config.database.name
|
||||
self.db_path = self.handle_folder_reachability(config.database.path, config.database.backupLocation)
|
||||
else:
|
||||
self.db_path = db_path
|
||||
|
||||
if not os.path.exists(config.database.path):
|
||||
os.makedirs(config.database.path)
|
||||
try:
|
||||
os.makedirs(config.database.path)
|
||||
except FileNotFoundError:
|
||||
dbg(self.db_path)
|
||||
if not os.path.exists(config.database.backupLocation):
|
||||
os.makedirs(config.database.backupLocation)
|
||||
|
||||
#if main path does not exist, try to create it. if that fails, use the backuplocation
|
||||
dbg(self.db_path)
|
||||
self.checkDatabaseStatus()
|
||||
|
||||
def handle_folder_reachability(self, original_path, backup_path):
|
||||
"""
|
||||
Checks if the original folder is reachable. If not, creates a backup.
|
||||
If the original folder becomes reachable again, restores the backup.
|
||||
|
||||
Args:
|
||||
original_path (str): Path to the original folder.
|
||||
backup_path (str): Path to the backup folder.
|
||||
|
||||
Returns:
|
||||
str: Path to the current accessible folder.
|
||||
"""
|
||||
|
||||
backup_file = os.path.join(backup_path, ".backup")
|
||||
|
||||
if not os.path.exists(original_path):
|
||||
#original folder not reachable, use backup path and create .backup file
|
||||
if not os.path.exists(backup_path):
|
||||
os.makedirs(backup_path)
|
||||
with open(backup_file, "w") as f:
|
||||
f.write("")
|
||||
|
||||
# Create an empty backup file as a marker
|
||||
return backup_path +"/" + FILE
|
||||
|
||||
else:
|
||||
dbg("Original Path Exists, using this path")
|
||||
# Original folder is reachable, check for backup
|
||||
if os.path.exists(backup_file):
|
||||
# Restore backup
|
||||
shutil.rmtree(original_path) # Remove original folder to avoid conflicts
|
||||
os.rename(backup_path, original_path)
|
||||
# (backup_path, original_path)
|
||||
#os.remove(backup_file)
|
||||
#remove backup file from original path
|
||||
os.remove(original_path + "/.backup")
|
||||
os.makedirs(backup_path)
|
||||
return original_path +"/" + FILE
|
||||
|
||||
def checkDatabasePath(self):
|
||||
self.db_path = config.database.path + "/" + config.database.name
|
||||
#if backup file in backup location, move database to main location, delete backup file
|
||||
if os.path.exists(config.database.backupLocation + "/backup"):
|
||||
if os.path.exists(self.db_path):
|
||||
os.remove(self.db_path)
|
||||
os.rename(f"{config.database.backupLocation}/{config.database.name}", self.db_path)
|
||||
#remove backup file
|
||||
os.remove(config.database.backupLocation + "/backup")
|
||||
return self.db_path
|
||||
else:
|
||||
#keep using backup file
|
||||
self.db_path = config.database.backupLocation + "/" + config.database.name
|
||||
if not os.path.exists(config.database.path):
|
||||
try:
|
||||
os.makedirs(config.database.path)
|
||||
except:
|
||||
self.db_path = config.database.backupLocation + "/" + config.database.name
|
||||
if not os.path.exists(config.database.backupLocation):
|
||||
os.makedirs(config.database.backupLocation)
|
||||
#create a backup file in the backup location
|
||||
with open(f"{config.database.backupLocation}/backup.txt", "w") as f:
|
||||
f.write("Backup File")
|
||||
return self.db_path
|
||||
|
||||
def checkDatabaseStatus(self):
|
||||
log.info("Checking Database Status")
|
||||
if self.tableCheck() == []:
|
||||
# self.logger.log_critical("Database does not exist, creating tables")
|
||||
self.createDatabase()
|
||||
# self.insertSubjects()
|
||||
|
||||
def connect(self) -> sql.Connection:
|
||||
"""
|
||||
'''
|
||||
Connect to the database
|
||||
|
||||
Returns:
|
||||
sql.Connection: The active connection to the database
|
||||
"""
|
||||
'''
|
||||
return sql.connect(self.db_path)
|
||||
|
||||
def close_connection(self, conn: sql.Connection):
|
||||
"""
|
||||
'''
|
||||
closes the connection to the database
|
||||
|
||||
Args:
|
||||
----
|
||||
- conn (sql.Connection): the connection to be closed
|
||||
"""
|
||||
'''
|
||||
conn.close()
|
||||
|
||||
def createDatabase(self):
|
||||
print("Creating Database")
|
||||
if not os.path.exists(config.database.path):
|
||||
os.makedirs(config.database.path)
|
||||
log.info("Creating Database")
|
||||
#print("Creating Database")
|
||||
if not os.path.exists(self.db_path):
|
||||
os.makedirs(self.db_path)
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(USERS)
|
||||
log.debug("Creating Users Table")
|
||||
cursor.execute(MEDIA)
|
||||
log.debug("Creating Media Table")
|
||||
cursor.execute(LOANS)
|
||||
log.debug("Creating Loans Table")
|
||||
conn.commit()
|
||||
self.close_connection(conn)
|
||||
|
||||
def getLoanCount(self):
|
||||
query = "SELECT COUNT(*) FROM loans"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchone()
|
||||
self.close_connection(conn)
|
||||
return result[0]
|
||||
|
||||
def tableCheck(self):
|
||||
# check if database has tables
|
||||
"""
|
||||
'''
|
||||
Get the contents of the
|
||||
|
||||
Returns:
|
||||
Union[List[Tuple], None]: _description_
|
||||
"""
|
||||
Union[List[Tuple], None]: Returns a list of tuples containing the table names or None if no tables are present
|
||||
'''
|
||||
try:
|
||||
with sql.connect(self.db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
@@ -79,7 +168,9 @@ class Database:
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
|
||||
result = cursor.fetchall()
|
||||
self.close_connection(conn)
|
||||
return result
|
||||
def checkUserExists(self, key, value) -> list[User] | bool:
|
||||
query = f"SELECT * FROM users WHERE {key} like '%{value}%'"
|
||||
conn = self.connect()
|
||||
@@ -91,29 +182,120 @@ class Database:
|
||||
|
||||
users = cursor.fetchall()
|
||||
for index, user in enumerate(users):
|
||||
users[index] = User(id=user[0], username=user[1], email=user[2])
|
||||
users[index] = User(userid=user[1], username=user[2], email=user[3], id=user[0])
|
||||
self.close_connection(conn)
|
||||
return users
|
||||
|
||||
def insertUser(self, username, userno, usermail):
|
||||
log.debug(f"Inserting User {userno}, {username}, {usermail}")
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
try:
|
||||
cursor.execute(
|
||||
f"INSERT INTO users (user_id,username, usermail) VALUES ('{userno}', '{username}', '{usermail}' )"
|
||||
)
|
||||
conn.commit()
|
||||
except sql.IntegrityError:
|
||||
return False
|
||||
self.close_connection(conn)
|
||||
return True
|
||||
|
||||
def getUserBy(self, key, value) -> User:
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(f"SELECT * FROM users WHERE {key} = '{value}'")
|
||||
result = cursor.fetchone()
|
||||
self.close_connection(conn)
|
||||
user = User(userid=result[1], username=result[2], email=result[3], id=result[0])
|
||||
return user
|
||||
|
||||
def getUser(self, user_id) -> User:
|
||||
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(f"SELECT * FROM users")
|
||||
result = cursor.fetchall()
|
||||
self.close_connection(conn)
|
||||
if len(str(user_id)) == 1:
|
||||
for res in result:
|
||||
if res[0] == user_id:
|
||||
user = User(userid=res[1], username=res[2], email=res[3], id=res[0])
|
||||
dbg(f"Returning User {user}")
|
||||
log.info(f"Returning User {user}")
|
||||
return user
|
||||
else:
|
||||
for res in result:
|
||||
if res[1] == user_id:
|
||||
user = User(userid=res[1], username=res[2], email=res[3], id=res[0])
|
||||
dbg(f"Returning User {user}")
|
||||
log.info(f"Returning User {user}")
|
||||
return user
|
||||
raise ValueError(f"User {user_id} not found")
|
||||
#return User(userid="gelöscht", username="gelöscht", email="gelöscht", id="gelöscht")
|
||||
# user = User(userid=result[1], username=result[2], email=result[3],id = result[0])
|
||||
# return user
|
||||
|
||||
def getUserId(self, username) -> User:
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")
|
||||
result = cursor.fetchone()
|
||||
self.close_connection(conn)
|
||||
user = User(userid=result[1], username=result[2], email=result[3],id = result[0])
|
||||
log.info(f"Returning User {user}")
|
||||
return user
|
||||
|
||||
def updateUser(self, username, user_id, usermail):
|
||||
log.debug(f"Updating User {userno}, {username}, {usermail}")
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
f"INSERT INTO users (id,username, usermail) VALUES ('{userno}', '{username}', '{usermail}' )"
|
||||
f"UPDATE users SET username = '{username}', usermail = '{usermail}' WHERE user_id = '{user_id}'"
|
||||
)
|
||||
conn.commit()
|
||||
self.close_connection(conn)
|
||||
|
||||
def getUser(self, userid) -> User:
|
||||
|
||||
def setUserActiveDate(self, userid,date):
|
||||
query = f"UPDATE users SET lastActive = '{date}' WHERE user_id = '{userid}'"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(f"SELECT * FROM users WHERE id = '{userid}'")
|
||||
result = cursor.fetchone()
|
||||
cursor.execute(query)
|
||||
conn.commit()
|
||||
dbg(f"Setting User {userid} to active on {date}")
|
||||
|
||||
def renameInactiveUsers(self):
|
||||
lastYear = QtCore.QDate.currentDate().addDays(int(f"-{config.delete_inactive_user_duration}")).toString("yyyy-MM-dd")
|
||||
query = f"SELECT id FROM users WHERE lastActive < '{lastYear}'"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
result = cursor.execute(query).fetchall()
|
||||
self.close_connection(conn)
|
||||
user = User(id=result[0], username=result[1], email=result[2])
|
||||
return user
|
||||
|
||||
if len(result) == 0:
|
||||
log.info(f"Deleting {len(result)} inactive users")
|
||||
for user in result:
|
||||
hasLoans = self.hasLoans(user[0])
|
||||
if not hasLoans:
|
||||
self.deleteUser(user)
|
||||
|
||||
def hasLoans(self, userid)->bool:
|
||||
query = f"SELECT * FROM loans WHERE user_id = '{userid}' AND returned = 0"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchall()
|
||||
self.close_connection(conn)
|
||||
return False if len(result) == 0 else True
|
||||
|
||||
def deleteUser(self, userid):
|
||||
log.debug(f"Deleting User {userid}")
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(f"UPDATE users SET username='gelöscht', usermail = 'gelöscht', user_id='gelöscht' WHERE id = '{userid}'")
|
||||
conn.commit()
|
||||
self.close_connection(conn)
|
||||
|
||||
def getActiveLoans(self, userid):
|
||||
dbg("id", str(userid))
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
try:
|
||||
@@ -124,9 +306,37 @@ class Database:
|
||||
except sql.OperationalError:
|
||||
result = []
|
||||
self.close_connection(conn)
|
||||
log.info(f"Returning Active Loans {result}")
|
||||
return str(len(result))
|
||||
|
||||
def getMediaList(self):
|
||||
query = "SELECT signature FROM media"
|
||||
result = self.query(query)
|
||||
|
||||
return [res[0] for res in result]
|
||||
def getAllLoans(self):
|
||||
loan_data = []
|
||||
query = "SELECT * FROM loans"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
loans = cursor.fetchall()
|
||||
for loan in loans:
|
||||
l = Loan(
|
||||
loan[0],
|
||||
loan[1],
|
||||
loan[2],
|
||||
stringToDate(loan[3]),
|
||||
stringToDate(loan[4]),
|
||||
loan[5],
|
||||
stringToDate(loan[6]),
|
||||
self.getMedia(loan[2]),
|
||||
user_name=self.getUser(loan[1]).username,
|
||||
)
|
||||
loan_data.append(l)
|
||||
return loan_data
|
||||
def insertLoan(self, userid, mediaid, loandate, duedate):
|
||||
log.debug(f"Inserting Loan {userid}, {mediaid}, {loandate}, {duedate}")
|
||||
query = f"INSERT INTO loans (user_id, media_id, loan_date, return_date) Values ('{userid}', '{mediaid}', '{loandate}', '{duedate}')"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
@@ -135,15 +345,51 @@ class Database:
|
||||
self.close_connection(conn)
|
||||
|
||||
def insertMedia(self, media):
|
||||
query = f"INSERT OR IGNORE INTO media (ppn, title, signature, isbn) VALUES ('{media.ppn}', '{media.title}', '{media.signature}', '{media.isbn}')" # , '{media.link}'
|
||||
log.debug(f"Inserting Media {media}")
|
||||
query = f"INSERT OR IGNORE INTO media (ppn, title, signature, isbn,link) VALUES ('{media.ppn}', '{media.title}', '{media.signature}', '{media.isbn}','{media.link}')" # , '{media.link}'
|
||||
log.info(f"Query: |{query}|")
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
|
||||
conn.commit()
|
||||
|
||||
self.close_connection(conn)
|
||||
return cursor.lastrowid
|
||||
|
||||
def getLoansBy(self, field, value):
|
||||
# query all loans, sort by date descending and return
|
||||
pass
|
||||
def getMediaSimilarSignatureByID(self, media_id) -> list[Book]:
|
||||
log.info(f"Getting Media Similar to {media_id}")
|
||||
query = f"SELECT * FROM media WHERE id = '{media_id}'"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchone()
|
||||
signature = result[1]
|
||||
#print(signature)
|
||||
query = f"SELECT * FROM media WHERE signature LIKE '%{signature}%'"
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchall()
|
||||
|
||||
self.close_connection(conn)
|
||||
data = []
|
||||
for res in result:
|
||||
data.append(
|
||||
Book(
|
||||
signature=res[1],
|
||||
isbn=res[2],
|
||||
ppn=res[3],
|
||||
title=res[4],
|
||||
database_id=res[0],
|
||||
)
|
||||
)
|
||||
log.debug(f"Returning Similar Media {data}")
|
||||
return data
|
||||
|
||||
def getMedia(self, media_id):
|
||||
# log.info(f"Getting Media {media_id}")
|
||||
query = f"SELECT * FROM media WHERE id = '{media_id}'"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
@@ -159,7 +405,27 @@ class Database:
|
||||
)
|
||||
return res
|
||||
|
||||
def getAllMedia(self, user_id):
|
||||
# get all books that have the user id in the loans table
|
||||
query = f"SELECT * FROM loans WHERE user_id = '{user_id}'"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchall()
|
||||
self.close_connection(conn)
|
||||
books = []
|
||||
for res in result:
|
||||
book = self.getMedia(res[2])
|
||||
book.loan_from = res[3]
|
||||
book.loan_to = res[4]
|
||||
book.returned = res[5]
|
||||
book.returned_date = res[6]
|
||||
books.append(book)
|
||||
log.info(f"Returning All Media entries ({len(books)}) for user {user_id}")
|
||||
return books
|
||||
|
||||
def checkMediaExists(self, media):
|
||||
log.info(f"Checking Media {media}")
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
query = f"SELECT id, signature FROM media WHERE ppn = '{media.ppn}' OR title = '{media.title}' OR isbn = '{media.isbn}' OR signature = '{media.signature}'"
|
||||
@@ -174,6 +440,7 @@ class Database:
|
||||
return False
|
||||
|
||||
def checkLoanState(self, book_id):
|
||||
log.info(f"Checking Loan State for {book_id}")
|
||||
query = f"SELECT * FROM loans WHERE media_id = '{book_id}' AND returned = 0"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
@@ -182,8 +449,9 @@ class Database:
|
||||
self.close_connection(conn)
|
||||
return result
|
||||
|
||||
def returnMedia(self, media_id):
|
||||
query = f"UPDATE loans SET returned = 1 WHERE media_id = '{media_id}'"
|
||||
def returnMedia(self, media_id, returndate):
|
||||
log.info(f"Returning Media {media_id}")
|
||||
query = f"UPDATE loans SET returned = 1, returned_date = '{returndate}' WHERE media_id = '{media_id}' AND returned = 0"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
@@ -214,11 +482,23 @@ class Database:
|
||||
|
||||
#
|
||||
def selectClosestReturnDate(self, user_id):
|
||||
log.info(f"Selecting Closest Return Date for {user_id}")
|
||||
query = f"SELECT return_date FROM loans WHERE user_id = '{user_id}' AND returned = 0 ORDER BY return_date ASC LIMIT 1"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchone()
|
||||
dbg("Result", response=result)
|
||||
self.close_connection(conn)
|
||||
if result is not None:
|
||||
return result[0]
|
||||
|
||||
def extendLoanDuration(self, signature, newDate):
|
||||
log.info(f"Extending Loan Duration for {signature} to {newDate}")
|
||||
book_id = self.checkMediaExists(Book(signature=signature))
|
||||
query = f"UPDATE loans SET return_date = '{newDate}' WHERE media_id = '{book_id[0]}' AND returned = 0"
|
||||
conn = self.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query)
|
||||
conn.commit()
|
||||
self.close_connection(conn)
|
||||
|
||||
9
src/logic/documentation_thread.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
from src.utils import launch_documentation
|
||||
|
||||
class DocumentationThread(QThread):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def run(self):
|
||||
launch_documentation()
|
||||
@@ -1,3 +1,4 @@
|
||||
from .database import LOANS, MEDIA, USERS
|
||||
from .user import User
|
||||
from .book import Book
|
||||
from .book import Book
|
||||
from .loan import Loan
|
||||
@@ -3,10 +3,14 @@ from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Book:
|
||||
title: str | None = None
|
||||
ppn: int | None = None
|
||||
signature: str | None = None
|
||||
isbn: str | None = None
|
||||
link: str | None = None
|
||||
database_id: int | None = None
|
||||
link: str | None = None
|
||||
title: str = ""
|
||||
ppn: int = ""
|
||||
signature: str = ""
|
||||
isbn: str = ""
|
||||
link: str = ""
|
||||
database_id: int = ""
|
||||
link: str = ""
|
||||
loan_from: str = ""
|
||||
loan_to: str = ""
|
||||
returned: int = ""
|
||||
returned_date: str = ""
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
USERS = """CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
usermail TEXT NOT NULL);
|
||||
usermail TEXT NOT NULL,
|
||||
lastActive TEXT);
|
||||
""" # id == matrikelnr,
|
||||
#matrikelnr TEXT NOT NULL,
|
||||
MEDIA = """CREATE TABLE IF NOT EXISTS media (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
signature TEXT NOT NULL,
|
||||
isbn TEXT NOT NULL,
|
||||
ppn TEXT NOT NULL,
|
||||
isbn TEXT,
|
||||
ppn TEXT,
|
||||
title TEXT NOT NULL,
|
||||
link TEXT NOT NULL,);
|
||||
link TEXT);
|
||||
"""
|
||||
|
||||
LOANS = """CREATE TABLE IF NOT EXISTS loans (
|
||||
@@ -19,6 +22,7 @@ media_id INTEGER NOT NULL,
|
||||
loan_date TEXT NOT NULL,
|
||||
return_date TEXT NOT NULL,
|
||||
returned INTEGER DEFAULT 0,
|
||||
returned_date TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (media_id) REFERENCES media(id));
|
||||
"""
|
||||
|
||||
15
src/schemas/loan.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from .book import Book
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Loan:
|
||||
id: int
|
||||
user_id: int
|
||||
media_id: int
|
||||
loan_date: str
|
||||
return_date: str
|
||||
returned: int
|
||||
returned_date: str
|
||||
book: Book
|
||||
user_name: str
|
||||
@@ -1,11 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from typing import Any
|
||||
|
||||
@dataclass
|
||||
class User:
|
||||
username: str
|
||||
id: int
|
||||
userid: Any
|
||||
email: str
|
||||
id : int = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"Name: {self.username}\nMatrikelnr.: {self.id}\neMail: {self.email}"
|
||||
return f"Name: {self.username}\nMatrikelnr.: {self.userid}\neMail: {self.email}"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
from .sources.Ui_dialog_createUser import Ui_Dialog
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PyQt6.QtGui import QRegularExpressionValidator
|
||||
from src.logic import Database
|
||||
from src.schemas import User
|
||||
|
||||
from src.utils import Icon, Log
|
||||
|
||||
class CreateUser(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self, fieldname, data):
|
||||
super(CreateUser, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle("Benutzer erstellen")
|
||||
self.setWindowIcon(Icon("user").icon)
|
||||
# disable buttonbox save
|
||||
self.db = Database()
|
||||
self.buttonBox.button(
|
||||
@@ -25,12 +27,22 @@ class CreateUser(QtWidgets.QDialog, Ui_Dialog):
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Save
|
||||
).clicked.connect(self.saveUser)
|
||||
self.userid = None
|
||||
|
||||
self.userno.setMaxLength(20)
|
||||
self.user_mail.setValidator(
|
||||
QRegularExpressionValidator(
|
||||
QtCore.QRegularExpression(
|
||||
r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}"
|
||||
)
|
||||
)
|
||||
)
|
||||
self.userno.textChanged.connect(
|
||||
lambda: self.validateInputUserno(self.userno.text(), "int")
|
||||
)
|
||||
def checkFields(self):
|
||||
if (
|
||||
self.username.text() != ""
|
||||
and self.userno.text() != ""
|
||||
and self.user_mail.text() != ""
|
||||
self.username.hasAcceptableInput()
|
||||
and self.userno.hasAcceptableInput()
|
||||
and self.user_mail.hasAcceptableInput()
|
||||
):
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Save
|
||||
@@ -44,5 +56,33 @@ class CreateUser(QtWidgets.QDialog, Ui_Dialog):
|
||||
username = self.username.text()
|
||||
userno = int(self.userno.text())
|
||||
usermail = self.user_mail.text()
|
||||
self.db.insertUser(username, userno, usermail)
|
||||
self.userid = userno
|
||||
if self.db.insertUser(username, userno, usermail):
|
||||
self.userid = userno
|
||||
else:
|
||||
self.setStatusTipMessage(
|
||||
"Benutzer konnte nicht erstellt werden, bitte überprüfen Sie die Eingaben"
|
||||
)
|
||||
|
||||
def setStatusTipMessage(self, message):
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Information")
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Information)
|
||||
dialog.setWindowIcon(Icon("error").overwriteColor("#EA3323"))
|
||||
dialog.setText(message)
|
||||
dialog.exec()
|
||||
|
||||
def validateInputUserno(self, value, type):
|
||||
lastchar = value[-1] if value else ""
|
||||
# if lastchar is not of the type, remove it
|
||||
if type == "int":
|
||||
if not lastchar.isdigit():
|
||||
self.userno.setText(value[:-1])
|
||||
|
||||
def validateInputMail(self, value):
|
||||
pass
|
||||
|
||||
|
||||
def launch():
|
||||
app = QtWidgets.QApplication([])
|
||||
window = CreateUser("id", "123456")
|
||||
window.exec()
|
||||
32
src/ui/extendLoan.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from .sources.Ui_dialog_extendLoanDuration import Ui_Dialog
|
||||
from PyQt6 import QtWidgets, QtCore
|
||||
from src.utils import Icon
|
||||
|
||||
class ExtendLoan(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self, user, media):
|
||||
super(ExtendLoan, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle("Leihfrist verlängern")
|
||||
self.setWindowIcon(Icon("loan_extend").icon)
|
||||
self.user = user
|
||||
self.media = media
|
||||
self.currentDate = QtCore.QDate.currentDate().addDays(1)
|
||||
self.extendDate = ""
|
||||
self.extenduntil.setMinimumDate(self.currentDate)
|
||||
self.show()
|
||||
self.buttonBox.accepted.connect(self.extendLoan)
|
||||
|
||||
def extendLoan(self):
|
||||
#print("Extend Loan")
|
||||
selectedDate = self.extenduntil.selectedDate()
|
||||
#print(selectedDate)
|
||||
self.extendDate = selectedDate
|
||||
self.close()
|
||||
|
||||
|
||||
def launch(user, media):
|
||||
import sys
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = ExtendLoan(user, media)
|
||||
sys.exit(app.exec())
|
||||
138
src/ui/loans.py
Normal file
@@ -0,0 +1,138 @@
|
||||
from .sources.Ui_main_Loans import Ui_MainWindow
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from .user import UserUI
|
||||
from src.logic import Database
|
||||
from src.utils import stringToDate, Icon
|
||||
from src.utils import debugMessage as dbg
|
||||
from icecream import ic
|
||||
|
||||
TABLETOFIELDTRANSLATE = {
|
||||
"Titel": "book.title",
|
||||
"Signatur": "book.signature",
|
||||
"Nutzer": "user_name",
|
||||
}
|
||||
|
||||
|
||||
class LoanWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
def __init__(self):
|
||||
super(LoanWindow, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle("Ausleihhistorie")
|
||||
self.setWindowIcon(Icon("borrow").icon)
|
||||
self.loanTable.horizontalHeader().setSectionResizeMode(
|
||||
QtWidgets.QHeaderView.ResizeMode.Stretch
|
||||
)
|
||||
self.db = Database()
|
||||
self.loans = []
|
||||
self.loadLoans()
|
||||
|
||||
# lineedits
|
||||
self.searchbar.textChanged.connect(self.limitResults)
|
||||
self.searchbar.returnPressed.connect(self.selfpass)
|
||||
|
||||
# radio buttons
|
||||
self.radio_all.clicked.connect(self.filterResults)
|
||||
self.radio_current.clicked.connect(self.filterResults)
|
||||
self.radio_overdue.clicked.connect(self.filterResults)
|
||||
|
||||
# table
|
||||
self.loanTable.doubleClicked.connect(self.showUser)
|
||||
self.show()
|
||||
|
||||
def selfpass(self):
|
||||
pass
|
||||
|
||||
|
||||
def insertRow(self, data):
|
||||
dbg(contents=data)
|
||||
|
||||
retdate = ""
|
||||
if data.returned_date != "":
|
||||
retdate = stringToDate(data.returned_date).toString("dd.MM.yyyy")
|
||||
ic(retdate)
|
||||
self.loanTable.insertRow(0)
|
||||
self.loanTable.setItem(0, 0, QtWidgets.QTableWidgetItem(data.book.isbn))
|
||||
self.loanTable.setItem(0, 1, QtWidgets.QTableWidgetItem(data.book.signature))
|
||||
self.loanTable.setItem(0, 2, QtWidgets.QTableWidgetItem(data.book.title))
|
||||
self.loanTable.setItem(
|
||||
0,
|
||||
3,
|
||||
QtWidgets.QTableWidgetItem(str(self.db.getUser(data.user_id).username)),
|
||||
)
|
||||
self.loanTable.setItem(
|
||||
0,
|
||||
4,
|
||||
QtWidgets.QTableWidgetItem(
|
||||
stringToDate(data.loan_date).toString("dd.MM.yyyy")
|
||||
),
|
||||
)
|
||||
self.loanTable.setItem(
|
||||
0,
|
||||
5,
|
||||
QtWidgets.QTableWidgetItem(
|
||||
stringToDate(data.return_date).toString("dd.MM.yyyy")
|
||||
),
|
||||
)
|
||||
self.loanTable.setItem(0, 6, QtWidgets.QTableWidgetItem(retdate))
|
||||
|
||||
def loadLoans(self):
|
||||
loans = self.db.getAllLoans()
|
||||
for loan in loans:
|
||||
self.insertRow(loan)
|
||||
self.loans = loans
|
||||
|
||||
def filterResults(self):
|
||||
mode = (
|
||||
"all"
|
||||
if self.radio_all.isChecked()
|
||||
else "current"
|
||||
if self.radio_current.isChecked()
|
||||
else "overdue"
|
||||
)
|
||||
self.loanTable.setRowCount(0)
|
||||
today = QtCore.QDate.currentDate()
|
||||
for loan in self.loans:
|
||||
# convert string to Qdate
|
||||
date = loan.return_date
|
||||
qdate = stringToDate(date)
|
||||
# current mode
|
||||
if mode == "current":
|
||||
if loan.returned == 1:
|
||||
continue
|
||||
elif mode == "overdue":
|
||||
if loan.returned_date != "":
|
||||
continue
|
||||
else:
|
||||
if qdate > today:
|
||||
continue
|
||||
self.insertRow(loan)
|
||||
|
||||
def limitResults(self):
|
||||
limiter = self.searchbar.text().lower()
|
||||
limiter = str(limiter)
|
||||
searchfield = self.searchFields.currentText()
|
||||
searchfield = TABLETOFIELDTRANSLATE[searchfield]
|
||||
dbg(limiter=limiter, search=searchfield)
|
||||
self.loanTable.setRowCount(0)
|
||||
for loan in self.loans:
|
||||
fielddata = eval(f"loan.{searchfield}")
|
||||
if isinstance(fielddata, str):
|
||||
fielddata = fielddata.lower()
|
||||
if limiter in fielddata:
|
||||
self.insertRow(loan)
|
||||
|
||||
def showUser(self):
|
||||
row = self.loanTable.currentRow()
|
||||
user_name = self.loanTable.item(row, 3).text()
|
||||
user = self.db.getUserId(user_name)
|
||||
self.user = UserUI(user.username, user.id, user.email)
|
||||
self.user.show()
|
||||
|
||||
|
||||
def launch():
|
||||
import sys
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
main_ui = LoanWindow()
|
||||
# atexit.register(exit_handler)
|
||||
sys.exit(app.exec())
|
||||
@@ -1,224 +1,498 @@
|
||||
import ast
|
||||
import sys
|
||||
import atexit
|
||||
import datetime
|
||||
import webbrowser
|
||||
from src import config, __email__, docport
|
||||
from src.logic import Database, Catalogue, Backup
|
||||
from src.utils import stringToDate, Icon, Log
|
||||
from src.utils import debugMessage as dbg
|
||||
from src.utils.createReport import generate_report
|
||||
from src.schemas import Book
|
||||
from .sources.Ui_main_UserInterface import Ui_MainWindow
|
||||
from .user import UserUI
|
||||
from .createUser import CreateUser
|
||||
from .multiUserInfo import MultiUserFound
|
||||
from .newentry import NewEntry
|
||||
from src import config
|
||||
from src.logic import Database, Catalogue
|
||||
from src.utils.stringtodate import stringToDate
|
||||
from src.schemas import User, Book
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
import sys
|
||||
from .settings import Settings
|
||||
from .newBook import NewBook
|
||||
from .loans import LoanWindow
|
||||
from .reportUi import ReportUi
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from omegaconf import OmegaConf
|
||||
from src.logic.documentation_thread import DocumentationThread
|
||||
backup = Backup()
|
||||
cat = Catalogue()
|
||||
log = Log("main")
|
||||
dbg(backup=config.database.do_backup, catalogue=config.catalogue)
|
||||
|
||||
def getShortcut(shortcuts, name):
|
||||
shortcut = [cut for cut in shortcuts if cut["name"] == name][0]
|
||||
return shortcut["current"]
|
||||
|
||||
class MainUI(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
def __init__(self):
|
||||
super(MainUI, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.db = Database()
|
||||
self.currentDate = QtCore.QDate.currentDate()
|
||||
self.setWindowTitle(f"Handbibliotheksleihsystem {config.institution_name}")
|
||||
self.setWindowIcon(Icon("main").icon)
|
||||
self.label_7.hide()
|
||||
self.nextReturnDate.hide()
|
||||
self.activeUser = None
|
||||
# add default loan duration to current date
|
||||
loanDate = self.currentDate.addDays(config.default_loan_duration)
|
||||
self.duedate.setDate(loanDate)
|
||||
# hotkeys
|
||||
self.actionRueckgabemodus.triggered.connect(self.changeMode)
|
||||
self.actionNutzer.triggered.connect(self.showUser)
|
||||
|
||||
#Buttons
|
||||
self.actionEinstellungen.triggered.connect(self.showSettings)
|
||||
self.actionAusleihhistorie.triggered.connect(self.showLoanHistory)
|
||||
self.actionBericht_erstellen.triggered.connect(self.generateReport)
|
||||
self.actionDokumentation_ffnen.triggered.connect(self.openDocumentation)
|
||||
self.actionBeenden.triggered.connect(self.shutdown)
|
||||
def __mail():
|
||||
webbrowser.open(f"mailto:{__email__}")
|
||||
self.actionProblem_melden.triggered.connect(__mail)
|
||||
#if close button is pressed call shutdown
|
||||
self.closeEvent = self.shutdown
|
||||
# Buttons
|
||||
self.btn_show_lentmedia.clicked.connect(self.showUser)
|
||||
self.btn_createNewUser.clicked.connect(self.createUser)
|
||||
self.btn_createNewUser.setText("")
|
||||
self.btn_createNewUser.setIcon(Icon("add_user").overwriteColor("#1E90FF"))
|
||||
self.mode.clicked.connect(self.changeMode)
|
||||
|
||||
# LineEdits
|
||||
self.input_userno.returnPressed.connect(
|
||||
lambda: self.checkUser("id", self.input_userno.text())
|
||||
lambda: self.checkUser("user_id", self.input_userno.text())
|
||||
)
|
||||
self.input_username.returnPressed.connect(
|
||||
lambda: self.checkUser("username", self.input_username.text())
|
||||
)
|
||||
self.input_file_ident.returnPressed.connect(self.addMedia)
|
||||
self.input_userno.setValidator(QtGui.QIntValidator())
|
||||
|
||||
self.input_file_ident.returnPressed.connect(self.handleLineInput)
|
||||
self.input_userno.setMaxLength(40)
|
||||
self.input_userno.textChanged.connect(
|
||||
lambda: self.validateInput(self.input_userno.text(), "int")
|
||||
)
|
||||
self.input_username.setEnabled(False)
|
||||
self.input_userno.setEnabled(False)
|
||||
self.duedate.setEnabled(False)
|
||||
# TableWidget
|
||||
# set header size to be width/number of columns
|
||||
self.mediaOverview.horizontalHeader().setSectionResizeMode(
|
||||
QtWidgets.QHeaderView.ResizeMode.Stretch
|
||||
)
|
||||
# self.input_userno.
|
||||
self.input_file_ident.setFocus()
|
||||
self.assignShortcuts()
|
||||
# variables
|
||||
self.db = Database()
|
||||
self.currentDate = QtCore.QDate.currentDate()
|
||||
loanDate = self.currentDate.addDays(config.loan_duration)
|
||||
self.activeUser = None
|
||||
self.activeState = "Rückgabe"
|
||||
self.docu = DocumentationThread()
|
||||
if config.documentation:
|
||||
self.docu.start()
|
||||
|
||||
self.duedate.setDate(loanDate)
|
||||
# functions
|
||||
self.activateReturnMode()
|
||||
|
||||
if backup.backup:
|
||||
log.info("Backup enabled")
|
||||
else:
|
||||
log.warning("Backup disabled")
|
||||
self.show()
|
||||
|
||||
def shutdown(self, *args):
|
||||
#kill documentation thread
|
||||
log.info("Shutting down")
|
||||
if config.documentation:
|
||||
self.docu.terminate()
|
||||
sys.exit()
|
||||
|
||||
|
||||
def assignShortcuts(self):
|
||||
shortcuts = config.shortcuts
|
||||
shortcuts = OmegaConf.to_container(shortcuts)
|
||||
#convert to dictconfig
|
||||
|
||||
self.actionDokumentation_ffnen.setShortcut(getShortcut(shortcuts, "Hilfe"))
|
||||
self.actionAusleihhistorie.setShortcut(getShortcut(shortcuts, "Ausleihhistorie"))
|
||||
self.actionBericht_erstellen.setShortcut(getShortcut(shortcuts, "Bericht_erstellen"))
|
||||
self.actionNutzer.setShortcut(getShortcut(shortcuts, "Nutzer"))
|
||||
self.actionRueckgabemodus.setShortcut(getShortcut(shortcuts, "Rueckgabemodus"))
|
||||
|
||||
def generateReport(self):
|
||||
log.info("Generating Report")
|
||||
report = ReportUi()
|
||||
report.exec()
|
||||
|
||||
def showLoanHistory(self):
|
||||
log.info("Showing Loan History")
|
||||
self.loan = LoanWindow()
|
||||
self.loan.show()
|
||||
|
||||
def validateInput(self, value, type):
|
||||
lastchar = value[-1] if value else ""
|
||||
# if lastchar is not of the type, remove it
|
||||
if type == "int":
|
||||
if not lastchar.isdigit():
|
||||
self.input_userno.setText(value[:-1])
|
||||
|
||||
def showSettings(self):
|
||||
log.info("Showing Settings")
|
||||
settings = Settings()
|
||||
settings.exec()
|
||||
result = settings.result()
|
||||
print(settings.settingschanged, settings.restart_required)
|
||||
if result == 1:
|
||||
#dialog to ask if program should be restarted
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Einstellungen geändert")
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Information)
|
||||
dialog.setWindowIcon(Icon("settings").icon)
|
||||
dialog.setText("Einstellungen wurden geändert\nProgramm neu starten?")
|
||||
dialog.setStandardButtons(
|
||||
QtWidgets.QMessageBox.StandardButton.Yes
|
||||
| QtWidgets.QMessageBox.StandardButton.No
|
||||
)
|
||||
dialog.setDefaultButton(QtWidgets.QMessageBox.StandardButton.No)
|
||||
#translate buttons
|
||||
yes = dialog.button(QtWidgets.QMessageBox.StandardButton.Yes)
|
||||
yes.setText("Ja")
|
||||
no = dialog.button(QtWidgets.QMessageBox.StandardButton.No)
|
||||
no.setText("Nein")
|
||||
dialog.exec()
|
||||
result = dialog.result()
|
||||
if result == QtWidgets.QMessageBox.StandardButton.Yes:
|
||||
self.restart()
|
||||
# reload settings
|
||||
#print(config)
|
||||
|
||||
def openDocumentation(self):
|
||||
log.info("Opening Documentation")
|
||||
if config.documentation:
|
||||
webbrowser.open("http://localhost:{}/".format(docport))
|
||||
else:
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Dokumentation nicht verfügbar")
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
dialog.setWindowIcon(Icon("warning").icon)
|
||||
dialog.setText("Dokumentation nicht verfügbar")
|
||||
dialog.exec()
|
||||
|
||||
|
||||
|
||||
|
||||
def restart(self):
|
||||
#log restart
|
||||
dbg("Restarting Program")
|
||||
import os
|
||||
python_executable = sys.executable
|
||||
args = sys.argv[:]
|
||||
args.insert(0, sys.executable)
|
||||
os.execvp(python_executable, args)
|
||||
|
||||
|
||||
def changeMode(self):
|
||||
current = self.mode.text()
|
||||
if current == "Ausleihe":
|
||||
self.mode.setText("Rückgabe")
|
||||
self.input_username.clear()
|
||||
self.input_userno.clear()
|
||||
self.userdata.clear()
|
||||
self.btn_show_lentmedia.setText("")
|
||||
self.label_7.hide()
|
||||
self.nextReturnDate.hide()
|
||||
if current == "Rückgabe":
|
||||
self.input_username.clear()
|
||||
self.input_userno.clear()
|
||||
self.userdata.clear()
|
||||
self.mediaOverview.clearContents()
|
||||
self.btn_show_lentmedia.setText("")
|
||||
self.input_file_ident.clear()
|
||||
self.nextReturnDate.hide()
|
||||
self.label_7.hide()
|
||||
log.info("Changing Mode")
|
||||
dbg(f"Current mode: {self.activeState}")
|
||||
self.input_username.clear()
|
||||
stayReturn = False
|
||||
if config.advanced_refresh and self.userdata.toPlainText() != "":
|
||||
stayReturn = True
|
||||
self.userdata.clear()
|
||||
self.input_userno.clear()
|
||||
self.btn_show_lentmedia.setText("")
|
||||
self.input_file_ident.clear()
|
||||
self.label_7.hide()
|
||||
self.nextReturnDate.hide()
|
||||
self.mediaOverview.setRowCount(0)
|
||||
self.activeUser = None #! remove if last user should be kept
|
||||
if self.activeState == "Rückgabe":
|
||||
if stayReturn:
|
||||
self.activateReturnMode()
|
||||
else: self.activateLoanMode()
|
||||
|
||||
|
||||
else:
|
||||
self.activateReturnMode()
|
||||
|
||||
def activateLoanMode(self):
|
||||
dbg("Activating Loan Mode")
|
||||
self.input_username.setEnabled(True)
|
||||
self.input_userno.setEnabled(True)
|
||||
self.duedate.setEnabled(True)
|
||||
self.input_username.setPlaceholderText("")
|
||||
self.input_userno.setPlaceholderText("")
|
||||
self.input_userno.setFocus()
|
||||
# set mode background color to blue with rounded edges
|
||||
self.mode.setStyleSheet("background-color: #1E90FF")
|
||||
self.mode.setText("Ausleihe")
|
||||
self.activeState = "Ausleihe"
|
||||
if self.input_userno.text() == "" or self.input_username.text() == "":
|
||||
self.input_file_ident.setEnabled(False)
|
||||
self.input_file_ident.setPlaceholderText("Bitte zuerst Nutzerdaten eingeben")
|
||||
else:
|
||||
self.input_file_ident.setEnabled(True)
|
||||
|
||||
def activateReturnMode(self):
|
||||
dbg("Activating Return Mode")
|
||||
self.input_username.setEnabled(False)
|
||||
self.input_userno.setEnabled(False)
|
||||
# set mode background color to orange
|
||||
self.mode.setStyleSheet("background-color: #FFA500")
|
||||
self.duedate.setEnabled(False)
|
||||
self.activeState = "Rückgabe"
|
||||
self.mode.setText("Rückgabe")
|
||||
self.input_file_ident.setEnabled(True)
|
||||
self.input_file_ident.setPlaceholderText("Buchidentifikation eingeben")
|
||||
self.input_username.setPlaceholderText("Bitte erst in den Ausleihmodus wechseln")
|
||||
self.input_userno.setPlaceholderText("Bitte erst in den Ausleihmodus wechseln")
|
||||
|
||||
def showUser(self):
|
||||
log.info(f"Showing User {self.activeUser}")
|
||||
if self.activeUser is None:
|
||||
# create warning dialog
|
||||
log.info("Showing no user selected warning")
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Kein Nutzer ausgewählt")
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
dialog.setWindowIcon(Icon("warning").overwriteColor("#EA3323"))
|
||||
dialog.setText("Kein Nutzer ausgewählt")
|
||||
dialog.exec()
|
||||
return
|
||||
|
||||
self.user_ui = UserUI(
|
||||
self.activeUser.username, self.activeUser.id, self.activeUser.email
|
||||
self.activeUser
|
||||
)
|
||||
# self.user_ui.setFields("John Doe", "123456789", "test@mail.com")
|
||||
self.user_ui.show()
|
||||
|
||||
|
||||
def setUserData(self):
|
||||
self.input_username.setText(self.activeUser.username)
|
||||
self.input_userno.setText(str(self.activeUser.id))
|
||||
log.info("Setting User Data")
|
||||
self.input_username.setText(str(self.activeUser.username))
|
||||
self.input_userno.setText(str(self.activeUser.userid))
|
||||
self.userdata.setText(self.activeUser.__repr__())
|
||||
today = QtCore.QDate.currentDate().toString("yyyy-MM-dd")
|
||||
self.db.setUserActiveDate(self.activeUser.userid, today)
|
||||
|
||||
def createUser(self):
|
||||
log.info("Creating User")
|
||||
user = CreateUser(fieldname="id", data="")
|
||||
user.exec()
|
||||
userid = user.userid
|
||||
print(userid)
|
||||
if userid:
|
||||
log.info(f"User created {userid}")
|
||||
data = self.db.getUserBy("user_id", userid)
|
||||
self.activeUser = data
|
||||
# set user to active user
|
||||
self.setUserData()
|
||||
self.activateLoanMode()
|
||||
self.input_file_ident.setPlaceholderText("Buchidentifikation eingeben")
|
||||
|
||||
self.input_file_ident.setFocus()
|
||||
|
||||
return
|
||||
|
||||
def checkUser(self, fieldname, data):
|
||||
print("Checking User", fieldname, data)
|
||||
log.info(f"Checking User {fieldname}, {data}")
|
||||
#print("Checking User", fieldname, data)
|
||||
# set fieldname as key and data as variable
|
||||
user = self.db.checkUserExists(fieldname, data)
|
||||
if not user:
|
||||
user = CreateUser(fieldname, data)
|
||||
user.exec()
|
||||
userid = user.userid
|
||||
if userid:
|
||||
data = self.db.getUser(userid)
|
||||
self.activeUser = data
|
||||
warning = QtWidgets.QMessageBox()
|
||||
warning.setWindowTitle("Nutzer nicht gefunden")
|
||||
warning.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
warning.setWindowIcon(Icon("warning").overwriteColor("#EA3323"))
|
||||
warning.setText("Nutzer nicht gefunden, bitte erst anlegen")
|
||||
warning.exec()
|
||||
self.input_username.clear()
|
||||
self.input_userno.clear()
|
||||
return
|
||||
else:
|
||||
if len(user) > 1:
|
||||
log.info("Multiple Users found")
|
||||
multi = MultiUserFound(user)
|
||||
multi.exec()
|
||||
self.activeUser = multi.userdata
|
||||
else:
|
||||
#print("User found", user[0])
|
||||
self.activeUser = user[0]
|
||||
|
||||
if self.activeUser is not None:
|
||||
log.info(f"User found {self.activeUser}")
|
||||
#print("User found", self.activeUser)
|
||||
self.setUserData()
|
||||
self.input_file_ident.setFocus()
|
||||
self.mode.setText("Ausleihe")
|
||||
self.btn_show_lentmedia.setText(self.db.getActiveLoans(self.activeUser.id))
|
||||
#print(self.activeUser.__dict__)
|
||||
loans = self.db.getActiveLoans(self.activeUser.id)
|
||||
dbg(loans=loans)
|
||||
self.btn_show_lentmedia.setText(loans)
|
||||
retdate = self.db.selectClosestReturnDate(self.activeUser.id)
|
||||
date = stringToDate(retdate)
|
||||
self.nextReturnDate.setText(date)
|
||||
self.nextReturnDate.show()
|
||||
self.label_7.show()
|
||||
|
||||
if retdate:
|
||||
date = stringToDate(retdate).toString("dd.MM.yyyy")
|
||||
self.nextReturnDate.setText(date)
|
||||
self.nextReturnDate.show()
|
||||
self.label_7.show()
|
||||
self.input_file_ident.setEnabled(True)
|
||||
self.input_file_ident.setPlaceholderText("Buchidentifikation eingeben")
|
||||
self.input_file_ident.setFocus()
|
||||
def moveToLine(self, line):
|
||||
log.debug("Moving to Line", line)
|
||||
line.setFocus()
|
||||
|
||||
def handleLineInput(self):
|
||||
value = self.input_file_ident.text().strip()
|
||||
log.debug(f"Handling Line Input {value}")
|
||||
if self.mode.text() == "Rückgabe":
|
||||
if value in self.db.getMediaList():
|
||||
self.returnMedia(value)
|
||||
else:
|
||||
# create warning dialog
|
||||
log.info("Invalid Input")
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Ungültige Eingabe")
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
dialog.setWindowIcon(Icon("warning").overwriteColor("#EA3323"))
|
||||
dialog.setText("Eingabe ist nicht in der Datenbank\nBitte prüfen und erneut eingeben")
|
||||
dialog.exec()
|
||||
self.input_file_ident.setFocus()
|
||||
self.input_file_ident.clear()
|
||||
return
|
||||
else:
|
||||
if not " " in value:
|
||||
# create warning dialog
|
||||
log.info("Invalid Input")
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Ungültige Eingabe")
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
dialog.setWindowIcon(Icon("warning").overwriteColor("#EA3323"))
|
||||
dialog.setText(
|
||||
"Die Eingabe enthält kein Leerzeichen\nBitte prüfen und erneut eingeben"
|
||||
)
|
||||
dialog.exec()
|
||||
return
|
||||
self.mediaAdd(value)
|
||||
self.input_file_ident.setFocus()
|
||||
def mediaAdd(self, identifier):
|
||||
print("Adding Media", identifier)
|
||||
user_id = self.activeUser.id
|
||||
cat = Catalogue()
|
||||
media = cat.get_book(identifier)
|
||||
dbg("Adding Media", identifier = identifier)
|
||||
self.input_file_ident.clear()
|
||||
self.input_file_ident.setEnabled(False)
|
||||
|
||||
user_id = self.activeUser.id
|
||||
media = Book(signature=identifier)
|
||||
book_id = self.db.checkMediaExists(media)
|
||||
print(book_id)
|
||||
if book_id:
|
||||
if len(book_id) > 1:
|
||||
print("Multiple Books found")
|
||||
dbg(f"Book ID: {book_id}, User ID: {user_id}", media=media)
|
||||
if not book_id:
|
||||
dbg("Book not found, searching catalogue")
|
||||
if config.catalogue == True:
|
||||
media = cat.get_book(identifier)
|
||||
if not media:
|
||||
self.setStatusTipMessage("Buch nicht gefunden")
|
||||
self.input_file_ident.setEnabled(True)
|
||||
return
|
||||
book_id = self.db.insertMedia(media)
|
||||
self.loanMedia(user_id, [book_id])
|
||||
else:
|
||||
newbook = NewBook()
|
||||
newbook.exec()
|
||||
if newbook.result() == 1:
|
||||
media = newbook.book
|
||||
book_id = self.db.insertMedia(media)
|
||||
|
||||
elif book_id:
|
||||
if isinstance(book_id, list) and len(book_id) > 1:
|
||||
#print("Multiple Books found")
|
||||
# TODO: implement book selection dialog
|
||||
return
|
||||
else:
|
||||
if isinstance(book_id, int):
|
||||
book_id = [book_id]
|
||||
# check if book is already loaned
|
||||
loaned = self.db.checkLoanState(book_id[0])
|
||||
if loaned:
|
||||
print("Book already loaned")
|
||||
self.setStatusTip("Book already loaned")
|
||||
#print("Book already loaned")
|
||||
self.setStatusTipMessage("Buch bereits entliehen")
|
||||
# dialog with yes no to create new entry
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Buch bereits entliehen")
|
||||
dialog.setWindowIcon(Icon("duplicate").icon)
|
||||
dialog.setText(
|
||||
"Buch bereits entliehen, soll ein neues hinzugefügt werden?"
|
||||
)
|
||||
dialog.setStandardButtons(
|
||||
QtWidgets.QMessageBox.StandardButton.Yes
|
||||
| QtWidgets.QMessageBox.StandardButton.No
|
||||
)
|
||||
dialog.setDefaultButton(QtWidgets.QMessageBox.StandardButton.No)
|
||||
dialog.exec()
|
||||
result = dialog.result()
|
||||
if result == QtWidgets.QMessageBox.StandardButton.No:
|
||||
self.input_file_ident.setEnabled(True)
|
||||
return
|
||||
newentry = NewEntry([book_id[0]])
|
||||
newentry.exec()
|
||||
if newentry.result() == 1: # only create dialog if new entry was created
|
||||
self.setStatusTipMessage("Neues Exemplar hinzugefügt")
|
||||
#print(created_ids)
|
||||
self.input_file_ident.setEnabled(True)
|
||||
newentries = newentry.newIds
|
||||
if newentries:
|
||||
for entry in newentries:
|
||||
book = self.db.getMedia(entry)
|
||||
self.loanMedia(user_id, [entry], book)
|
||||
dbg("inserted duplicated book into database")
|
||||
return
|
||||
else:
|
||||
print("Book not loaned, loaning now")
|
||||
self.db.insertLoan(
|
||||
user_id,
|
||||
book_id[0],
|
||||
self.currentDate.toString(),
|
||||
self.duedate.date().toString(),
|
||||
)
|
||||
self.mediaOverview.insertRow(0)
|
||||
self.mediaOverview.setItem(
|
||||
0, 0, QtWidgets.QTableWidgetItem(media.isbn)
|
||||
)
|
||||
self.mediaOverview.setItem(
|
||||
0, 1, QtWidgets.QTableWidgetItem(media.title)
|
||||
)
|
||||
self.mediaOverview.setItem(
|
||||
0, 2, QtWidgets.QTableWidgetItem("Entliehen")
|
||||
)
|
||||
#print("Book not loaned, loaning now")
|
||||
self.loanMedia(user_id, book_id)
|
||||
|
||||
# print(media)
|
||||
# if media:
|
||||
# print(book_id, type(book_id))
|
||||
# loaned = self.db.checkLoanState(book_id)
|
||||
# print(loaned)
|
||||
# if self.db.checkMediaExists(media):
|
||||
# print("Book already exists", book_id)
|
||||
# else:
|
||||
# book_id = self.db.insertMedia(media)
|
||||
# print(book_id)
|
||||
# self.db.insertLoan(
|
||||
# self.activeUser.id,
|
||||
# book_id,
|
||||
# self.currentDate.toString(),
|
||||
# self.duedate.date().toString(),
|
||||
# )
|
||||
|
||||
# self.mediaOverview.insertRow(0)
|
||||
# self.mediaOverview.setItem(0, 0, QtWidgets.QTableWidgetItem(media.isbn))
|
||||
# self.mediaOverview.setItem(0, 1, QtWidgets.QTableWidgetItem(media.title))
|
||||
# self.mediaOverview.setItem(0, 2, QtWidgets.QTableWidgetItem("Entliehen"))
|
||||
|
||||
# # add media to database
|
||||
# # check if book present in database
|
||||
# print(self.db.getActiveLoans(self.activeUser.id))
|
||||
# self.btn_show_lentmedia.setText(self.db.getActiveLoans(self.activeUser.id))
|
||||
|
||||
def callShortcut(self, shortcut):
|
||||
print("Calling Shortcut", shortcut)
|
||||
# check if actions have shortcut key and call them
|
||||
sysem_shortcuts = None
|
||||
print(sysem_shortcuts)
|
||||
|
||||
def addMedia(self):
|
||||
mode = self.mode.text()
|
||||
value = self.input_file_ident.text()
|
||||
# if vaule is string, call shortcut, else mediaAdd
|
||||
if mode == "Rückgabe":
|
||||
self.returnMedia(value)
|
||||
else:
|
||||
self.lendMedia(value)
|
||||
def loanMedia(self, user_id, book_id):
|
||||
self.db.insertLoan(
|
||||
user_id,
|
||||
book_id[0],
|
||||
self.currentDate.toString("yyyy-MM-dd"),
|
||||
self.duedate.date().toString("yyyy-MM-dd"),
|
||||
)
|
||||
media = self.db.getMedia(book_id[0])
|
||||
#print(media)
|
||||
self.mediaOverview.insertRow(0)
|
||||
self.mediaOverview.setItem(0, 0, QtWidgets.QTableWidgetItem(media.signature))
|
||||
self.mediaOverview.setItem(0, 1, QtWidgets.QTableWidgetItem(media.title))
|
||||
self.mediaOverview.setItem(0, 2, QtWidgets.QTableWidgetItem("Entliehen"))
|
||||
self.btn_show_lentmedia.setText(self.db.getActiveLoans(self.activeUser.id))
|
||||
self.nextReturnDate.setText(
|
||||
stringToDate(self.db.selectClosestReturnDate(self.activeUser.id)).toString(
|
||||
"dd.MM.yyyy"
|
||||
)
|
||||
)
|
||||
self.nextReturnDate.show()
|
||||
self.label_7.show()
|
||||
self.input_file_ident.setEnabled(True)
|
||||
|
||||
def returnMedia(self, identifier):
|
||||
print("Returning Media", identifier)
|
||||
#print("Returning Media", identifier)
|
||||
# get book id from database
|
||||
# self.
|
||||
identifier = Book(
|
||||
isbn=identifier, title=identifier, signature=identifier, ppn=identifier
|
||||
)
|
||||
book_id = self.db.checkMediaExists(identifier)
|
||||
print(book_id)
|
||||
#print(book_id)
|
||||
if book_id:
|
||||
# check if book is already loaned
|
||||
loaned = self.db.checkLoanState(book_id[0])
|
||||
if loaned:
|
||||
print("Book already loaned, returning now")
|
||||
#print("Book already loaned, returning now")
|
||||
user = self.db.getUserByLoan(book_id[0])
|
||||
# set userdata in lineedits
|
||||
self.activeUser = user
|
||||
self.setUserData()
|
||||
book = self.db.returnMedia(book_id[0])
|
||||
book = self.db.returnMedia(
|
||||
book_id[0], self.currentDate.toString("yyyy-MM-dd")
|
||||
)
|
||||
self.mediaOverview.insertRow(0)
|
||||
self.mediaOverview.setItem(0, 0, QtWidgets.QTableWidgetItem(book.isbn))
|
||||
self.mediaOverview.setItem(0, 0, QtWidgets.QTableWidgetItem(book.signature))
|
||||
self.mediaOverview.setItem(0, 1, QtWidgets.QTableWidgetItem(book.title))
|
||||
self.mediaOverview.setItem(
|
||||
0, 2, QtWidgets.QTableWidgetItem("Zurückgegeben")
|
||||
@@ -227,23 +501,81 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self.btn_show_lentmedia.setText(
|
||||
self.db.getActiveLoans(self.activeUser.id)
|
||||
)
|
||||
#
|
||||
else:
|
||||
print("Book not loaned")
|
||||
#print("Book not loaned")
|
||||
self.setStatusTipMessage("Buch nicht entliehen")
|
||||
self.input_file_ident.clear()
|
||||
else:
|
||||
print("Book not found")
|
||||
dbg("Book not found")
|
||||
#print("Book not found")
|
||||
#self.input_file_ident.setPlaceholderText(f"Buch {identifier} nicht gefunden")
|
||||
|
||||
|
||||
|
||||
def lendMedia(self, value):
|
||||
if value.isnumeric():
|
||||
self.mediaAdd(value)
|
||||
else:
|
||||
if len(value) <= 2:
|
||||
self.callShortcut(value)
|
||||
else:
|
||||
self.mediaAdd(value)
|
||||
|
||||
|
||||
def launch():
|
||||
def setStatusTipMessage(self, message):
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setWindowTitle("Information")
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Information)
|
||||
dialog.setWindowIcon(Icon("error").overwriteColor("#EA3323"))
|
||||
dialog.setText(message)
|
||||
dialog.exec()
|
||||
def exit_handler():
|
||||
dbg("Exiting, creating backup")
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
#print(backup.backup)
|
||||
# generate report if monday
|
||||
if datetime.datetime.now().weekday() == config.report.report_day:
|
||||
generate_report()
|
||||
dbg("Generated Report")
|
||||
Database().renameInactiveUsers()
|
||||
if config.database.do_backup:
|
||||
state = backup.createBackup()
|
||||
# create dialog to show state
|
||||
if state == True:
|
||||
return
|
||||
else:
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
# set icon
|
||||
dialog.setWindowIcon(Icon("backup").icon)
|
||||
dialog.setWindowTitle("Backup")
|
||||
dialog.setText("Backup konnte nicht erstellt werden")
|
||||
|
||||
dialog.exec()
|
||||
dbg("Exiting", backupstate=state)
|
||||
else:
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
# set icon
|
||||
reason = "Unbekannter Grund"
|
||||
if config.database.do_backup is False:
|
||||
reason = "Backup deaktiviert"
|
||||
if backup.backup is False:
|
||||
reason = "Backuppfad nicht gefunden"
|
||||
dialog.setWindowIcon(Icon("backup").icon)
|
||||
dialog.setWindowTitle("Backup nicht möglich")
|
||||
dialog.setText("Backup konnte nicht erstellt werden\nGrund: {}".format(reason))
|
||||
dialog.exec()
|
||||
def launch(*argv):
|
||||
options = sys.argv
|
||||
if argv:
|
||||
options += [arg for arg in argv]
|
||||
options = [arg for arg in options if arg.startswith("--")]
|
||||
#print("Launching Main UI")
|
||||
#print(options)
|
||||
QtCore.QLocale().setDefault(QtCore.QLocale(QtCore.QLocale.Language.German, QtCore.QLocale.Country.Germany))
|
||||
SYSTEM_LANGUAGE = QtCore.QLocale().system().name()
|
||||
print(SYSTEM_LANGUAGE)
|
||||
|
||||
# Load base QT translations from the normal place
|
||||
app = QtWidgets.QApplication([])
|
||||
main_ui = MainUI()
|
||||
#translate ui to system language
|
||||
if SYSTEM_LANGUAGE:
|
||||
translator = QtCore.QTranslator()
|
||||
#do not use ascii encoding
|
||||
translator.load(f"qt_{SYSTEM_LANGUAGE}", "translations")
|
||||
translator.load("app.qm", "translations")
|
||||
app.installTranslator(translator)
|
||||
|
||||
atexit.register(exit_handler)
|
||||
sys.exit(app.exec())
|
||||
# sys.exit(app.exec())
|
||||
|
||||
@@ -1,35 +1,44 @@
|
||||
from .sources.Ui_dialog_multipleUserfound import Ui_Dialog
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from src.schemas import User
|
||||
from src.utils import Icon
|
||||
|
||||
|
||||
class MultiUserFound(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self, users: list[User]):
|
||||
super(MultiUserFound, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle("Mehrere Benutzer gefunden")
|
||||
self.setWindowIcon(Icon("multiuser").icon)
|
||||
self.users = users
|
||||
self.userdata = None
|
||||
self.row = None
|
||||
|
||||
self.displayUsers()
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Ok
|
||||
).clicked.connect(self.selectUser)
|
||||
).clicked.connect(self.accept)
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Cancel
|
||||
).clicked.connect(self.reject)
|
||||
self.tableWidget.horizontalHeader().setSectionResizeMode(
|
||||
QtWidgets.QHeaderView.ResizeMode.Stretch
|
||||
)
|
||||
self.tableWidget.cellClicked.connect(self.selectUser)
|
||||
|
||||
def selectUser(self, row):
|
||||
def selectUser(self, row, column):
|
||||
#print(row, column)
|
||||
user = User(
|
||||
id=self.tableWidget.item(row, 0).text(),
|
||||
userid=self.tableWidget.item(row, 0).text(),
|
||||
username=self.tableWidget.item(row, 1).text(),
|
||||
email=self.tableWidget.item(row, 2).text(),
|
||||
)
|
||||
|
||||
self.userdata = user
|
||||
self.accept()
|
||||
|
||||
def displayUsers(self):
|
||||
for user in self.users:
|
||||
self.tableWidget.insertRow(0)
|
||||
self.tableWidget.setItem(0, 0, QtWidgets.QTableWidgetItem(str(user.id)))
|
||||
self.tableWidget.setItem(0, 0, QtWidgets.QTableWidgetItem(str(user.userid)))
|
||||
self.tableWidget.setItem(0, 1, QtWidgets.QTableWidgetItem(user.username))
|
||||
self.tableWidget.setItem(0, 2, QtWidgets.QTableWidgetItem(user.email))
|
||||
|
||||
38
src/ui/newBook.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from .sources.Ui_dialog_addBook import Ui_Dialog
|
||||
from src.schemas import Book
|
||||
from src.utils import Icon
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class NewBook(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self):
|
||||
super(NewBook, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle("Buch hinzufügen")
|
||||
self.setWindowIcon(Icon("addBook").icon)
|
||||
self.btn_save.setEnabled(False)
|
||||
self.btn_save.clicked.connect(self.saveBook)
|
||||
self.btn_cancel.clicked.connect(self.reject)
|
||||
self.book_title.setFocus()
|
||||
self.book_title.textChanged.connect(self.checkFields)
|
||||
self.book_signature.textChanged.connect(self.checkFields)
|
||||
self.book = None
|
||||
|
||||
self.show()
|
||||
|
||||
def checkFields(self):
|
||||
if (
|
||||
self.book_title.hasAcceptableInput()
|
||||
and self.book_signature.hasAcceptableInput
|
||||
):
|
||||
self.btn_save.setEnabled(True)
|
||||
else:
|
||||
self.btn_save.setEnabled(False)
|
||||
|
||||
def saveBook(self):
|
||||
title = self.book_title.text()
|
||||
signature = self.book_signature.text()
|
||||
book = Book(title=title, signature=signature)
|
||||
self.book = book
|
||||
self.accept()
|
||||
@@ -2,23 +2,32 @@ from .sources.Ui_dialog_addNewTitleEntry import Ui_Dialog
|
||||
from PyQt6 import QtWidgets, QtCore, QtGui
|
||||
from src.logic import Database
|
||||
from src.schemas import Book
|
||||
|
||||
from src.utils import Icon
|
||||
|
||||
class NewEntry(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self, title):
|
||||
def __init__(self, title_id: list[int]):
|
||||
super(NewEntry, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle("Neues Exemplar hinzufügen")
|
||||
self.setWindowIcon(Icon("newentry").icon)
|
||||
self.tableWidget.horizontalHeader().setSectionResizeMode(
|
||||
QtWidgets.QHeaderView.ResizeMode.Stretch
|
||||
)
|
||||
self.db = Database()
|
||||
self.titles = title
|
||||
self.titles = title_id
|
||||
self.newIds = []
|
||||
self.populateTable()
|
||||
self.btn_addNewBook.clicked.connect(self.addEntry)
|
||||
self.buttonBox.accepted.connect(self.insertEntry)
|
||||
#disable buttonbox accepted
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Ok
|
||||
).setEnabled(False)
|
||||
|
||||
def addEntry(self):
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Ok
|
||||
).setEnabled(True)
|
||||
# clone last row and its data
|
||||
row = self.tableWidget.rowCount()
|
||||
self.tableWidget.insertRow(row)
|
||||
@@ -26,16 +35,30 @@ class NewEntry(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.tableWidget.setItem(
|
||||
row, i, QtWidgets.QTableWidgetItem(self.tableWidget.item(row - 1, i))
|
||||
)
|
||||
|
||||
if i == 2 and "+" in self.tableWidget.item(row, i).text():
|
||||
entry = self.tableWidget.item(row, i).text().split("+")[1]
|
||||
entry = str(int(entry) + 1)
|
||||
self.tableWidget.setItem(
|
||||
row,
|
||||
i,
|
||||
QtWidgets.QTableWidgetItem(
|
||||
self.tableWidget.item(row, i).text().split("+")[0] + "+" + entry
|
||||
),
|
||||
)
|
||||
def populateTable(self):
|
||||
for title in self.titles:
|
||||
print(title)
|
||||
entry = self.db.getMedia(title)
|
||||
self.tableWidget.insertRow(0)
|
||||
self.tableWidget.setItem(0, 0, QtWidgets.QTableWidgetItem(entry.isbn))
|
||||
self.tableWidget.setItem(0, 1, QtWidgets.QTableWidgetItem(entry.title))
|
||||
self.tableWidget.setItem(0, 2, QtWidgets.QTableWidgetItem(entry.signature))
|
||||
self.tableWidget.setItem(0, 3, QtWidgets.QTableWidgetItem(entry.ppn))
|
||||
#print(title)
|
||||
entries = self.db.getMediaSimilarSignatureByID(title)
|
||||
# sort by signature
|
||||
entries.sort(key=lambda x: x.signature, reverse=True)
|
||||
for entry in entries:
|
||||
self.tableWidget.insertRow(0)
|
||||
self.tableWidget.setItem(0, 0, QtWidgets.QTableWidgetItem(entry.isbn))
|
||||
self.tableWidget.setItem(0, 1, QtWidgets.QTableWidgetItem(entry.title))
|
||||
self.tableWidget.setItem(
|
||||
0, 2, QtWidgets.QTableWidgetItem(entry.signature)
|
||||
)
|
||||
self.tableWidget.setItem(0, 3, QtWidgets.QTableWidgetItem(entry.ppn))
|
||||
|
||||
def insertEntry(self):
|
||||
# get all rows, convert them to Book and insert into database
|
||||
@@ -50,9 +73,10 @@ class NewEntry(QtWidgets.QDialog, Ui_Dialog):
|
||||
signature=signature,
|
||||
ppn=eval(ppn),
|
||||
)
|
||||
print(book)
|
||||
#print(book)
|
||||
if not self.db.checkMediaExists(book):
|
||||
self.db.insertMedia(book)
|
||||
newBookId = self.db.insertMedia(book)
|
||||
self.newIds.append(newBookId)
|
||||
|
||||
|
||||
def launch():
|
||||
|
||||
105
src/ui/reportUi.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from PyQt6 import QtCore, QtWidgets, QtGui
|
||||
from .sources.Ui_dialog_generateReport import Ui_Dialog
|
||||
from src.utils import Icon
|
||||
from src.utils.reportThread import ReportThread
|
||||
from src.logic import Database
|
||||
import os
|
||||
|
||||
|
||||
class ReportUi(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self, parent=None):
|
||||
super(ReportUi, self).__init__(parent)
|
||||
self.setupUi(self)
|
||||
self.setWindowIcon(Icon("report").icon)
|
||||
self.setWindowTitle("Report erstellen")
|
||||
self.radioButton.hide()
|
||||
self.db = Database()
|
||||
|
||||
self.reportprogress.hide()
|
||||
|
||||
# variables
|
||||
self.maxrecords = 0
|
||||
self.days = 0
|
||||
self.rthread = ReportThread()
|
||||
|
||||
# buttons
|
||||
self.generateReport.setEnabled(False)
|
||||
self.generateReport.clicked.connect(self.generate_report)
|
||||
self.radio_year.clicked.connect(self.set_days_by_radio)
|
||||
self.radio_month.clicked.connect(self.set_days_by_radio)
|
||||
self.radio_week.clicked.connect(self.set_days_by_radio)
|
||||
self.format_txt.clicked.connect(lambda: self.rthread.setFormat("txt"))
|
||||
self.format_csv.clicked.connect(lambda: self.rthread.setFormat("tsv"))
|
||||
self.format_csv.clicked.connect(lambda: self.generateReport.setEnabled(True))
|
||||
self.format_txt.clicked.connect(lambda: self.generateReport.setEnabled(True))
|
||||
# sliders
|
||||
self.dayslider.valueChanged.connect(self.set_days)
|
||||
self.show()
|
||||
|
||||
# labels
|
||||
self.label_4.hide()
|
||||
|
||||
def set_days_by_radio(self):
|
||||
if self.radio_year.isChecked():
|
||||
self.set_days(365)
|
||||
self.dayslider.setValue(365)
|
||||
elif self.radio_month.isChecked():
|
||||
self.set_days(30)
|
||||
self.dayslider.setValue(30)
|
||||
elif self.radio_week.isChecked():
|
||||
self.set_days(7)
|
||||
self.dayslider.setValue(7)
|
||||
|
||||
def set_days(self, value):
|
||||
# if value is not 7,30,365, deactivate radio buttons
|
||||
if value != 7 and value != 30 and value != 365:
|
||||
self.radioButton.setChecked(True)
|
||||
|
||||
self.days = value
|
||||
self.dayValue.setText(str(value))
|
||||
|
||||
def generate_report(self):
|
||||
self.rthread.setDays(self.days)
|
||||
self.rthread.report_signal.connect(self.report_generated)
|
||||
self.rthread.report_nums_signal.connect(self.show_progress)
|
||||
self.rthread.report_progress_signal.connect(self.update_progress)
|
||||
self.rthread.finished.connect(self.reset)
|
||||
# self.rthread.finished.connect(self.rthread.deleteLater)
|
||||
self.rthread.start()
|
||||
|
||||
def reset(self):
|
||||
self.days = 0
|
||||
self.reportprogress.hide()
|
||||
self.reportprogress.setValue(0)
|
||||
self.label_4.setText("Fortschritt:")
|
||||
self.label_4.hide()
|
||||
|
||||
def update_progress(self, num):
|
||||
self.reportlink.clear()
|
||||
self.reportprogress.setValue(num)
|
||||
self.label_4.setText("Fortschritt: " + str(num) + "/" + str(self.maxrecords))
|
||||
if num == self.reportprogress.maximum():
|
||||
self.label_4.setText("Datei wird generiert")
|
||||
|
||||
def show_progress(self, num):
|
||||
self.reportprogress.show()
|
||||
self.label_4.show()
|
||||
self.maxrecords = num
|
||||
self.reportprogress.setMaximum(self.maxrecords)
|
||||
|
||||
def report_generated(self):
|
||||
self.reportlink.setOpenExternalLinks(True)
|
||||
fileformat = self.rthread.format
|
||||
#print(fileformat)
|
||||
self.reportlink.setText(
|
||||
f'<a href="file:///{os.getcwd()}/report.{fileformat}">Report</a>'
|
||||
)
|
||||
self.reportprogress.hide()
|
||||
|
||||
|
||||
def launch():
|
||||
import sys
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = ReportUi()
|
||||
sys.exit(app.exec())
|
||||
269
src/ui/settings.py
Normal file
@@ -0,0 +1,269 @@
|
||||
from .sources.Ui_dialog_settings import Ui_Dialog
|
||||
from PyQt6 import QtWidgets, QtCore
|
||||
from src.utils import Icon
|
||||
from src import config
|
||||
from src.utils import debugMessage as dbg
|
||||
from omegaconf import OmegaConf
|
||||
import os
|
||||
|
||||
class Settings(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self):
|
||||
super(Settings, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle("Einstellungen")
|
||||
self.setWindowIcon(Icon("settings").icon)
|
||||
#variables
|
||||
self.originalSettings = config.to_Omegaconf()
|
||||
self.changedSettings = config.to_Omegaconf()
|
||||
self.shortcuts = config.shortcuts
|
||||
self.shortcuts = self.sortShortcuts(self.shortcuts)
|
||||
self.settingschanged = False
|
||||
self.restart_required = False
|
||||
|
||||
|
||||
# buttonbox
|
||||
self.buttonBox.accepted.connect(self.saveSettings)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
self.loadSettings()
|
||||
|
||||
|
||||
self.populateShortcuts()
|
||||
# buttons
|
||||
self.btn_select_database_backupLocation.clicked.connect(
|
||||
self.selectBackupLocation
|
||||
)
|
||||
self.btn_select_database_path.clicked.connect(self.selectDatabasePath)
|
||||
self.btn_select_database_name.clicked.connect(self.selectDatabaseName)
|
||||
self.btn_select_report_path.clicked.connect(self.selectReportPath)
|
||||
self.returnMode.clicked.connect(self.returnModeSetting)
|
||||
|
||||
|
||||
#other
|
||||
#stretch columns
|
||||
self.shortcutchanger.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Stretch)
|
||||
def returnModeSetting(self):
|
||||
currentstate = self.returnMode.isChecked()
|
||||
if self.originalSettings.advanced_refresh != currentstate:
|
||||
self.enableButtonBox()
|
||||
|
||||
def populateShortcuts(self):
|
||||
for shortcut in self.shortcuts:
|
||||
name = shortcut["name"]
|
||||
default = shortcut["default"]
|
||||
current = shortcut["current"]
|
||||
self.addShortcut(name, default, current)
|
||||
#assume the shortcuts will be changed
|
||||
self.settingschanged = True
|
||||
def addShortcut(self, name, default, current):
|
||||
#remove all pages from shortcutchanger
|
||||
#add new page with name, default and current
|
||||
|
||||
self.shortcutchanger.insertRow(0)
|
||||
self.shortcutchanger.setItem(0, 0, QtWidgets.QTableWidgetItem(name))
|
||||
self.shortcutchanger.setItem(0, 1, QtWidgets.QTableWidgetItem(default))
|
||||
#add keysequenceedit
|
||||
keysequenceedit = QtWidgets.QKeySequenceEdit()
|
||||
keysequenceedit.setKeySequence(current)
|
||||
self.shortcutchanger.setCellWidget(0, 2, keysequenceedit)
|
||||
|
||||
def selectBackupLocation(self):
|
||||
backupLocation = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
"Select Backup Location",
|
||||
self.originalSettings.database.backupLocation,
|
||||
)
|
||||
self.database_backupLocation.setText(backupLocation)
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Discard
|
||||
).setEnabled(True)
|
||||
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setEnabled(
|
||||
True
|
||||
)
|
||||
self.settingschanged = True
|
||||
|
||||
def selectReportPath(self):
|
||||
reportPath = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self, "Select Report Path", self.originalSettings.report.path
|
||||
)
|
||||
self.report_path.setText(reportPath)
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Discard
|
||||
).setEnabled(True)
|
||||
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setEnabled(
|
||||
True
|
||||
)
|
||||
self.settingschanged = True
|
||||
|
||||
def selectDatabasePath(self):
|
||||
databasePath = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self, "Select Database Path", self.originalSettings.database.path
|
||||
)
|
||||
self.database_path.setText(databasePath)
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Discard
|
||||
).setEnabled(True)
|
||||
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setEnabled(
|
||||
True
|
||||
)
|
||||
self.settingschanged = True
|
||||
self.restart_required = True
|
||||
|
||||
def selectDatabaseName(self):
|
||||
# filepicker with filter to select only .db files if a file is selected, set name to the lineedit and set database_path
|
||||
databaseName = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Select Database",
|
||||
self.originalSettings.database.path,
|
||||
"Database Files (*.db)",
|
||||
)
|
||||
self.database_path.setText(databaseName[0].rsplit("/", 1)[0])
|
||||
self.buttonBox.button(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Discard
|
||||
).setEnabled(True)
|
||||
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setEnabled(
|
||||
True
|
||||
)
|
||||
self.settingschanged = True
|
||||
self.restart_required = True
|
||||
|
||||
def getShortcuts(self):
|
||||
shortcuts = []
|
||||
for row in range(self.shortcutchanger.rowCount()):
|
||||
name = self.shortcutchanger.item(row, 0).text()
|
||||
default = self.shortcutchanger.item(row, 1).text()
|
||||
current = self.shortcutchanger.cellWidget(row, 2).keySequence().toString()
|
||||
shortcuts.append(
|
||||
{
|
||||
"name": name,
|
||||
"default": default,
|
||||
"current": current,
|
||||
}
|
||||
)
|
||||
return shortcuts
|
||||
|
||||
def sortShortcuts(self, shortcuts):
|
||||
short = []
|
||||
for shortcut in shortcuts:
|
||||
short.append(shortcut)
|
||||
short.sort(key=lambda x: x["name"])
|
||||
return short
|
||||
|
||||
def saveSettings(self):
|
||||
# save settings to config file
|
||||
institution_name = self.institution_name.text()
|
||||
default_loan_duration = int(self.default_loan_duration.text())
|
||||
delete_inactive_users = int(self.delete_inactive_user_duration.text())
|
||||
database_backupLocation = self.database_backupLocation.text()
|
||||
database_path = self.database_path.text()
|
||||
database_name = self.database_name.text()
|
||||
report_day = self.report_day.currentIndex()
|
||||
report_generate = self.check_generate_report.isChecked()
|
||||
report_path = self.report_path.text()
|
||||
refresh_state = self.returnMode.isChecked()
|
||||
shortcuts = self.getShortcuts()
|
||||
#shortcuts to omegaconf.DictConfig
|
||||
shortcuts = OmegaConf.create(shortcuts)
|
||||
|
||||
|
||||
if database_path != self.originalSettings.database.path :
|
||||
os.makedirs(database_path, exist_ok=True)
|
||||
self.restart_required = True
|
||||
|
||||
|
||||
|
||||
# create new Settings
|
||||
self.changedSettings.institution_name = institution_name
|
||||
self.changedSettings.loan_duration = default_loan_duration
|
||||
self.changedSettings.database.backupLocation = database_backupLocation
|
||||
self.changedSettings.database.path = database_path
|
||||
self.changedSettings.database.name = database_name
|
||||
self.changedSettings.delete_inactive_user_duration = delete_inactive_users
|
||||
self.changedSettings.report.report_day = report_day
|
||||
self.changedSettings.report.path = report_path
|
||||
self.changedSettings.report.generate_report = report_generate
|
||||
self.changedSettings.advanced_refresh = refresh_state
|
||||
self.changedSettings.shortcuts = shortcuts
|
||||
|
||||
changed = self.changedSettings
|
||||
original = self.originalSettings
|
||||
|
||||
|
||||
|
||||
if changed == original:
|
||||
self.settingschanged = False
|
||||
self.restart_required = False
|
||||
dbg("Settings not changed")
|
||||
else:
|
||||
self.settingschanged = True
|
||||
#compare if database or shortcuts were changed
|
||||
database = original.database == changed.database
|
||||
shortcuts = self.shortcuts == self.sortShortcuts(changed.shortcuts)
|
||||
if not database or not shortcuts:
|
||||
self.restart_required = True
|
||||
dbg(f"Settings changed, restart required: {self.restart_required}",database=database,shortcuts=shortcuts)
|
||||
|
||||
# save the new settings
|
||||
if self.settingschanged:
|
||||
# save the settings
|
||||
config.updateValue("institution_name", self.changedSettings.institution_name)
|
||||
config.updateValue("default_loan_duration", self.changedSettings.loan_duration)
|
||||
config.updateValue("database.backupLocation", self.changedSettings.database.backupLocation)
|
||||
config.updateValue("database.path", self.changedSettings.database.path)
|
||||
config.updateValue("database.name", self.changedSettings.database.name)
|
||||
config.updateValue("inactive_user_deletion", self.changedSettings.inactive_user_deletion)
|
||||
config.updateValue("report.report_day", self.changedSettings.report.report_day)
|
||||
config.updateValue("report.generate_report", self.changedSettings.report.generate_report)
|
||||
config.updateValue("report.path", self.changedSettings.report.path)
|
||||
config.updateValue("advanced_refresh", self.changedSettings.advanced_refresh)
|
||||
config.updateValue("shortcuts", self.changedSettings.shortcuts)
|
||||
self.originalSettings = self.changedSettings
|
||||
config.save()
|
||||
|
||||
if self.restart_required:
|
||||
self.restart()
|
||||
self.close()
|
||||
|
||||
def restart(self):
|
||||
dialog = QtWidgets.QMessageBox()
|
||||
dialog.setIcon(QtWidgets.QMessageBox.Icon.Information)
|
||||
dialog.setText("Neustart erforderlich")
|
||||
dialog.setInformativeText(
|
||||
"Das Programm muss neu gestartet werden, um die Änderungen zu übernehmen."
|
||||
)
|
||||
dialog.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok)
|
||||
dialog.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Ok)
|
||||
dialog.setWindowTitle("Neustart erforderlich")
|
||||
dialog.setWindowIcon(Icon("restart").icon)
|
||||
dialog.exec()
|
||||
|
||||
def DiscardSettings(self):
|
||||
self.loadSettings()
|
||||
self.restart_required = False
|
||||
self.settingschanged = False
|
||||
def loadSettings(self):
|
||||
self.institution_name.setText(config.institution_name)
|
||||
self.default_loan_duration.setValue(
|
||||
int(config.loan_duration)
|
||||
)
|
||||
self.delete_inactive_user_duration.setValue(
|
||||
int(config.delete_inactive_user_duration)
|
||||
)
|
||||
self.database_backupLocation.setText(
|
||||
config.database.backupLocation
|
||||
)
|
||||
self.database_path.setText(config.database.path)
|
||||
self.database_name.setText(config.database.name)
|
||||
self.report_day.setCurrentIndex(config.report.report_day)
|
||||
self.check_generate_report.setChecked(config.report.generate_report)
|
||||
self.report_path.setText(config.report.path)
|
||||
self.returnMode.setChecked(config.advanced_refresh)
|
||||
pass
|
||||
|
||||
|
||||
def launch():
|
||||
import sys
|
||||
|
||||
app = QtWidgets.QApplication([])
|
||||
settings = Settings()
|
||||
settings.show()
|
||||
sys.exit(app.exec())
|
||||
54
src/ui/sources/Ui_dialog_addBook.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\LibrarySystem\src\ui\sources\dialog_addBook.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
def setupUi(self, Dialog):
|
||||
Dialog.setObjectName("Dialog")
|
||||
Dialog.resize(262, 100)
|
||||
self.gridLayout = QtWidgets.QGridLayout(Dialog)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.book_signature = QtWidgets.QLineEdit(parent=Dialog)
|
||||
self.book_signature.setObjectName("book_signature")
|
||||
self.gridLayout.addWidget(self.book_signature, 1, 1, 1, 1)
|
||||
self.label_2 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
|
||||
self.label = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label.setObjectName("label")
|
||||
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
|
||||
self.book_title = QtWidgets.QLineEdit(parent=Dialog)
|
||||
self.book_title.setObjectName("book_title")
|
||||
self.gridLayout.addWidget(self.book_title, 0, 1, 1, 1)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.btn_save = QtWidgets.QPushButton(parent=Dialog)
|
||||
self.btn_save.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
|
||||
self.btn_save.setObjectName("btn_save")
|
||||
self.horizontalLayout.addWidget(self.btn_save)
|
||||
self.btn_cancel = QtWidgets.QPushButton(parent=Dialog)
|
||||
self.btn_cancel.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
|
||||
self.btn_cancel.setObjectName("btn_cancel")
|
||||
self.horizontalLayout.addWidget(self.btn_cancel)
|
||||
self.gridLayout.addLayout(self.horizontalLayout, 2, 1, 1, 1)
|
||||
|
||||
self.retranslateUi(Dialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||
Dialog.setTabOrder(self.book_title, self.book_signature)
|
||||
Dialog.setTabOrder(self.book_signature, self.btn_save)
|
||||
Dialog.setTabOrder(self.btn_save, self.btn_cancel)
|
||||
|
||||
def retranslateUi(self, Dialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
|
||||
self.label_2.setText(_translate("Dialog", "Signatur"))
|
||||
self.label.setText(_translate("Dialog", "Titel"))
|
||||
self.btn_save.setText(_translate("Dialog", "Speichern"))
|
||||
self.btn_cancel.setText(_translate("Dialog", "Abbrechen"))
|
||||
8
src/ui/sources/Ui_dialog_addBook.ui.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\LibrarySystem\src\ui\sources\dialog_addBook.ui.VLqeTH'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ class Ui_Dialog(object):
|
||||
def retranslateUi(self, Dialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
Dialog.setWindowTitle(_translate("Dialog", "Nutzer anlegen"))
|
||||
self.userno.setPlaceholderText(_translate("Dialog", "102888557"))
|
||||
self.label_2.setText(_translate("Dialog", "Matrikelnummer"))
|
||||
self.label_3.setText(_translate("Dialog", "Mail"))
|
||||
self.user_mail.setPlaceholderText(_translate("Dialog", "email@ph-freiburg.de"))
|
||||
self.label.setText(_translate("Dialog", "Name, Vorname"))
|
||||
self.username.setPlaceholderText(_translate("Dialog", "Nachname, Vorname"))
|
||||
|
||||
@@ -12,6 +12,7 @@ from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
class Ui_Dialog(object):
|
||||
def setupUi(self, Dialog):
|
||||
Dialog.setObjectName("Dialog")
|
||||
Dialog.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
|
||||
Dialog.resize(400, 300)
|
||||
self.gridLayout = QtWidgets.QGridLayout(Dialog)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
|
||||
119
src/ui/sources/Ui_dialog_generateReport.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\LibrarySystem\src\ui\sources\dialog_generateReport.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
def setupUi(self, Dialog):
|
||||
Dialog.setObjectName("Dialog")
|
||||
Dialog.resize(375, 245)
|
||||
Dialog.setMinimumSize(QtCore.QSize(40, 0))
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.label = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label.setObjectName("label")
|
||||
self.verticalLayout.addWidget(self.label)
|
||||
self.gridLayout = QtWidgets.QGridLayout()
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.label_2 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.radio_week = QtWidgets.QRadioButton(parent=Dialog)
|
||||
self.radio_week.setObjectName("radio_week")
|
||||
self.horizontalLayout.addWidget(self.radio_week)
|
||||
self.radio_month = QtWidgets.QRadioButton(parent=Dialog)
|
||||
self.radio_month.setObjectName("radio_month")
|
||||
self.horizontalLayout.addWidget(self.radio_month)
|
||||
self.radio_year = QtWidgets.QRadioButton(parent=Dialog)
|
||||
self.radio_year.setObjectName("radio_year")
|
||||
self.horizontalLayout.addWidget(self.radio_year)
|
||||
self.gridLayout.addLayout(self.horizontalLayout, 1, 1, 1, 1)
|
||||
self.dayValue = QtWidgets.QLineEdit(parent=Dialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.dayValue.sizePolicy().hasHeightForWidth())
|
||||
self.dayValue.setSizePolicy(sizePolicy)
|
||||
self.dayValue.setMinimumSize(QtCore.QSize(0, 0))
|
||||
self.dayValue.setMaximumSize(QtCore.QSize(40, 16777215))
|
||||
self.dayValue.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
|
||||
self.dayValue.setReadOnly(True)
|
||||
self.dayValue.setObjectName("dayValue")
|
||||
self.gridLayout.addWidget(self.dayValue, 0, 2, 1, 1)
|
||||
self.radioButton = QtWidgets.QRadioButton(parent=Dialog)
|
||||
self.radioButton.setText("")
|
||||
self.radioButton.setCheckable(True)
|
||||
self.radioButton.setObjectName("radioButton")
|
||||
self.gridLayout.addWidget(self.radioButton, 3, 2, 1, 1)
|
||||
self.reportlink = QtWidgets.QLabel(parent=Dialog)
|
||||
self.reportlink.setText("")
|
||||
self.reportlink.setObjectName("reportlink")
|
||||
self.gridLayout.addWidget(self.reportlink, 3, 1, 1, 1)
|
||||
self.dayslider = QtWidgets.QSlider(parent=Dialog)
|
||||
self.dayslider.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
|
||||
self.dayslider.setMinimum(1)
|
||||
self.dayslider.setMaximum(365)
|
||||
self.dayslider.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||
self.dayslider.setInvertedControls(True)
|
||||
self.dayslider.setTickPosition(QtWidgets.QSlider.TickPosition.TicksAbove)
|
||||
self.dayslider.setTickInterval(10)
|
||||
self.dayslider.setObjectName("dayslider")
|
||||
self.gridLayout.addWidget(self.dayslider, 0, 1, 1, 1)
|
||||
self.frame = QtWidgets.QFrame(parent=Dialog)
|
||||
self.frame.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
|
||||
self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Plain)
|
||||
self.frame.setLineWidth(0)
|
||||
self.frame.setObjectName("frame")
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.frame)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.format_txt = QtWidgets.QRadioButton(parent=self.frame)
|
||||
self.format_txt.setObjectName("format_txt")
|
||||
self.gridLayout_2.addWidget(self.format_txt, 0, 0, 1, 1)
|
||||
self.format_csv = QtWidgets.QRadioButton(parent=self.frame)
|
||||
self.format_csv.setObjectName("format_csv")
|
||||
self.gridLayout_2.addWidget(self.format_csv, 0, 1, 1, 1)
|
||||
self.gridLayout.addWidget(self.frame, 2, 1, 1, 1)
|
||||
self.label_3 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
|
||||
self.verticalLayout.addLayout(self.gridLayout)
|
||||
self.label_4 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.verticalLayout.addWidget(self.label_4)
|
||||
self.reportprogress = QtWidgets.QProgressBar(parent=Dialog)
|
||||
self.reportprogress.setProperty("value", 24)
|
||||
self.reportprogress.setTextVisible(True)
|
||||
self.reportprogress.setInvertedAppearance(False)
|
||||
self.reportprogress.setObjectName("reportprogress")
|
||||
self.verticalLayout.addWidget(self.reportprogress)
|
||||
self.generateReport = QtWidgets.QPushButton(parent=Dialog)
|
||||
self.generateReport.setObjectName("generateReport")
|
||||
self.verticalLayout.addWidget(self.generateReport)
|
||||
|
||||
self.retranslateUi(Dialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||
Dialog.setTabOrder(self.radio_week, self.radio_month)
|
||||
Dialog.setTabOrder(self.radio_month, self.radio_year)
|
||||
Dialog.setTabOrder(self.radio_year, self.generateReport)
|
||||
|
||||
def retranslateUi(self, Dialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
|
||||
self.label.setText(_translate("Dialog", "Wieviele Tage sollen im Bericht erfasst werden?"))
|
||||
self.label_2.setText(_translate("Dialog", "Tage"))
|
||||
self.radio_week.setText(_translate("Dialog", "Woche"))
|
||||
self.radio_month.setText(_translate("Dialog", "Monat"))
|
||||
self.radio_year.setText(_translate("Dialog", "Jahr"))
|
||||
self.format_txt.setText(_translate("Dialog", "Text"))
|
||||
self.format_csv.setText(_translate("Dialog", "Excel"))
|
||||
self.label_3.setText(_translate("Dialog", "Dateiformat"))
|
||||
self.label_4.setText(_translate("Dialog", "Fortschritt:"))
|
||||
self.generateReport.setText(_translate("Dialog", " Bericht erstellen"))
|
||||
8
src/ui/sources/Ui_dialog_generateReport.ui.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\LibrarySystem\src\ui\sources\dialog_generateReport.ui.nKmkjJ'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
196
src/ui/sources/Ui_dialog_settings.py
Normal file
@@ -0,0 +1,196 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\LibrarySystem\src\ui\sources\dialog_settings.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
def setupUi(self, Dialog):
|
||||
Dialog.setObjectName("Dialog")
|
||||
Dialog.resize(492, 445)
|
||||
self.formLayout = QtWidgets.QFormLayout(Dialog)
|
||||
self.formLayout.setObjectName("formLayout")
|
||||
self.label = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label.setObjectName("label")
|
||||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label)
|
||||
self.institution_name = QtWidgets.QLineEdit(parent=Dialog)
|
||||
self.institution_name.setObjectName("institution_name")
|
||||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.institution_name)
|
||||
self.label_2 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_2)
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.default_loan_duration = QtWidgets.QSpinBox(parent=Dialog)
|
||||
self.default_loan_duration.setProperty("value", 7)
|
||||
self.default_loan_duration.setObjectName("default_loan_duration")
|
||||
self.horizontalLayout_2.addWidget(self.default_loan_duration)
|
||||
self.label_13 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_13.setMaximumSize(QtCore.QSize(43, 16777215))
|
||||
self.label_13.setObjectName("label_13")
|
||||
self.horizontalLayout_2.addWidget(self.label_13)
|
||||
self.formLayout.setLayout(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.horizontalLayout_2)
|
||||
self.label_7 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_7.setObjectName("label_7")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_7)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.delete_inactive_user_duration = QtWidgets.QSpinBox(parent=Dialog)
|
||||
self.delete_inactive_user_duration.setMaximum(9999)
|
||||
self.delete_inactive_user_duration.setProperty("value", 365)
|
||||
self.delete_inactive_user_duration.setObjectName("delete_inactive_user_duration")
|
||||
self.horizontalLayout.addWidget(self.delete_inactive_user_duration)
|
||||
self.label_12 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_12.setMaximumSize(QtCore.QSize(43, 16777215))
|
||||
self.label_12.setObjectName("label_12")
|
||||
self.horizontalLayout.addWidget(self.label_12)
|
||||
self.formLayout.setLayout(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.horizontalLayout)
|
||||
self.returnMode = QtWidgets.QCheckBox(parent=Dialog)
|
||||
self.returnMode.setTristate(False)
|
||||
self.returnMode.setObjectName("returnMode")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.returnMode)
|
||||
self.label_3 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_3)
|
||||
self.databasesettings = QtWidgets.QGridLayout()
|
||||
self.databasesettings.setObjectName("databasesettings")
|
||||
self.database_name = QtWidgets.QLineEdit(parent=Dialog)
|
||||
self.database_name.setObjectName("database_name")
|
||||
self.databasesettings.addWidget(self.database_name, 1, 1, 1, 1)
|
||||
self.label_4 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.databasesettings.addWidget(self.label_4, 0, 0, 1, 1)
|
||||
self.label_6 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_6.setObjectName("label_6")
|
||||
self.databasesettings.addWidget(self.label_6, 2, 0, 1, 1)
|
||||
self.database_path = QtWidgets.QLineEdit(parent=Dialog)
|
||||
self.database_path.setObjectName("database_path")
|
||||
self.databasesettings.addWidget(self.database_path, 0, 1, 1, 1)
|
||||
self.database_backupLocation = QtWidgets.QLineEdit(parent=Dialog)
|
||||
self.database_backupLocation.setObjectName("database_backupLocation")
|
||||
self.databasesettings.addWidget(self.database_backupLocation, 2, 1, 1, 1)
|
||||
self.label_5 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_5.setObjectName("label_5")
|
||||
self.databasesettings.addWidget(self.label_5, 1, 0, 1, 1)
|
||||
self.btn_select_database_path = QtWidgets.QToolButton(parent=Dialog)
|
||||
self.btn_select_database_path.setObjectName("btn_select_database_path")
|
||||
self.databasesettings.addWidget(self.btn_select_database_path, 0, 2, 1, 1)
|
||||
self.btn_select_database_name = QtWidgets.QToolButton(parent=Dialog)
|
||||
self.btn_select_database_name.setObjectName("btn_select_database_name")
|
||||
self.databasesettings.addWidget(self.btn_select_database_name, 1, 2, 1, 1)
|
||||
self.btn_select_database_backupLocation = QtWidgets.QToolButton(parent=Dialog)
|
||||
self.btn_select_database_backupLocation.setObjectName("btn_select_database_backupLocation")
|
||||
self.databasesettings.addWidget(self.btn_select_database_backupLocation, 2, 2, 1, 1)
|
||||
self.formLayout.setLayout(5, QtWidgets.QFormLayout.ItemRole.FieldRole, self.databasesettings)
|
||||
self.label_9 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_9.setObjectName("label_9")
|
||||
self.formLayout.setWidget(6, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_9)
|
||||
self.gridLayout = QtWidgets.QGridLayout()
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.btn_select_report_path = QtWidgets.QToolButton(parent=Dialog)
|
||||
self.btn_select_report_path.setObjectName("btn_select_report_path")
|
||||
self.gridLayout.addWidget(self.btn_select_report_path, 2, 2, 1, 1)
|
||||
self.label_10 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_10.setText("")
|
||||
self.label_10.setObjectName("label_10")
|
||||
self.gridLayout.addWidget(self.label_10, 1, 0, 1, 1)
|
||||
self.check_generate_report = QtWidgets.QCheckBox(parent=Dialog)
|
||||
self.check_generate_report.setObjectName("check_generate_report")
|
||||
self.gridLayout.addWidget(self.check_generate_report, 1, 1, 1, 1)
|
||||
self.report_path = QtWidgets.QLineEdit(parent=Dialog)
|
||||
self.report_path.setObjectName("report_path")
|
||||
self.gridLayout.addWidget(self.report_path, 2, 1, 1, 1)
|
||||
self.label_8 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_8.setObjectName("label_8")
|
||||
self.gridLayout.addWidget(self.label_8, 2, 0, 1, 1)
|
||||
self.label_11 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_11.setObjectName("label_11")
|
||||
self.gridLayout.addWidget(self.label_11, 0, 0, 1, 1)
|
||||
self.report_day = QtWidgets.QComboBox(parent=Dialog)
|
||||
self.report_day.setObjectName("report_day")
|
||||
self.report_day.addItem("")
|
||||
self.report_day.addItem("")
|
||||
self.report_day.addItem("")
|
||||
self.report_day.addItem("")
|
||||
self.report_day.addItem("")
|
||||
self.gridLayout.addWidget(self.report_day, 0, 1, 1, 1)
|
||||
self.formLayout.setLayout(6, QtWidgets.QFormLayout.ItemRole.FieldRole, self.gridLayout)
|
||||
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
|
||||
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Discard|QtWidgets.QDialogButtonBox.StandardButton.Ok)
|
||||
self.buttonBox.setObjectName("buttonBox")
|
||||
self.formLayout.setWidget(9, QtWidgets.QFormLayout.ItemRole.FieldRole, self.buttonBox)
|
||||
self.shortcutchanger = QtWidgets.QTableWidget(parent=Dialog)
|
||||
self.shortcutchanger.setObjectName("shortcutchanger")
|
||||
self.shortcutchanger.setColumnCount(3)
|
||||
self.shortcutchanger.setRowCount(0)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.shortcutchanger.setHorizontalHeaderItem(0, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.shortcutchanger.setHorizontalHeaderItem(1, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.shortcutchanger.setHorizontalHeaderItem(2, item)
|
||||
self.formLayout.setWidget(8, QtWidgets.QFormLayout.ItemRole.FieldRole, self.shortcutchanger)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.label_14 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_14.setObjectName("label_14")
|
||||
self.verticalLayout.addWidget(self.label_14)
|
||||
self.label_15 = QtWidgets.QLabel(parent=Dialog)
|
||||
self.label_15.setObjectName("label_15")
|
||||
self.verticalLayout.addWidget(self.label_15)
|
||||
self.formLayout.setLayout(8, QtWidgets.QFormLayout.ItemRole.LabelRole, self.verticalLayout)
|
||||
|
||||
self.retranslateUi(Dialog)
|
||||
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
|
||||
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
|
||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||
Dialog.setTabOrder(self.institution_name, self.database_path)
|
||||
Dialog.setTabOrder(self.database_path, self.database_name)
|
||||
Dialog.setTabOrder(self.database_name, self.database_backupLocation)
|
||||
Dialog.setTabOrder(self.database_backupLocation, self.btn_select_database_path)
|
||||
Dialog.setTabOrder(self.btn_select_database_path, self.btn_select_database_name)
|
||||
Dialog.setTabOrder(self.btn_select_database_name, self.btn_select_database_backupLocation)
|
||||
|
||||
def retranslateUi(self, Dialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
|
||||
self.label.setText(_translate("Dialog", "Name der Einrichtung"))
|
||||
self.label_2.setText(_translate("Dialog", "Leihdauer"))
|
||||
self.label_13.setText(_translate("Dialog", "Tage(n)"))
|
||||
self.label_7.setText(_translate("Dialog", "Inaktive Nutzer\n"
|
||||
"Löschen nach"))
|
||||
self.label_12.setText(_translate("Dialog", "Tage(n)"))
|
||||
self.returnMode.setToolTip(_translate("Dialog", "Wenn aktiv: Wenn ein Medium zurückgegeben wird, wird die nächste Aktion des Moduswechsels zum normalen Rückgabemodus führen"))
|
||||
self.returnMode.setText(_translate("Dialog", "Erweiterter Rückgabemodus"))
|
||||
self.label_3.setText(_translate("Dialog", "Datenbank"))
|
||||
self.label_4.setText(_translate("Dialog", "Speicherort"))
|
||||
self.label_6.setText(_translate("Dialog", "Sicherungspfad"))
|
||||
self.label_5.setText(_translate("Dialog", "Datenbankname"))
|
||||
self.btn_select_database_path.setText(_translate("Dialog", "..."))
|
||||
self.btn_select_database_name.setText(_translate("Dialog", "..."))
|
||||
self.btn_select_database_backupLocation.setText(_translate("Dialog", "..."))
|
||||
self.label_9.setText(_translate("Dialog", "Bericht"))
|
||||
self.btn_select_report_path.setText(_translate("Dialog", "..."))
|
||||
self.check_generate_report.setText(_translate("Dialog", "Bericht erstellen"))
|
||||
self.label_8.setText(_translate("Dialog", "Speicherpfad"))
|
||||
self.label_11.setText(_translate("Dialog", "Tag"))
|
||||
self.report_day.setItemText(0, _translate("Dialog", "Montag"))
|
||||
self.report_day.setItemText(1, _translate("Dialog", "Dienstag"))
|
||||
self.report_day.setItemText(2, _translate("Dialog", "Mittwoch"))
|
||||
self.report_day.setItemText(3, _translate("Dialog", "Donnerstag"))
|
||||
self.report_day.setItemText(4, _translate("Dialog", "Freitag"))
|
||||
item = self.shortcutchanger.horizontalHeaderItem(0)
|
||||
item.setText(_translate("Dialog", "Name"))
|
||||
item = self.shortcutchanger.horizontalHeaderItem(1)
|
||||
item.setText(_translate("Dialog", "Standard"))
|
||||
item = self.shortcutchanger.horizontalHeaderItem(2)
|
||||
item.setText(_translate("Dialog", "Aktuell"))
|
||||
self.label_14.setText(_translate("Dialog", "Shortcuts"))
|
||||
self.label_15.setText(_translate("Dialog", "(Erst nach Neustart\n"
|
||||
"wirksam)"))
|
||||
8
src/ui/sources/Ui_dialog_settings.ui.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\LibrarySystem\src\ui\sources\dialog_settings.ui.vqAAbY'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
110
src/ui/sources/Ui_main_Loans.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\LibrarySystem\src\ui\sources\main_Loans.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(899, 658)
|
||||
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.radio_all = QtWidgets.QRadioButton(parent=self.centralwidget)
|
||||
self.radio_all.setChecked(True)
|
||||
self.radio_all.setObjectName("radio_all")
|
||||
self.horizontalLayout.addWidget(self.radio_all)
|
||||
self.radio_current = QtWidgets.QRadioButton(parent=self.centralwidget)
|
||||
self.radio_current.setObjectName("radio_current")
|
||||
self.horizontalLayout.addWidget(self.radio_current)
|
||||
self.radio_overdue = QtWidgets.QRadioButton(parent=self.centralwidget)
|
||||
self.radio_overdue.setObjectName("radio_overdue")
|
||||
self.horizontalLayout.addWidget(self.radio_overdue)
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.searchbar = QtWidgets.QLineEdit(parent=self.centralwidget)
|
||||
self.searchbar.setObjectName("searchbar")
|
||||
self.horizontalLayout_2.addWidget(self.searchbar)
|
||||
self.searchFields = QtWidgets.QComboBox(parent=self.centralwidget)
|
||||
self.searchFields.setObjectName("searchFields")
|
||||
self.searchFields.addItem("")
|
||||
self.searchFields.addItem("")
|
||||
self.searchFields.addItem("")
|
||||
self.horizontalLayout_2.addWidget(self.searchFields)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
||||
self.loanTable = QtWidgets.QTableWidget(parent=self.centralwidget)
|
||||
self.loanTable.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||
self.loanTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection)
|
||||
self.loanTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
self.loanTable.setObjectName("loanTable")
|
||||
self.loanTable.setColumnCount(7)
|
||||
self.loanTable.setRowCount(0)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.loanTable.setHorizontalHeaderItem(0, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.loanTable.setHorizontalHeaderItem(1, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.loanTable.setHorizontalHeaderItem(2, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.loanTable.setHorizontalHeaderItem(3, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.loanTable.setHorizontalHeaderItem(4, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.loanTable.setHorizontalHeaderItem(5, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
self.loanTable.setHorizontalHeaderItem(6, item)
|
||||
self.verticalLayout.addWidget(self.loanTable)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 899, 22))
|
||||
self.menubar.setObjectName("menubar")
|
||||
self.menuDatei = QtWidgets.QMenu(parent=self.menubar)
|
||||
self.menuDatei.setObjectName("menuDatei")
|
||||
MainWindow.setMenuBar(self.menubar)
|
||||
self.actionBeenden = QtGui.QAction(parent=MainWindow)
|
||||
self.actionBeenden.setObjectName("actionBeenden")
|
||||
self.menuDatei.addAction(self.actionBeenden)
|
||||
self.menubar.addAction(self.menuDatei.menuAction())
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.actionBeenden.triggered.connect(MainWindow.close) # type: ignore
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
||||
self.radio_all.setText(_translate("MainWindow", "Alle Ausleihen"))
|
||||
self.radio_current.setText(_translate("MainWindow", "Aktuell Entliehene Medien"))
|
||||
self.radio_overdue.setText(_translate("MainWindow", "Überzogene Medien"))
|
||||
self.searchFields.setItemText(0, _translate("MainWindow", "Titel"))
|
||||
self.searchFields.setItemText(1, _translate("MainWindow", "Signatur"))
|
||||
self.searchFields.setItemText(2, _translate("MainWindow", "Nutzer"))
|
||||
item = self.loanTable.horizontalHeaderItem(0)
|
||||
item.setText(_translate("MainWindow", "ISBN"))
|
||||
item = self.loanTable.horizontalHeaderItem(1)
|
||||
item.setText(_translate("MainWindow", "Signatur"))
|
||||
item = self.loanTable.horizontalHeaderItem(2)
|
||||
item.setText(_translate("MainWindow", "Titel"))
|
||||
item = self.loanTable.horizontalHeaderItem(3)
|
||||
item.setText(_translate("MainWindow", "Nutzerkonto"))
|
||||
item = self.loanTable.horizontalHeaderItem(4)
|
||||
item.setText(_translate("MainWindow", "entliehen am"))
|
||||
item = self.loanTable.horizontalHeaderItem(5)
|
||||
item.setText(_translate("MainWindow", "entliehen bis"))
|
||||
item = self.loanTable.horizontalHeaderItem(6)
|
||||
item.setText(_translate("MainWindow", "Zurückgegeben am"))
|
||||
self.menuDatei.setTitle(_translate("MainWindow", "Datei"))
|
||||
self.actionBeenden.setText(_translate("MainWindow", "Beenden"))
|
||||
self.actionBeenden.setShortcut(_translate("MainWindow", "Q"))
|
||||