Merge branch 'dev'
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
[tool.bumpversion]
|
||||
current_version = "0.2.1"
|
||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||
serialize = ["{major}.{minor}.{patch}"]
|
||||
search = "{current_version}"
|
||||
replace = "{new_version}"
|
||||
regex = false
|
||||
ignore_missing_version = false
|
||||
ignore_missing_files = false
|
||||
tag = true
|
||||
sign_tags = false
|
||||
tag_name = "v{new_version}"
|
||||
tag_message = "Bump version: {current_version} → {new_version}"
|
||||
allow_dirty = false
|
||||
commit = true
|
||||
message = "Bump version: {current_version} → {new_version}"
|
||||
commit_args = ""
|
||||
setup_hooks = []
|
||||
pre_commit_hooks = []
|
||||
post_commit_hooks = []
|
||||
[[tool.bumpversion.files]]
|
||||
filename = "src/__init__.py"
|
||||
@@ -42,16 +42,6 @@ jobs:
|
||||
id: bump_version
|
||||
run: |
|
||||
uv tool run bump-my-version bump ${{ github.event.inputs.bump }} --tag --allow-dirty
|
||||
- name: Add release notes
|
||||
id: add_release_notes
|
||||
run: |
|
||||
echo "RELEASE_NOTES<<EOF" >> $GITHUB_ENV
|
||||
echo "${{ github.event.inputs.release_notes }}" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
- name: Create Gitea Release
|
||||
if: ${{ github.event.inputs.github_release == 'true' }}
|
||||
uses: softprops/action-gh-release@v1
|
||||
|
||||
- name: Add release notes
|
||||
id: add_release_notes
|
||||
run: |
|
||||
@@ -62,14 +52,7 @@ jobs:
|
||||
if: ${{ github.event.inputs.github_release == 'true' }}
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ github.sha }}
|
||||
release_name: Release ${{ github.sha }}
|
||||
body: ${{ env.RELEASE_NOTES }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
tag_name:
|
||||
release_name: Release ${{ github.sha }}
|
||||
body: ${{ env.RELEASE_NOTES }}
|
||||
draft: false
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -229,4 +229,5 @@ config.yaml
|
||||
|
||||
logs/
|
||||
*.pdf
|
||||
*.docx
|
||||
*.docx
|
||||
test.py
|
||||
|
||||
31
build.py
Normal file
31
build.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
with open(".version", "r") as version_file:
|
||||
version = version_file.read().strip()
|
||||
|
||||
print("Building the project...")
|
||||
print("Cleaning build dir...")
|
||||
# clear dist directory
|
||||
if os.path.exists("dist"):
|
||||
shutil.rmtree("dist")
|
||||
os.makedirs("dist")
|
||||
print("Build directory cleaned.")
|
||||
build = input("Include console in build? (y/n): ").strip().lower()
|
||||
|
||||
|
||||
command = f"uv run python -m nuitka --standalone --output-dir=dist --include-data-dir=./config=config --include-data-dir=./site=site --include-data-dir=./icons=icons --include-data-dir=./mail_vorlagen=mail_vorlagen --enable-plugin=pyside6 --product-name=SemesterApparatsManager --product-version={version} --output-filename=SAM.exe --windows-icon-from-ico=icons/logo.ico"
|
||||
executable = "main.py"
|
||||
|
||||
|
||||
if build == 'y':
|
||||
|
||||
os.system(f"{command} {executable}")
|
||||
else:
|
||||
command += " --windows-console-mode=disable"
|
||||
os.system(f"{command} {executable}")
|
||||
|
||||
# rename main.dist in dist dir to SemesterApparatsManager
|
||||
os.rename("dist/main.dist", "dist/SemesterApparatsManager")
|
||||
|
||||
print("Build complete.")
|
||||
57
config/base_config.yaml
Normal file
57
config/base_config.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
default_apps: true
|
||||
save_path: .
|
||||
icon_path: icons/
|
||||
openAI:
|
||||
api_key:
|
||||
model:
|
||||
zotero:
|
||||
api_key:
|
||||
library_id:
|
||||
library_type: user
|
||||
database:
|
||||
name: semesterapparate.db
|
||||
path: .
|
||||
temp: ~/AppData/Local/SAM/SemesterApparatsManager/Cache
|
||||
mail:
|
||||
smtp_server:
|
||||
port:
|
||||
sender:
|
||||
printer_mail:
|
||||
user_name:
|
||||
use_user_name: true
|
||||
password:
|
||||
signature:
|
||||
colors:
|
||||
dark: '#6b6160'
|
||||
light: '#000000'
|
||||
warning: '#ff0000'
|
||||
success: '#00ff00'
|
||||
icons:
|
||||
locked: locked.svg
|
||||
logo: logo.ico
|
||||
show_password: visibility_off.svg
|
||||
hide_password: visibility_on.svg
|
||||
settings: settings.svg
|
||||
today: calendar_today.svg
|
||||
save: save.svg
|
||||
edit_note: edit_note.svg
|
||||
warning: warning.svg
|
||||
error: error.svg
|
||||
mail: mail.svg
|
||||
semester: semester.svg
|
||||
template_fail: test_fail.svg
|
||||
offAction: shutdown.svg
|
||||
info: info.svg
|
||||
help: help.svg
|
||||
close: close.svg
|
||||
notification: notification.svg
|
||||
valid_true: check_success.svg
|
||||
valid_false: check_fail.svg
|
||||
edit: edit.svg
|
||||
important_warn: red_warn.svg
|
||||
person: person_add.svg
|
||||
database: database.svg
|
||||
icons: icons.svg
|
||||
api: api.svg
|
||||
print: print.svg
|
||||
db_search: db_search.svg
|
||||
@@ -1,8 +1,19 @@
|
||||
from typing import Optional
|
||||
from typing import Optional, Any, Union
|
||||
from dataclasses import dataclass
|
||||
from omegaconf import OmegaConf, DictConfig
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
@dataclass
|
||||
class OpenAI:
|
||||
api_key: str
|
||||
model: str
|
||||
|
||||
def getattr(self, name: str):
|
||||
return getattr(self, name)
|
||||
|
||||
def _setattr(self, name: str, value: Any):
|
||||
setattr(self, name, value)
|
||||
|
||||
@dataclass
|
||||
class Zotero:
|
||||
@@ -10,25 +21,29 @@ class Zotero:
|
||||
library_id: str
|
||||
library_type: str
|
||||
|
||||
def getattr(self, name):
|
||||
def getattr(self, name: str):
|
||||
return getattr(self, name)
|
||||
|
||||
def _setattr(self, name, value):
|
||||
def _setattr(self, name: str, value: Any):
|
||||
setattr(self, name, value)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Database:
|
||||
name: str
|
||||
path: str
|
||||
temp: str
|
||||
|
||||
def getattr(self, name):
|
||||
path: Union[str, Path, None]
|
||||
temp: Union[str, Path, None]
|
||||
def getattr(self, name: str):
|
||||
return getattr(self, name)
|
||||
|
||||
def _setattr(self, name, value):
|
||||
def _setattr(self, name: str, value: Any):
|
||||
setattr(self, name, value)
|
||||
|
||||
def __post_init__(self):
|
||||
if isinstance(self.path, str):
|
||||
self.path = Path(self.path).expanduser()
|
||||
if isinstance(self.temp, str):
|
||||
self.temp = Path(self.temp).expanduser()
|
||||
|
||||
@dataclass
|
||||
class Mail:
|
||||
@@ -59,13 +74,13 @@ class Mail:
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px;
|
||||
margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>"""
|
||||
|
||||
def getattr(self, name):
|
||||
def getattr(self, name: str):
|
||||
return getattr(self, name)
|
||||
|
||||
def _setattr(self, name, value):
|
||||
def _setattr(self, name: str, value: Any):
|
||||
setattr(self, name, value)
|
||||
|
||||
def setValue(self, **kwargs):
|
||||
def setValue(self, **kwargs: Any):
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
@@ -79,7 +94,7 @@ class Icons:
|
||||
self._colors = None
|
||||
self._icons = None
|
||||
|
||||
def assign(self, key, value):
|
||||
def assign(self, key: str, value: Any):
|
||||
setattr(self, key, value)
|
||||
|
||||
@property
|
||||
@@ -87,7 +102,7 @@ class Icons:
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, value):
|
||||
def path(self, value: Any):
|
||||
self._path = value
|
||||
|
||||
@property
|
||||
@@ -95,7 +110,7 @@ class Icons:
|
||||
return self._colors
|
||||
|
||||
@colors.setter
|
||||
def colors(self, value):
|
||||
def colors(self, value: Any):
|
||||
self._colors = value
|
||||
|
||||
@property
|
||||
@@ -103,10 +118,10 @@ class Icons:
|
||||
return self._icons
|
||||
|
||||
@icons.setter
|
||||
def icons(self, value):
|
||||
def icons(self, value: Any):
|
||||
self._icons = value
|
||||
|
||||
def get(self, name):
|
||||
def get(self, name: str):
|
||||
return self.icons.get(name)
|
||||
|
||||
|
||||
@@ -121,7 +136,7 @@ class Config:
|
||||
"""
|
||||
|
||||
_config: Optional[DictConfig] = None
|
||||
|
||||
config_exists: bool = True
|
||||
def __init__(self, config_path: str):
|
||||
"""
|
||||
Loads the configuration file and stores it for future access.
|
||||
@@ -133,10 +148,22 @@ class Config:
|
||||
FileNotFoundError: Configuration file not found
|
||||
"""
|
||||
if not os.path.exists(config_path):
|
||||
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
||||
# copy base config file to the given path
|
||||
base = "config/base_config.yaml"
|
||||
if not os.path.exists(base):
|
||||
raise FileNotFoundError(f"Base configuration file not found: {base}")
|
||||
with open(base, "r") as base_file:
|
||||
base_config = base_file.read()
|
||||
with open(config_path, "w") as config_file:
|
||||
config_file.write(base_config)
|
||||
self.config_exists = False
|
||||
self._config = OmegaConf.load(config_path)
|
||||
self.config_path = config_path
|
||||
|
||||
@property
|
||||
def exists(self) -> bool:
|
||||
return self.config_exists
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Saves the current configuration to the file.
|
||||
@@ -146,16 +173,22 @@ class Config:
|
||||
"""
|
||||
OmegaConf.save(self._config, self.config_path)
|
||||
|
||||
def reload(self):
|
||||
"""
|
||||
Reloads the configuration from the file.
|
||||
"""
|
||||
self._config = OmegaConf.load(self.config_path)
|
||||
|
||||
@property
|
||||
def zotero(self):
|
||||
return Zotero(**self._config.zotero)
|
||||
|
||||
@property
|
||||
def zotero_attr(self, name):
|
||||
def zotero_attr(self, name: str):
|
||||
return getattr(self.zotero, name)
|
||||
|
||||
@zotero_attr.setter
|
||||
def zotero_attr(self, name, value):
|
||||
def zotero_attr(self, name: str, value: Any):
|
||||
self.zotero._setattr(name, value)
|
||||
|
||||
@property
|
||||
@@ -163,30 +196,37 @@ class Config:
|
||||
return Database(**self._config.database)
|
||||
|
||||
@property
|
||||
def database_attr(self, name):
|
||||
def database_attr(self, name: str):
|
||||
return getattr(self.database, name)
|
||||
|
||||
@database_attr.setter
|
||||
def database_attr(self, name, value):
|
||||
def database_attr(self, name: str, value: Any):
|
||||
self.database._setattr(name, value)
|
||||
|
||||
@property
|
||||
def openAI(self):
|
||||
return OpenAI(**self._config.openAI)
|
||||
|
||||
@property
|
||||
def mail(self):
|
||||
return Mail(**self._config.mail)
|
||||
|
||||
def mail_attr(self, name):
|
||||
def mail_attr(self, name: str):
|
||||
return getattr(self.mail, name)
|
||||
|
||||
def set_mail_attr(self, name, value):
|
||||
def set_mail_attr(self, name: str, value: Any):
|
||||
OmegaConf.update(self._config, f"mail.{name}", value)
|
||||
|
||||
def set_database_attr(self, name, value):
|
||||
def set_database_attr(self, name: str, value: Any):
|
||||
OmegaConf.update(self._config, f"database.{name}", value)
|
||||
|
||||
def set_zotero_attr(self, name, value):
|
||||
def set_zotero_attr(self, name: str, value: Any):
|
||||
OmegaConf.update(self._config, f"zotero.{name}", value)
|
||||
|
||||
def set_icon_attr(self, name, value):
|
||||
def set_openai_attr(self, name: str, value: Any):
|
||||
OmegaConf.update(self._config, f"openAI.{name}", value)
|
||||
|
||||
def set_icon_attr(self, name: str, value: Any):
|
||||
OmegaConf.update(self._config, f"icons.{name}", value)
|
||||
|
||||
@property
|
||||
|
||||
BIN
icons/logo.ico
BIN
icons/logo.ico
Binary file not shown.
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 264 KiB |
33
mail.py
33
mail.py
@@ -1,33 +0,0 @@
|
||||
|
||||
from PyQt6 import QtWidgets
|
||||
|
||||
from src.ui.dialogs.mail import Mail_Dialog
|
||||
|
||||
|
||||
def launch_gui(app_id="", app_name="", app_subject="", prof_name="", prof_mail=""):
|
||||
QtWidgets.QApplication([""])
|
||||
|
||||
dialog = Mail_Dialog(
|
||||
app_id=app_id,
|
||||
app_name=app_name,
|
||||
app_subject=app_subject,
|
||||
prof_name=prof_name,
|
||||
prof_mail=prof_mail,
|
||||
# default_mail="Information bezüglich der Auflösung des Semesterapparates",
|
||||
)
|
||||
dialog.exec()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app_id = "123"
|
||||
app_name = "Test"
|
||||
app_subject = "TestFach"
|
||||
prof_name = "Test"
|
||||
prof_mail = "kirchneralexander020@gmail.com"
|
||||
launch_gui(
|
||||
app_id=app_id,
|
||||
app_name=app_name,
|
||||
app_subject=app_subject,
|
||||
prof_name=prof_name,
|
||||
prof_mail=prof_mail,
|
||||
)
|
||||
17
main.py
17
main.py
@@ -1,4 +1,19 @@
|
||||
from src import first_launch, settings
|
||||
from src.ui.widgets.welcome_wizard import launch_wizard as startup
|
||||
from PySide6 import QtWidgets
|
||||
import sys
|
||||
from src.ui.userInterface import launch_gui as UI
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
UI()
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
if not first_launch:
|
||||
setup = startup()
|
||||
if setup == 1:
|
||||
settings.reload()
|
||||
# kill qApplication singleton
|
||||
UI()
|
||||
else:
|
||||
sys.exit()
|
||||
else:
|
||||
UI()
|
||||
@@ -1,10 +1,11 @@
|
||||
[project]
|
||||
name = "semesterapparatsmanager"
|
||||
version = "0.1.0"
|
||||
version = "0.2.1"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"appdirs>=1.4.4",
|
||||
"beautifulsoup4>=4.12.3",
|
||||
"bump-my-version>=0.29.0",
|
||||
"chardet>=5.2.0",
|
||||
@@ -17,10 +18,11 @@ dependencies = [
|
||||
"mkdocs-material-extensions>=1.3.1",
|
||||
"natsort>=8.4.0",
|
||||
"omegaconf>=2.3.0",
|
||||
"openai>=1.79.0",
|
||||
"pandas>=2.2.3",
|
||||
"playwright>=1.49.1",
|
||||
"pyqt6>=6.8.0",
|
||||
"pyqtgraph>=0.13.7",
|
||||
"pyramid>=2.0.2",
|
||||
"pyside6>=6.9.1",
|
||||
"python-docx>=1.1.2",
|
||||
"pyzotero>=1.6.4",
|
||||
"ratelimit>=2.2.1",
|
||||
@@ -33,3 +35,29 @@ dev = [
|
||||
"icecream>=2.1.4",
|
||||
"nuitka>=2.5.9",
|
||||
]
|
||||
|
||||
[tool.bumpversion]
|
||||
current_version = "0.2.1"
|
||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||
serialize = ["{major}.{minor}.{patch}"]
|
||||
search = "{current_version}"
|
||||
replace = "{new_version}"
|
||||
regex = false
|
||||
ignore_missing_version = false
|
||||
ignore_missing_files = false
|
||||
tag = true
|
||||
sign_tags = false
|
||||
tag_name = "v{new_version}"
|
||||
tag_message = "Bump version: {current_version} → {new_version}"
|
||||
allow_dirty = true
|
||||
commit = true
|
||||
message = "Bump version: {current_version} → {new_version}"
|
||||
moveable_tags = []
|
||||
commit_args = ""
|
||||
setup_hooks = []
|
||||
pre_commit_hooks = []
|
||||
post_commit_hooks = []
|
||||
[[tool.bumpversion.files]]
|
||||
filename = "src/__init__.py"
|
||||
[[tool.bumpversion.files]]
|
||||
filename = ".version"
|
||||
|
||||
@@ -1,16 +1,33 @@
|
||||
__all__ = ["__version__", "__author__", "Icon", "settings"]
|
||||
from config import Config
|
||||
import os
|
||||
|
||||
|
||||
settings = Config("config/config.yaml")
|
||||
if not os.path.exists(settings.database.temp):
|
||||
os.mkdir(settings.database.temp)
|
||||
from .utils.icon import Icon
|
||||
|
||||
__version__ = "0.2.1"
|
||||
__author__ = "Alexander Kirchner"
|
||||
__all__ = ["__version__", "__author__", "Icon", "settings"]
|
||||
|
||||
import os
|
||||
|
||||
from appdirs import AppDirs
|
||||
|
||||
from config import Config
|
||||
|
||||
|
||||
app = AppDirs("SemesterApparatsManager", "SAM")
|
||||
LOG_DIR = app.user_log_dir
|
||||
CONFIG_DIR = app.user_config_dir
|
||||
if not os.path.exists(LOG_DIR):
|
||||
os.makedirs(LOG_DIR)
|
||||
if not os.path.exists(CONFIG_DIR):
|
||||
os.makedirs(CONFIG_DIR)
|
||||
|
||||
|
||||
settings = Config(f"{CONFIG_DIR}/config.yaml")
|
||||
DATABASE_DIR = (
|
||||
app.user_config_dir if settings.database.path is None else settings.database.path
|
||||
)
|
||||
if not os.path.exists(DATABASE_DIR):
|
||||
os.makedirs(DATABASE_DIR)
|
||||
first_launch = settings.exists
|
||||
if not os.path.exists(settings.database.temp.expanduser()):
|
||||
settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True)
|
||||
from .utils.icon import Icon
|
||||
|
||||
if not os.path.exists("logs"):
|
||||
os.mkdir("logs")
|
||||
|
||||
@@ -2,6 +2,14 @@ import hashlib
|
||||
import random
|
||||
|
||||
from .database import Database
|
||||
import loguru
|
||||
import sys
|
||||
from src import LOG_DIR
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
# change passwords for apparats, change passwords for users, list users, create and delete users etc
|
||||
@@ -9,9 +17,14 @@ from .database import Database
|
||||
class AdminCommands:
|
||||
"""Basic Admin commands for the admin console. This class is used to create, delete, and list users. It also has the ability to change passwords for users."""
|
||||
|
||||
def __init__(self):
|
||||
"""Defaulf Constructor for the AdminCommands class."""
|
||||
self.db = Database()
|
||||
def __init__(self, db_path=None):
|
||||
"""Default Constructor for the AdminCommands class."""
|
||||
if db_path is None:
|
||||
self.db = Database()
|
||||
else:
|
||||
self.db = Database(db_path=db_path)
|
||||
log.info("AdminCommands initialized with database connection.")
|
||||
log.debug("location: {}", self.db.db_path)
|
||||
|
||||
def create_password(self, password: str) -> tuple[str, str]:
|
||||
"""Create a hashed password and a salt for the password.
|
||||
@@ -44,6 +57,20 @@ class AdminCommands:
|
||||
hashed_password = self.hash_password("admin")
|
||||
self.db.createUser("admin", salt + hashed_password, "admin", salt)
|
||||
|
||||
def create_user(self, username: str, password: str, role: str = "user") -> bool:
|
||||
"""Create a new user in the database.
|
||||
|
||||
Args:
|
||||
username (str): the username of the user to be created.
|
||||
password (str): the password of the user to be created.
|
||||
role (str, optional): the role of the user to be created. Defaults to "user".
|
||||
"""
|
||||
hashed_password, salt = self.create_password(password)
|
||||
status = self.db.createUser(
|
||||
user=username, password=salt + hashed_password, role=role, salt=salt
|
||||
)
|
||||
return status
|
||||
|
||||
def hash_password(self, password: str) -> str:
|
||||
"""Hash a password using SHA256.
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ from src.backend.database import Database
|
||||
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
from src import LOG_DIR
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
db = Database()
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import os
|
||||
import sqlite3 as sql
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from src import settings
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sqlite3 as sql
|
||||
import sys
|
||||
import tempfile
|
||||
from dataclasses import asdict
|
||||
from pathlib import Path
|
||||
from string import ascii_lowercase as lower
|
||||
from string import digits, punctuation
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
|
||||
import loguru
|
||||
|
||||
from src import LOG_DIR, settings, DATABASE_DIR
|
||||
from src.backend.db import (
|
||||
CREATE_ELSA_FILES_TABLE,
|
||||
CREATE_ELSA_MEDIA_TABLE,
|
||||
@@ -20,17 +26,16 @@ from src.backend.db import (
|
||||
CREATE_TABLE_USER,
|
||||
)
|
||||
from src.errors import AppPresentError, NoResultError
|
||||
from src.logic import ApparatData, BookData, Prof, Apparat, ELSA
|
||||
from src.logic import ELSA, Apparat, ApparatData, BookData, Prof
|
||||
from src.logic.constants import SEMAP_MEDIA_ACCOUNTS
|
||||
from src.utils.blob import create_blob
|
||||
|
||||
from .semester import Semester
|
||||
from string import ascii_lowercase as lower, digits, punctuation
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
|
||||
@@ -39,12 +44,11 @@ ascii_lowercase = lower + digits + punctuation
|
||||
|
||||
# get the line that called the function
|
||||
class Database:
|
||||
database = settings.database
|
||||
"""
|
||||
Initialize the database and create the tables if they do not exist.
|
||||
"""
|
||||
|
||||
def __init__(self, db_path: str = None):
|
||||
def __init__(self, db_path: Union[Path, None] = None):
|
||||
"""
|
||||
Default constructor for the database class
|
||||
|
||||
@@ -52,23 +56,41 @@ class Database:
|
||||
db_path (str, optional): Optional Path for testing / specific purposes. Defaults to None.
|
||||
"""
|
||||
if db_path is None:
|
||||
self.db_path = self.database.path + self.database.name
|
||||
self.db_path = self.db_path.replace("~", str(Path.home()))
|
||||
log.debug(self.db_path)
|
||||
if settings.database.path is not None:
|
||||
self.db_path = Path(
|
||||
settings.database.path.expanduser(), settings.database.name
|
||||
)
|
||||
else:
|
||||
self.db_path = None
|
||||
|
||||
# self.db_path = self.db_path.replace("~", str(Path.home()))
|
||||
else:
|
||||
self.db_path = db_path
|
||||
self.checkDatabaseStatus()
|
||||
log.debug(f"Database path: {self.db_path}")
|
||||
self.db_initialized = False
|
||||
|
||||
def initializeDatabase(self):
|
||||
if not self.db_initialized:
|
||||
self.checkDatabaseStatus()
|
||||
self.db_initialized = True
|
||||
|
||||
def overwritePath(self, new_db_path: str):
|
||||
log.debug("got new path, overwriting")
|
||||
self.db_path = Path(new_db_path)
|
||||
|
||||
def checkDatabaseStatus(self):
|
||||
path = self.database.path
|
||||
path = path.replace("~", str(Path.home()))
|
||||
path = os.path.abspath(path)
|
||||
path = settings.database.path
|
||||
if path is None:
|
||||
path = Path(DATABASE_DIR)
|
||||
# path = path.replace("~", str(Path.home()))
|
||||
# path = os.path.abspath(path)
|
||||
if not os.path.exists(path):
|
||||
# create path
|
||||
# log.debug(path)
|
||||
os.makedirs(path)
|
||||
if self.get_db_contents() == []:
|
||||
log.critical("Database does not exist, creating tables")
|
||||
log.critical(f"Path: {path}")
|
||||
self.create_tables()
|
||||
self.insertSubjects()
|
||||
|
||||
@@ -503,11 +525,9 @@ class Database:
|
||||
str: The filename of the recreated file
|
||||
"""
|
||||
blob = self.getBlob(filename, app_id)
|
||||
tempdir = self.database.temp
|
||||
tempdir = tempdir.replace("~", str(Path.home()))
|
||||
tempdir_path = Path(tempdir)
|
||||
if not os.path.exists(tempdir_path):
|
||||
os.mkdir(tempdir_path)
|
||||
tempdir = settings.database.temp.expanduser()
|
||||
if not tempdir.exists():
|
||||
tempdir.mkdir(parents=True, exist_ok=True)
|
||||
file = tempfile.NamedTemporaryFile(
|
||||
delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}"
|
||||
)
|
||||
@@ -1197,10 +1217,13 @@ class Database:
|
||||
Returns:
|
||||
bool: True if the login was successful, False if not
|
||||
"""
|
||||
salt = self.query_db(
|
||||
"SELECT salt FROM user WHERE username=?", (user,), one=True
|
||||
)[0]
|
||||
if salt is None:
|
||||
try:
|
||||
salt = self.query_db(
|
||||
"SELECT salt FROM user WHERE username=?", (user,), one=True
|
||||
)[0]
|
||||
if salt is None:
|
||||
return False
|
||||
except TypeError:
|
||||
return False
|
||||
hashed_password = salt + hashed_password
|
||||
password = self.query_db(
|
||||
@@ -1276,6 +1299,13 @@ class Database:
|
||||
"INSERT OR IGNORE INTO user (username, password, role, salt) VALUES (?,?,?,?)",
|
||||
(user, password, role, salt),
|
||||
)
|
||||
# check if user was created
|
||||
return (
|
||||
self.query_db(
|
||||
"SELECT username FROM user WHERE username=?", (user,), one=True
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def deleteUser(self, user):
|
||||
"""delete an unser
|
||||
@@ -1463,11 +1493,10 @@ class Database:
|
||||
"SELECT fileblob FROM elsa_files WHERE filename=?", (filename,), one=True
|
||||
)[0]
|
||||
# log.debug(blob)
|
||||
tempdir = self.database.temp
|
||||
tempdir = tempdir.replace("~", str(Path.home()))
|
||||
tempdir_path = Path(tempdir)
|
||||
if not os.path.exists(tempdir_path):
|
||||
os.mkdir(tempdir_path)
|
||||
tempdir = settings.database.temp.expanduser()
|
||||
if not tempdir.exists():
|
||||
tempdir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
file = tempfile.NamedTemporaryFile(
|
||||
delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}"
|
||||
)
|
||||
|
||||
@@ -9,10 +9,7 @@ def delete_temp_contents():
|
||||
"""
|
||||
delete_temp_contents deletes the contents of the temp directory.
|
||||
"""
|
||||
path = database.temp
|
||||
path = path.replace("~", str(Path.home()))
|
||||
path = Path(path)
|
||||
path = path.resolve()
|
||||
path = database.temp.expanduser()
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
os.remove(os.path.join(root, file))
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
from PyQt6.QtCore import QThread
|
||||
from src.utils.documentation import run_mkdocs
|
||||
from PySide6.QtCore import QThread, Slot
|
||||
from src.utils.documentation import website, QuietHandler
|
||||
from wsgiref.simple_server import make_server
|
||||
|
||||
|
||||
class DocumentationThread(QThread):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._server = None # store server so we can shut it down
|
||||
|
||||
def run(self):
|
||||
# launch_documentation()
|
||||
run_mkdocs()
|
||||
self._server = make_server(
|
||||
"localhost", 8000, website(), handler_class=QuietHandler
|
||||
)
|
||||
while not self.isInterruptionRequested():
|
||||
self._server.handle_request()
|
||||
|
||||
@Slot() # slot you can connect to aboutToQuit
|
||||
def stop(self):
|
||||
self.requestInterruption() # ask the loop above to exit
|
||||
if self._server:
|
||||
self._server.shutdown() # unblock handle_request()
|
||||
@@ -1,146 +1,242 @@
|
||||
import datetime
|
||||
"""Semester helper class
|
||||
|
||||
A small utility around the *German* academic calendar that distinguishes
|
||||
between *Wintersemester* (WiSe) and *Sommersemester* (SoSe).
|
||||
|
||||
Key points
|
||||
----------
|
||||
* A **`Semester`** is identified by a *term* ("SoSe" or "WiSe") and the last two
|
||||
digits of the calendar year in which the term *starts*.
|
||||
* Formatting **never** pads the year with a leading zero – so ``6`` stays ``6``.
|
||||
* ``offset(n)`` and the static ``generate_missing`` reliably walk the timeline
|
||||
one semester at a time with correct year transitions:
|
||||
|
||||
SoSe 6 → **WiSe 6/7** → SoSe 7 → WiSe 7/8 → …
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import datetime
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
from src import LOG_DIR
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
# @dataclass
|
||||
class Semester:
|
||||
log.debug("Semester class loaded")
|
||||
"""Represents a German university semester (WiSe or SoSe)."""
|
||||
|
||||
_year: int | None = str(datetime.datetime.now().year)[2:]
|
||||
_semester: str | None = None
|
||||
# ------------------------------------------------------------------
|
||||
# Class‑level defaults – will be *copied* to each instance and then
|
||||
# potentially overwritten in ``__init__``.
|
||||
# ------------------------------------------------------------------
|
||||
_year: int | None = int(str(datetime.datetime.now().year)[2:]) # 24 → 24
|
||||
_semester: str | None = None # "WiSe" or "SoSe" – set later
|
||||
_month: int | None = datetime.datetime.now().month
|
||||
value: str = None
|
||||
log.debug(
|
||||
f"Initialized Semester class with values: month: {_month}, semester: {_semester}, year {_year}"
|
||||
)
|
||||
value: str | None = None # Human‑readable label, e.g. "WiSe 23/24"
|
||||
|
||||
def __post_init__(self):
|
||||
if isinstance(self._year, str):
|
||||
self._year = int(self._year)
|
||||
# ------------------------------------------------------------------
|
||||
# Construction helpers
|
||||
# ------------------------------------------------------------------
|
||||
def __init__(
|
||||
self,
|
||||
year: int | None = None,
|
||||
semester: str | None = None,
|
||||
month: int | None = None,
|
||||
) -> None:
|
||||
if year is not None:
|
||||
self._year = int(year)
|
||||
if semester is not None:
|
||||
if semester not in ("WiSe", "SoSe"):
|
||||
raise ValueError("semester must be 'WiSe' or 'SoSe'")
|
||||
self._semester = semester
|
||||
if month is not None:
|
||||
self._month = month
|
||||
|
||||
self.__post_init__()
|
||||
|
||||
def __post_init__(self) -> None: # noqa: D401 – keep original name
|
||||
if self._year is None:
|
||||
self._year = datetime.datetime.now().year[2:]
|
||||
self._year = int(str(datetime.datetime.now().year)[2:])
|
||||
if self._month is None:
|
||||
self._month = datetime.datetime.now().month
|
||||
if self._semester is None:
|
||||
self.generateSemester()
|
||||
self.computeValue()
|
||||
self._generate_semester_from_month()
|
||||
self._compute_value()
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
# ------------------------------------------------------------------
|
||||
# Dunder helpers
|
||||
# ------------------------------------------------------------------
|
||||
def __str__(self) -> str: # noqa: D401 – keep original name
|
||||
return self.value or "<invalid Semester>"
|
||||
|
||||
def generateSemester(self):
|
||||
if self._month <= 3 or self._month > 9:
|
||||
self._semester = "WiSe"
|
||||
else:
|
||||
self._semester = "SoSe"
|
||||
def __repr__(self) -> str: # Helpful for debugging lists
|
||||
return f"Semester({self._year!r}, {self._semester!r})"
|
||||
|
||||
@log.catch
|
||||
def computeValue(self):
|
||||
# year is only last two digits
|
||||
# ------------------------------------------------------------------
|
||||
# Internal helpers
|
||||
# ------------------------------------------------------------------
|
||||
def _generate_semester_from_month(self) -> None:
|
||||
"""Infer *WiSe* / *SoSe* from the month attribute."""
|
||||
self._semester = "WiSe" if (self._month <= 3 or self._month > 9) else "SoSe"
|
||||
|
||||
def _compute_value(self) -> None:
|
||||
"""Human‑readable semester label – e.g. ``WiSe 23/24`` or ``SoSe 24``."""
|
||||
year = self._year
|
||||
valueyear = str(year)
|
||||
if self._semester == "WiSe":
|
||||
if self._month < 4:
|
||||
valueyear = str(year - 1) + "/" + str(year)
|
||||
else:
|
||||
valueyear = str(year) + "/" + str(year + 1)
|
||||
self.value = f"{self._semester} {valueyear}"
|
||||
next_year = (year + 1) % 100 # wrap 99 → 0
|
||||
self.value = f"WiSe {year}/{next_year}"
|
||||
else: # SoSe
|
||||
self.value = f"SoSe {year}"
|
||||
|
||||
@log.catch
|
||||
def offset(self, value: int) -> str:
|
||||
"""Generate a new Semester object by offsetting the current semester by a given value
|
||||
# ------------------------------------------------------------------
|
||||
# Public API
|
||||
# ------------------------------------------------------------------
|
||||
def offset(self, value: int) -> "Semester":
|
||||
"""Return a new :class:`Semester` *value* steps away.
|
||||
|
||||
Args:
|
||||
value (int): The value by which the semester should be offset
|
||||
The algorithm maps every semester to a monotonically increasing
|
||||
*linear index* so that simple addition suffices:
|
||||
|
||||
Returns:
|
||||
str: the new semester value
|
||||
``index = year * 2 + (0 if SoSe else 1)``.
|
||||
"""
|
||||
assert isinstance(value, int), "Value must be an integer"
|
||||
if not isinstance(value, int):
|
||||
raise TypeError("value must be an int (number of semesters to jump)")
|
||||
if value == 0:
|
||||
return self
|
||||
if value > 0:
|
||||
if value % 2 == 0:
|
||||
return Semester(
|
||||
self._year - value // 2, self._semester - value // 2 + 1
|
||||
)
|
||||
else:
|
||||
semester = self._semester
|
||||
semester = "SoSe" if semester == "WiSe" else "WiSe"
|
||||
return Semester(self._year + value // 2, semester)
|
||||
else:
|
||||
if value % 2 == 0:
|
||||
return Semester(self.year + value // 2, self._semester)
|
||||
else:
|
||||
semester = self._semester
|
||||
semester = "SoSe" if semester == "WiSe" else "WiSe"
|
||||
return Semester(self._year + value // 2, semester)
|
||||
return Semester(self._year, self._semester)
|
||||
|
||||
def isPastSemester(self, semester) -> bool:
|
||||
"""Checks if the current Semester is a past Semester compared to the given Semester
|
||||
current_idx = self._year * 2 + (0 if self._semester == "SoSe" else 1)
|
||||
target_idx = current_idx + value
|
||||
if target_idx < 0:
|
||||
raise ValueError("offset would result in a negative year – not supported")
|
||||
|
||||
Args:
|
||||
semester (str): The semester to compare to
|
||||
new_year, semester_bit = divmod(target_idx, 2)
|
||||
new_semester = "SoSe" if semester_bit == 0 else "WiSe"
|
||||
return Semester(new_year, new_semester)
|
||||
|
||||
Returns:
|
||||
bool: True if the current semester is in the past, False otherwise
|
||||
"""
|
||||
if self.year < semester.year:
|
||||
# ------------------------------------------------------------------
|
||||
# Comparison helpers
|
||||
# ------------------------------------------------------------------
|
||||
def isPastSemester(self, other: "Semester") -> bool:
|
||||
if self.year < other.year:
|
||||
return True
|
||||
if self.year == semester.year:
|
||||
if self.semester == "WiSe" and semester.semester == "SoSe":
|
||||
return True
|
||||
if self.year == other.year:
|
||||
return (
|
||||
self.semester == "WiSe" and other.semester == "SoSe"
|
||||
) # WiSe before next SoSe
|
||||
return False
|
||||
|
||||
def isFutureSemester(self, semester: "Semester") -> bool:
|
||||
"""Checks if the current Semester is a future Semester compared to the given Semester
|
||||
|
||||
Args:
|
||||
semester (str): The semester to compare to
|
||||
|
||||
Returns:
|
||||
bool: True if the current semester is in the future, False otherwise
|
||||
"""
|
||||
if self.year > semester.year:
|
||||
def isFutureSemester(self, other: "Semester") -> bool:
|
||||
if self.year > other.year:
|
||||
return True
|
||||
if self.year == semester.year:
|
||||
if self.semester == "SoSe" and semester.semester == "WiSe":
|
||||
return True
|
||||
if self.year == other.year:
|
||||
return (
|
||||
self.semester == "SoSe" and other.semester == "WiSe"
|
||||
) # SoSe after WiSe of same year
|
||||
return False
|
||||
|
||||
def from_string(self, val: str):
|
||||
if " " in val:
|
||||
values = val.split(" ")
|
||||
if len(values) != 2:
|
||||
raise ValueError("Invalid semester format")
|
||||
self._semester = values[0]
|
||||
if len(values[1]) == 4:
|
||||
self._year = int(values[1][2:])
|
||||
# self._year = int(values[1])
|
||||
self.computeValue()
|
||||
return self
|
||||
def isMatch(self, other: "Semester") -> bool:
|
||||
return self.year == other.year and self.semester == other.semester
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Convenience properties
|
||||
# ------------------------------------------------------------------
|
||||
@property
|
||||
def next(self):
|
||||
def next(self) -> "Semester":
|
||||
return self.offset(1)
|
||||
|
||||
@property
|
||||
def previous(self):
|
||||
def previous(self) -> "Semester":
|
||||
return self.offset(-1)
|
||||
|
||||
@property
|
||||
def year(self):
|
||||
def year(self) -> int:
|
||||
return self._year
|
||||
|
||||
@property
|
||||
def semester(self):
|
||||
def semester(self) -> str:
|
||||
return self._semester
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Static helpers
|
||||
# ------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def generate_missing(start: "Semester", end: "Semester") -> list[str]:
|
||||
"""Return all consecutive semesters from *start* to *end* (inclusive)."""
|
||||
if not isinstance(start, Semester) or not isinstance(end, Semester):
|
||||
raise TypeError("start and end must be Semester instances")
|
||||
if start.isFutureSemester(end) and not start.isMatch(end):
|
||||
raise ValueError("'start' must not be after 'end'")
|
||||
|
||||
chain: list[Semester] = [start.value]
|
||||
current = start
|
||||
while not current.isMatch(end):
|
||||
current = current.next
|
||||
chain.append(current.value)
|
||||
if len(chain) > 1000: # sanity guard
|
||||
raise RuntimeError("generate_missing exceeded sane iteration limit")
|
||||
return chain
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Parsing helper
|
||||
# ------------------------------------------------------------------
|
||||
@classmethod
|
||||
def from_string(cls, s: str) -> "Semester":
|
||||
"""Parse a human‑readable semester label and return a :class:`Semester`.
|
||||
|
||||
Accepted formats (case‑insensitive)::
|
||||
|
||||
"SoSe <YY>" → SoSe of year YY
|
||||
"WiSe <YY>/<YY+1>" → Winter term starting in YY
|
||||
"WiSe <YY>" → Shorthand for the above (next year implied)
|
||||
|
||||
``YY`` may contain a leading zero ("06" → 6).
|
||||
"""
|
||||
if not isinstance(s, str):
|
||||
raise TypeError("s must be a string")
|
||||
|
||||
pattern = r"\s*(WiSe|SoSe)\s+(\d{1,2})(?:\s*/\s*(\d{1,2}))?\s*"
|
||||
m = re.fullmatch(pattern, s, flags=re.IGNORECASE)
|
||||
if not m:
|
||||
raise ValueError(
|
||||
"invalid semester string format – expected 'SoSe YY' or 'WiSe YY/YY' (spacing flexible)"
|
||||
)
|
||||
|
||||
term_raw, y1_str, y2_str = m.groups()
|
||||
term = term_raw.capitalize() # normalize case → "WiSe" or "SoSe"
|
||||
year = int(y1_str.lstrip("0") or "0") # "06" → 6, "0" stays 0
|
||||
|
||||
if term == "SoSe":
|
||||
if y2_str is not None:
|
||||
raise ValueError(
|
||||
"SoSe string should not contain '/' followed by a second year"
|
||||
)
|
||||
return cls(year, "SoSe")
|
||||
|
||||
# term == "WiSe"
|
||||
if y2_str is not None:
|
||||
next_year = int(y2_str.lstrip("0") or "0")
|
||||
expected_next = (year + 1) % 100
|
||||
if next_year != expected_next:
|
||||
raise ValueError("WiSe second year must equal first year + 1 (mod 100)")
|
||||
# Accept both explicit "WiSe 6/7" and shorthand "WiSe 6"
|
||||
return cls(year, "WiSe")
|
||||
|
||||
|
||||
# ------------------------- quick self‑test -------------------------
|
||||
if __name__ == "__main__":
|
||||
# Chain generation demo ------------------------------------------------
|
||||
s_start = Semester(6, "SoSe") # SoSe 6
|
||||
s_end = Semester(25, "WiSe") # WiSe 25/26
|
||||
chain = Semester.generate_missing(s_start, s_end)
|
||||
print("generate_missing:", [str(s) for s in chain])
|
||||
|
||||
# Parsing demo ---------------------------------------------------------
|
||||
for label in ["SoSe 6", "WiSe 6/7", "wise 23/24", "WiSe 9"]:
|
||||
print("from_string:", label, "→", Semester.from_string(label))
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
from PyQt6.QtCore import QThread
|
||||
from PyQt6.QtCore import pyqtSignal as Signal
|
||||
from PySide6.QtCore import QThread
|
||||
from PySide6.QtCore import Signal
|
||||
from src.backend import Database
|
||||
|
||||
from src.logic.webrequest import BibTextTransformer, WebRequest
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
from src import LOG_DIR
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
|
||||
log.add(sys.stdout)
|
||||
log.add(sys.stdout, level="INFO")
|
||||
|
||||
|
||||
class BookGrabber(QThread):
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import time
|
||||
|
||||
# from icecream import ic
|
||||
from PyQt6.QtCore import QThread
|
||||
from PyQt6.QtCore import pyqtSignal as Signal
|
||||
from PySide6.QtCore import QThread
|
||||
from PySide6.QtCore import Signal as Signal
|
||||
|
||||
from src.backend import Database
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
from src import LOG_DIR
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
# from src.transformers import RDS_AVAIL_DATA
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import time
|
||||
|
||||
# from icecream import ic
|
||||
from PyQt6.QtCore import QThread
|
||||
from PyQt6.QtCore import pyqtSignal as Signal
|
||||
from PySide6.QtCore import QThread
|
||||
from PySide6.QtCore import Signal as Signal
|
||||
|
||||
from src.backend.database import Database
|
||||
|
||||
from src import LOG_DIR
|
||||
from src.logic.webrequest import BibTextTransformer, WebRequest
|
||||
|
||||
# from src.transformers import RDS_AVAIL_DATA
|
||||
@@ -14,8 +14,8 @@ import sys
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
from docx import Document
|
||||
|
||||
data = {}
|
||||
wordDoc = Document("files/Semesterapparat - Anmeldung.docx")
|
||||
paragraphs = wordDoc.tables
|
||||
for table in paragraphs:
|
||||
for column in table.columns:
|
||||
cellcount = 0
|
||||
for _cell in column.cells:
|
||||
if cellcount < 12:
|
||||
cellcount += 1
|
||||
# print(f"cell:{cell.text}")
|
||||
|
||||
# # print(f'paragraphs[{i}]: {paragraphs[i]}')
|
||||
# data[i] = paragraphs[i]
|
||||
|
||||
# for i in range(0, len(paragraphs)):
|
||||
# for i in range(2, len(paragraphs)):
|
||||
# data[i] = paragraphs[i]
|
||||
|
||||
# print(data)
|
||||
|
||||
# for table in wordDoc.tables:
|
||||
# for row in table.rows:
|
||||
# # print('---')
|
||||
# for cell in row.cells:
|
||||
# # print(f'cell:{cell.text}')
|
||||
@@ -1,10 +0,0 @@
|
||||
import tabula
|
||||
|
||||
file = "files/Semesterapparat - Anmeldung.pdf"
|
||||
|
||||
|
||||
def extract_book_data(file):
|
||||
tabula.read_pdf(file, pages="all", encoding="utf-8", multiple_tables=True)
|
||||
tabula.convert_into(file, file.replace(".pdf"), output_format="csv", pages="all")
|
||||
with open("files/Semesterapparat - Anmeldung.csv", "r") as f:
|
||||
f.read()
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
53
src/logic/openai.py
Normal file
53
src/logic/openai.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from openai import OpenAI
|
||||
from src import settings
|
||||
import json
|
||||
|
||||
|
||||
|
||||
def init_client():
|
||||
"""Initialize the OpenAI client with the API key and model from settings."""
|
||||
global client, model, api_key
|
||||
if not settings.openAI.api_key:
|
||||
raise ValueError("OpenAI API key is not set in the configuration.")
|
||||
if not settings.openAI.model:
|
||||
raise ValueError("OpenAI model is not set in the configuration.")
|
||||
|
||||
model = settings.openAI.model
|
||||
api_key = settings.openAI.api_key
|
||||
client = OpenAI(api_key=api_key)
|
||||
return client
|
||||
def run_shortener(title:str, length:int):
|
||||
client = init_client()
|
||||
response = client.responses.create(
|
||||
model=model,
|
||||
instructions="""you are a sentence shortener. The next message will contain the string to shorten and the length limit.
|
||||
You need to shorten the string to be under the length limit, while keeping as much detail as possible. The result may NOT be longer than the length limit.
|
||||
based on that, please reply only the shortened string. Give me 5 choices. if the length is too long, discard the string and try another one.Return the data as a python list containing the result as {"shortened_string": shortened_string, "length": lengthasInt}. Do not return the answer in a codeblock, use a pure string. Before answering, check the results and if ANY is longer than the needed_length, discard all and try again""",
|
||||
input=f'{{"string":"{title}", "needed_length":{length}}}',
|
||||
)
|
||||
answers = response.output_text
|
||||
return eval(answers) # type: ignore
|
||||
#answers are strings in json format, so we need to convert them to a list of dicts
|
||||
|
||||
|
||||
def name_tester(name: str):
|
||||
client = init_client()
|
||||
response = client.responses.create(
|
||||
model = model,
|
||||
instructions="""you are a name tester, You are given a name and will have to split the name into first name, last name, and if present the title. Return the name in a json format with the keys "title", "first_name", "last_name". If no title is present, set title to none. Do NOt return the answer in a codeblock, use a pure json string. Assume the names are in the usual german naming scheme""",
|
||||
input = f'{{"name":"{name}"}}'
|
||||
)
|
||||
answers = response.output_text
|
||||
|
||||
return json.loads(answers)
|
||||
|
||||
def semester_converter(semester:str):
|
||||
client = init_client()
|
||||
response = client.responses.create(
|
||||
model = model,
|
||||
instructions="""you are a semester converter. You will be given a string. Convert this into a string like this: SoSe YY or WiSe YY/YY+1. Do not return the answer in a codeblock, use a pure string.""",
|
||||
input = semester
|
||||
)
|
||||
answers = response.output_text
|
||||
|
||||
return answers
|
||||
@@ -1,194 +0,0 @@
|
||||
import os
|
||||
|
||||
# from icecream import ic
|
||||
from omegaconf import OmegaConf
|
||||
from PyQt6 import QtWidgets
|
||||
from PyQt6.QtCore import QThread
|
||||
from PyQt6.QtCore import pyqtSignal as Signal
|
||||
|
||||
from src.backend.database import Database
|
||||
from src.logic.log import MyLogger
|
||||
from src.logic.webrequest import BibTextTransformer, WebRequest
|
||||
|
||||
# from src.transformers import RDS_AVAIL_DATA
|
||||
from src.ui.dialogs.Ui_mail_preview import Ui_eMailPreview
|
||||
|
||||
config = OmegaConf.load("config.yaml")
|
||||
|
||||
|
||||
class BackgroundChecker(QThread):
|
||||
"""Check all apparats for available Books"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MockAvailCheck:
|
||||
def __init__(
|
||||
self, links: list = None, appnumber: int = None, parent=None, books=list[dict]
|
||||
):
|
||||
if links is None:
|
||||
links = []
|
||||
super().__init__(parent)
|
||||
self.logger = MyLogger("MockAvailChecker")
|
||||
self.logger.log_info("Starting worker thread")
|
||||
self.logger.log_info(
|
||||
"Checking availability for "
|
||||
+ str(links)
|
||||
+ " with appnumber "
|
||||
+ str(appnumber)
|
||||
+ "..."
|
||||
)
|
||||
self.links = links
|
||||
self.appnumber = appnumber
|
||||
self.books = books
|
||||
|
||||
def run(self):
|
||||
self.db = Database()
|
||||
state = 0
|
||||
count = 0
|
||||
result = []
|
||||
for link in self.links:
|
||||
self.logger.log_info("Processing entry: " + str(link))
|
||||
data = WebRequest().get_ppn(link).get_data()
|
||||
transformer = BibTextTransformer("RDS")
|
||||
rds = transformer.get_data(data).return_data("rds_availability")
|
||||
|
||||
for item in rds.items:
|
||||
sign = item.superlocation
|
||||
loc = item.location
|
||||
# ic(item.location, item.superlocation)
|
||||
if self.appnumber in sign or self.appnumber in loc:
|
||||
state = 1
|
||||
book_id = None
|
||||
for book in self.books:
|
||||
if book["bookdata"].signature == link:
|
||||
book_id = book["id"]
|
||||
break
|
||||
self.logger.log_info(f"State of {link}: " + str(state))
|
||||
print(
|
||||
"lock acquired, updating availability of "
|
||||
+ str(book_id)
|
||||
+ " to "
|
||||
+ str(state)
|
||||
)
|
||||
result.append((item.callnumber, state))
|
||||
count += 1
|
||||
return result
|
||||
|
||||
self.logger.log_info("Worker thread finished")
|
||||
# teminate thread
|
||||
|
||||
|
||||
class Mailer(Ui_eMailPreview):
|
||||
updateSignal = Signal(int)
|
||||
|
||||
def __init__(self, data=None, parent=None):
|
||||
super(QThread).__init__()
|
||||
super(Ui_eMailPreview).__init__()
|
||||
|
||||
self.logger = MyLogger("Mailer")
|
||||
self.data = data
|
||||
self.appid = data["app_id"]
|
||||
self.appname = data["app_name"]
|
||||
self.subject = data["app_subject"]
|
||||
self.profname = data["prof_name"]
|
||||
self.mail_data = ""
|
||||
self.prof_mail = data["prof_mail"]
|
||||
self.dialog = QtWidgets.QDialog()
|
||||
self.comboBox.currentIndexChanged.connect(self.set_mail)
|
||||
self.prof_name.setText(self.prof_name)
|
||||
self.mail_name.setText(self.prof_mail)
|
||||
self.load_mail_templates()
|
||||
self.gender_female.clicked.connect(self.set_mail)
|
||||
self.gender_male.clicked.connect(self.set_mail)
|
||||
self.gender_non.clicked.connect(self.set_mail)
|
||||
self.buttonBox.accepted.connect(self.createAndSendMail)
|
||||
|
||||
def load_mail_templates(self):
|
||||
# print("loading mail templates")
|
||||
mail_templates = os.listdir("mail_vorlagen")
|
||||
for template in mail_templates:
|
||||
self.comboBox.addItem(template)
|
||||
|
||||
def get_greeting(self):
|
||||
if self.gender_male.isChecked():
|
||||
return "Sehr geehrter Herr"
|
||||
elif self.gender_female.isChecked():
|
||||
return "Sehr geehrte Frau"
|
||||
elif self.gender_non.isChecked():
|
||||
return "Guten Tag"
|
||||
|
||||
def set_mail(self):
|
||||
email_template = self.comboBox.currentText()
|
||||
if email_template == "":
|
||||
return
|
||||
with open(f"mail_vorlagen/{email_template}", "r", encoding="utf-8") as f:
|
||||
mail_template = f.read()
|
||||
email_header = email_template.split(".eml")[0]
|
||||
if "{AppNr}" in email_template:
|
||||
email_header = email_template.split(".eml")[0]
|
||||
email_header = email_header.format(AppNr=self.appid, AppName=self.appname)
|
||||
self.mail_header.setText(email_header)
|
||||
self.mail_data = mail_template.split("<html>")[0]
|
||||
mail_html = mail_template.split("<html>")[1]
|
||||
mail_html = "<html>" + mail_html
|
||||
Appname = self.appname
|
||||
mail_html = mail_html.format(
|
||||
Profname=self.prof_name.text().split(" ")[-1],
|
||||
Appname=Appname,
|
||||
AppNr=self.appid,
|
||||
AppSubject=self.subject,
|
||||
greeting=self.get_greeting(),
|
||||
)
|
||||
|
||||
self.mail_body.setHtml(mail_html)
|
||||
|
||||
def createAndSendMail(self):
|
||||
import smtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
smtp_server = config["mail"]["smtp_server"]
|
||||
port: int = config["mail"]["port"]
|
||||
sender_email = config["mail"]["sender"]
|
||||
password = config["mail"]["password"]
|
||||
message = MIMEMultipart()
|
||||
message["From"] = sender_email
|
||||
message["To"] = self.prof_mail
|
||||
message["Subject"] = self.mail_header.text()
|
||||
mail_body = self.mail_body.toHtml()
|
||||
message.attach(MIMEText(mail_body, "html"))
|
||||
mail = message.as_string()
|
||||
|
||||
server = smtplib.SMTP_SSL(smtp_server, port)
|
||||
# server.starttls()
|
||||
# server.auth(mechanism="PLAIN")
|
||||
if config["mail"]["use_user_name"] == 1:
|
||||
# print(config["mail"]["user_name"])
|
||||
server.login(config["mail"]["user_name"], password)
|
||||
else:
|
||||
server.login(sender_email, password)
|
||||
server.sendmail(sender_email, self.prof_mail, mail)
|
||||
# print("Mail sent")
|
||||
# end active process
|
||||
server.quit()
|
||||
|
||||
|
||||
class MailThread(QThread):
|
||||
updateSignal = Signal(int)
|
||||
|
||||
def __init__(self, data=None, parent=None):
|
||||
super(QThread).__init__()
|
||||
super(MailThread).__init__()
|
||||
self.logger = MyLogger("MailThread")
|
||||
self.data = data
|
||||
|
||||
def show_ui(self):
|
||||
self.mailer = Mailer()
|
||||
self.mailer.__init__()
|
||||
self.mailer.dialog.exec_()
|
||||
self.mailer.dialog.show()
|
||||
|
||||
def run(self):
|
||||
self.show_ui()
|
||||
self.updateSignal.emit(1)
|
||||
@@ -1,266 +0,0 @@
|
||||
import sqlite3
|
||||
import time
|
||||
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
|
||||
from src.backend.database import Database
|
||||
from src.logic.log import MyLogger
|
||||
from src.logic.webrequest import BibTextTransformer, WebRequest
|
||||
|
||||
# from icecream import ic
|
||||
|
||||
|
||||
class BookGrabber(QThread):
|
||||
updateSignal = pyqtSignal(int, int)
|
||||
|
||||
def __init__(self, filename):
|
||||
super(BookGrabber, self).__init__(parent=None)
|
||||
self.is_Running = True
|
||||
self.logger = MyLogger("Worker")
|
||||
self.logger.log_info("Starting worker thread")
|
||||
self.data, self.app_id, self.prof_id, self.mode = self.readFile(filename)
|
||||
|
||||
self.book_id = None
|
||||
time.sleep(2)
|
||||
|
||||
def readFile(self, filename):
|
||||
with open(filename, "r") as file:
|
||||
data = file.readlines()
|
||||
app_id = data[0].strip()
|
||||
prof_id = data[1].strip()
|
||||
mode = data[2].strip()
|
||||
data = data[3:]
|
||||
return data, app_id, prof_id, mode
|
||||
|
||||
# def resetValues(self):
|
||||
# self.app_id = None
|
||||
# self.prof_id = None
|
||||
# self.mode = None
|
||||
# self.data = None
|
||||
# self.book_id = None
|
||||
|
||||
def run(self):
|
||||
while self.is_Running:
|
||||
self.db = Database()
|
||||
item = 0
|
||||
iterdata = self.data
|
||||
print(iterdata)
|
||||
for entry in iterdata:
|
||||
signature = str(entry)
|
||||
self.logger.log_info("Processing entry: " + signature)
|
||||
|
||||
webdata = WebRequest().get_ppn(entry).get_data()
|
||||
if webdata == "error":
|
||||
continue
|
||||
bd = BibTextTransformer(self.mode).get_data(webdata).return_data()
|
||||
transformer = BibTextTransformer("RDS")
|
||||
rds = transformer.get_data(webdata).return_data("rds_availability")
|
||||
bd.signature = entry
|
||||
# confirm lock is acquired
|
||||
print("lock acquired, adding book to database")
|
||||
self.db.addBookToDatabase(bd, self.app_id, self.prof_id)
|
||||
# get latest book id
|
||||
self.book_id = self.db.getLastBookId()
|
||||
self.logger.log_info("Added book to database")
|
||||
state = 0
|
||||
print(len(rds.items))
|
||||
for rds_item in rds.items:
|
||||
sign = rds_item.superlocation
|
||||
loc = rds_item.location
|
||||
# ic(sign, loc)
|
||||
# ic(rds_item)
|
||||
if self.app_id in sign or self.app_id in loc:
|
||||
state = 1
|
||||
break
|
||||
|
||||
# for book in self.books:
|
||||
# if book["bookdata"].signature == entry:
|
||||
# book_id = book["id"]
|
||||
# break
|
||||
self.logger.log_info(f"State of {signature}: {state}")
|
||||
print(
|
||||
"updating availability of "
|
||||
+ str(self.book_id)
|
||||
+ " to "
|
||||
+ str(state)
|
||||
)
|
||||
try:
|
||||
self.db.setAvailability(self.book_id, state)
|
||||
except sqlite3.OperationalError as e:
|
||||
self.logger.log_error(f"Failed to update availability: {e}")
|
||||
|
||||
# time.sleep(5)
|
||||
item += 1
|
||||
self.updateSignal.emit(item, len(self.data))
|
||||
self.logger.log_info("Worker thread finished")
|
||||
self.stop()
|
||||
if not self.is_Running:
|
||||
break
|
||||
|
||||
def stop(self):
|
||||
self.is_Running = False
|
||||
|
||||
|
||||
class AvailChecker(QThread):
|
||||
updateSignal = pyqtSignal(str, int)
|
||||
updateProgress = pyqtSignal(int, int)
|
||||
|
||||
def __init__(
|
||||
self, links: list = None, appnumber: int = None, parent=None, books=list[dict]
|
||||
):
|
||||
if links is None:
|
||||
links = []
|
||||
super().__init__(parent)
|
||||
self.logger = MyLogger("AvailChecker")
|
||||
self.logger.log_info("Starting worker thread")
|
||||
self.logger.log_info(
|
||||
"Checking availability for "
|
||||
+ str(links)
|
||||
+ " with appnumber "
|
||||
+ str(appnumber)
|
||||
+ "..."
|
||||
)
|
||||
self.links = links
|
||||
self.appnumber = appnumber
|
||||
self.books = books
|
||||
self.logger.log_info(
|
||||
f"Started worker with appnumber: {self.appnumber} and links: {self.links} and {len(self.books)} books..."
|
||||
)
|
||||
time.sleep(2)
|
||||
|
||||
def run(self):
|
||||
self.db = Database()
|
||||
state = 0
|
||||
count = 0
|
||||
for link in self.links:
|
||||
self.logger.log_info("Processing entry: " + str(link))
|
||||
data = WebRequest().get_ppn(link).get_data()
|
||||
transformer = BibTextTransformer("RDS")
|
||||
rds = transformer.get_data(data).return_data("rds_availability")
|
||||
|
||||
book_id = None
|
||||
for item in rds.items:
|
||||
sign = item.superlocation
|
||||
loc = item.location
|
||||
# print(item.location)
|
||||
if self.appnumber in sign or self.appnumber in loc:
|
||||
state = 1
|
||||
break
|
||||
for book in self.books:
|
||||
if book["bookdata"].signature == link:
|
||||
book_id = book["id"]
|
||||
break
|
||||
self.logger.log_info(f"State of {link}: " + str(state))
|
||||
print("Updating availability of " + str(book_id) + " to " + str(state))
|
||||
self.db.setAvailability(book_id, state)
|
||||
count += 1
|
||||
self.updateProgress.emit(count, len(self.links))
|
||||
self.updateSignal.emit(item.callnumber, state)
|
||||
|
||||
self.logger.log_info("Worker thread finished")
|
||||
# teminate thread
|
||||
|
||||
self.quit()
|
||||
|
||||
|
||||
class AutoAdder(QThread):
|
||||
updateSignal = pyqtSignal(int)
|
||||
|
||||
setTextSignal = pyqtSignal(int)
|
||||
progress = pyqtSignal(int)
|
||||
|
||||
def __init__(self, data=None, app_id=None, prof_id=None, parent=None):
|
||||
super().__init__(parent)
|
||||
self.logger = MyLogger("AutoAdder")
|
||||
self.data = data
|
||||
self.app_id = app_id
|
||||
self.prof_id = prof_id
|
||||
|
||||
print("Launched AutoAdder")
|
||||
print(self.data, self.app_id, self.prof_id)
|
||||
|
||||
def run(self):
|
||||
self.db = Database()
|
||||
# show the dialog, start the thread to gather data and dynamically update progressbar and listwidget
|
||||
self.logger.log_info("Starting worker thread")
|
||||
item = 0
|
||||
for entry in self.data:
|
||||
try:
|
||||
# webdata = WebRequest().get_ppn(entry).get_data()
|
||||
# bd = BibTextTransformer("ARRAY").get_data(webdata).return_data()
|
||||
# bd.signature = entry
|
||||
self.updateSignal.emit(item)
|
||||
self.setTextSignal.emit(entry)
|
||||
# qsleep
|
||||
item += 1
|
||||
self.progress.emit(item)
|
||||
print(item, len(self.data))
|
||||
time.sleep(1)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.logger.log_exception(
|
||||
f"The query failed with message {e} for signature {entry}"
|
||||
)
|
||||
continue
|
||||
if item == len(self.data):
|
||||
self.logger.log_info("Worker thread finished")
|
||||
# teminate thread
|
||||
self.finished.emit()
|
||||
|
||||
|
||||
class MockAvailCheck:
|
||||
def __init__(
|
||||
self, links: list = None, appnumber: int = None, parent=None, books=list[dict]
|
||||
):
|
||||
if links is None:
|
||||
links = []
|
||||
super().__init__(parent)
|
||||
self.logger = MyLogger("MockAvailChecker")
|
||||
self.logger.log_info("Starting worker thread")
|
||||
self.logger.log_info(
|
||||
"Checking availability for "
|
||||
+ str(links)
|
||||
+ " with appnumber "
|
||||
+ str(appnumber)
|
||||
+ "..."
|
||||
)
|
||||
self.links = links
|
||||
self.appnumber = appnumber
|
||||
self.books = books
|
||||
|
||||
def run(self):
|
||||
self.db = Database()
|
||||
state = 0
|
||||
count = 0
|
||||
result = []
|
||||
for link in self.links:
|
||||
self.logger.log_info("Processing entry: " + str(link))
|
||||
data = WebRequest().get_ppn(link).get_data()
|
||||
transformer = BibTextTransformer("RDS")
|
||||
rds = transformer.get_data(data).return_data("rds_availability")
|
||||
|
||||
for item in rds.items:
|
||||
sign = item.superlocation
|
||||
loc = item.location
|
||||
# ic(item.location, item.superlocation)
|
||||
if self.appnumber in sign or self.appnumber in loc:
|
||||
state = 1
|
||||
book_id = None
|
||||
for book in self.books:
|
||||
if book["bookdata"].signature == link:
|
||||
book_id = book["id"]
|
||||
break
|
||||
self.logger.log_info(f"State of {link}: " + str(state))
|
||||
print(
|
||||
"lock acquired, updating availability of "
|
||||
+ str(book_id)
|
||||
+ " to "
|
||||
+ str(state)
|
||||
)
|
||||
result.append((item.callnumber, state))
|
||||
count += 1
|
||||
return result
|
||||
|
||||
self.logger.log_info("Worker thread finished")
|
||||
# teminate thread
|
||||
@@ -11,10 +11,11 @@ from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
|
||||
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
|
||||
import loguru
|
||||
import sys
|
||||
from src import LOG_DIR
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
|
||||
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import pandas as pd
|
||||
from docx import Document
|
||||
import sys
|
||||
import zipfile
|
||||
from dataclasses import dataclass
|
||||
from src.backend import Semester
|
||||
from typing import Union, Any
|
||||
from typing import Any, Union
|
||||
|
||||
import loguru
|
||||
import sys
|
||||
import pandas as pd
|
||||
from bs4 import BeautifulSoup
|
||||
from docx import Document
|
||||
|
||||
from src import LOG_DIR
|
||||
from src.backend import Semester
|
||||
from src.logic.openai import name_tester, run_shortener, semester_converter
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
|
||||
@@ -75,12 +80,37 @@ class SemapDocument:
|
||||
phoneNumber: int = None
|
||||
mail: str = None
|
||||
title: str = None
|
||||
title_suggestions: list[str] = None
|
||||
semester: Union[str, Semester] = None
|
||||
books: list[Book] = None
|
||||
eternal: bool = False
|
||||
personName: str = None
|
||||
personTitle: str = None
|
||||
title_length = 0
|
||||
title_max_length = 0
|
||||
|
||||
def __post_init__(self):
|
||||
self.title_suggestions = []
|
||||
|
||||
@property
|
||||
def nameSetter(self):
|
||||
data = name_tester(self.personTitle)
|
||||
name = f"{data['last_name']}, {data['first_name']}"
|
||||
if data["title"] is not None:
|
||||
title = data["title"]
|
||||
self.personTitle = title
|
||||
self.personName = name
|
||||
self.title_length = len(self.title) + 3 + len(self.personName.split(",")[0])
|
||||
if self.title_length > 40:
|
||||
log.warning("Title is too long")
|
||||
name_len = len(self.personName.split(",")[0])
|
||||
self.title_max_length = 38 - name_len
|
||||
suggestions = run_shortener(self.title, self.title_max_length)
|
||||
for suggestion in suggestions:
|
||||
self.title_suggestions.append(suggestion["shortened_string"])
|
||||
else:
|
||||
self.title_suggestions = []
|
||||
pass
|
||||
@property
|
||||
def renameSemester(self) -> None:
|
||||
if ", Dauer" in self.semester:
|
||||
@@ -88,8 +118,8 @@ class SemapDocument:
|
||||
self.eternal = True
|
||||
self.semester = Semester().from_string(self.semester)
|
||||
else:
|
||||
logger.warning("Semester {} is not valid", self.semester)
|
||||
self.semester = None
|
||||
log.warning("Semester {} is not valid", self.semester)
|
||||
self.semester = Semester().from_string(semester_converter(self.semester))
|
||||
|
||||
@property
|
||||
def signatures(self) -> list[str]:
|
||||
@@ -105,11 +135,14 @@ def word_docx_to_csv(path: str) -> list[pd.DataFrame]:
|
||||
for table in tables:
|
||||
data = []
|
||||
for row in table.rows:
|
||||
row_data = []
|
||||
row_data: list[Any] = []
|
||||
for cell in row.cells:
|
||||
text = cell.text
|
||||
|
||||
text = text.replace("\n", "")
|
||||
row_data.append(text)
|
||||
if text == "Ihr Fach:":
|
||||
row_data.append(get_fach(path))
|
||||
data.append(row_data)
|
||||
df = pd.DataFrame(data)
|
||||
df.columns = df.iloc[0]
|
||||
@@ -117,11 +150,27 @@ def word_docx_to_csv(path: str) -> list[pd.DataFrame]:
|
||||
|
||||
m_data.append(df)
|
||||
|
||||
# for df[0, 1]: merge i and i+1 as key, value
|
||||
|
||||
return m_data
|
||||
|
||||
|
||||
def get_fach(path: str) -> str:
|
||||
document = zipfile.ZipFile(path)
|
||||
xml_data = document.read("word/document.xml")
|
||||
document.close()
|
||||
|
||||
soup = BeautifulSoup(xml_data, "xml")
|
||||
# text we need is in <w:p w14:paraId="12456A32" ... > -> w:r -> w:t
|
||||
paragraphs = soup.find_all("w:p")
|
||||
names = []
|
||||
for para in paragraphs:
|
||||
para_id = para.get("w14:paraId")
|
||||
if para_id == "12456A32":
|
||||
# get the data in the w:t
|
||||
for run in para.find_all("w:r"):
|
||||
data = run.find("w:t")
|
||||
return data.contents[0]
|
||||
|
||||
|
||||
def makeDict():
|
||||
return {
|
||||
"work_author": None,
|
||||
@@ -222,10 +271,10 @@ def word_to_semap(word_path: str) -> SemapDocument:
|
||||
df = word_docx_to_csv(word_path)
|
||||
apparatdata = df[0]
|
||||
apparatdata = apparatdata.to_dict()
|
||||
|
||||
keys = list(apparatdata.keys())
|
||||
print(apparatdata, keys)
|
||||
|
||||
appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys), 2)}
|
||||
appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys) - 1, 2)}
|
||||
semap.phoneNumber = appdata["Telefon:"]
|
||||
semap.subject = appdata["Ihr Fach:"]
|
||||
semap.mail = appdata["Mailadresse:"]
|
||||
@@ -238,6 +287,8 @@ def word_to_semap(word_path: str) -> SemapDocument:
|
||||
semap.title = appdata["Veranstaltung:"]
|
||||
semap.semester = appdata["Semester:"]
|
||||
semap.renameSemester
|
||||
semap.nameSetter
|
||||
|
||||
books = df[2]
|
||||
booklist = []
|
||||
for i in range(len(books)):
|
||||
@@ -254,7 +305,6 @@ def word_to_semap(word_path: str) -> SemapDocument:
|
||||
booklist.append(book)
|
||||
log.info("Found {} books", len(booklist))
|
||||
semap.books = booklist
|
||||
|
||||
return semap
|
||||
|
||||
|
||||
|
||||
@@ -160,6 +160,8 @@ class ZoteroController:
|
||||
zoterocfg = settings.zotero
|
||||
|
||||
def __init__(self):
|
||||
if self.zoterocfg.library_id is None:
|
||||
return
|
||||
self.zot = zotero.Zotero(
|
||||
self.zoterocfg.library_id,
|
||||
self.zoterocfg.library_type,
|
||||
|
||||
@@ -6,15 +6,15 @@ from dataclasses import dataclass
|
||||
from dataclasses import field as dataclass_field
|
||||
from typing import Any, List
|
||||
|
||||
|
||||
from src import LOG_DIR
|
||||
from src.logic.dataclass import BookData
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
###Pydatnic models
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\Semesterapparate\ui\dialogs\edit_bookdata.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from src.logic.dataclass import BookData
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\Semesterapparate\ui\dialogs\fileparser.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from src.logic.webrequest import BibTextTransformer, WebRequest
|
||||
|
||||
@@ -61,7 +61,7 @@ class Ui_Dialog(object):
|
||||
self.listWidget.setObjectName("listWidget")
|
||||
self.signatures = []
|
||||
self.returned = []
|
||||
# self.data_gathering_complete = QtCore.pyqtSignal()
|
||||
# self.data_gathering_complete = QtCore.Signal()
|
||||
self.retranslateUi(Dialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Form implementation generated from reading ui file '/home/alexander/GitHub/Semesterapparate/ui/dialogs/login.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.5.3
|
||||
# Created by: PySide6 UI code generator 6.5.3
|
||||
#
|
||||
# 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.
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import hashlib
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
from src.backend.database import Database
|
||||
from src.backend.admin_console import AdminCommands
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\mail_preview.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\Semesterapparate\ui\dialogs\medianadder.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\Semesterapparate\ui\dialogs\new_subject.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\Semesterapparate\ui\dialogs\parsed_titles.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from src.logic.log import MyLogger
|
||||
from src.logic.threads import AutoAdder
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\reminder.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\Semesterapparate\ui\dialogs\settings.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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 omegaconf import OmegaConf
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
config = OmegaConf.load("config.yaml")
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from .dialog_sources.Ui_about import Ui_about
|
||||
from PyQt6 import QtWidgets
|
||||
from PyQt6.QtCore import PYQT_VERSION_STR
|
||||
from PySide6 import QtWidgets
|
||||
import PySide6
|
||||
from src import Icon, __version__, __author__
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class About(QtWidgets.QDialog, Ui_about):
|
||||
data = {
|
||||
"Version": __version__,
|
||||
"Author": __author__,
|
||||
"PyQt6 Version": PYQT_VERSION_STR,
|
||||
"PySide6 Version": PySide6.__version__,
|
||||
"License": "MIT License",
|
||||
"Icons": """Google Material Design Icons (https://fonts.google.com/icons)
|
||||
StableDiffusion (logo)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
from .dialog_sources.Ui_apparat_extend import Ui_Dialog
|
||||
from src import Icon
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
from src.logic.dataclass import BookData
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from .dialog_sources.Ui_confirm_extend import Ui_extend_confirm
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
|
||||
class ConfirmExtend(QtWidgets.QDialog, Ui_extend_confirm):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\confirm_extend.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_extend_confirm(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\about.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_about(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\app_status.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore
|
||||
from PySide6 import QtCore
|
||||
|
||||
|
||||
class Ui_Form(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\apparat_extend.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
# Created by: PySide6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\confirm_extend.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
# Created by: PySide6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
class Ui_extend_confirm(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\edit_bookdata.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_add_table_entry.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
# Created by: PySide6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_generate_citation.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_generator_confirm.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\login.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\mail_preview.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
# Created by: PySide6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_eMailPreview(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\medianadder.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
# Created by: PySide6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\newMailTemplateDesigner.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\parsed_titles.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Form(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\reminder.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.6.1
|
||||
# Created by: PySide6 UI code generator 6.6.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
class Ui_Erinnerung(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\settings.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.7.1
|
||||
# Created by: PySide6 UI code generator 6.7.1
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\about.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_about(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\app_status.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Form(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\apparat_extend.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\confirm_extend.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_extend_confirm(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\documentprint.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.9.0
|
||||
# Created by: PySide6 UI code generator 6.9.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\edit_bookdata.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_add_table_entry.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_generate_citation.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\elsa_generator_confirm.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\login.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\mail_preview.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_eMailPreview(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\medianadder.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\newMailTemplateDesigner.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\parsed_titles.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Form(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\reminder.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Erinnerung(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\settings.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.9.0
|
||||
# Created by: PySide6 UI code generator 6.9.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from .dialog_sources.documentprint_ui import Ui_Dialog
|
||||
from PyQt6 import QtWidgets, QtCore
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
from src import Icon
|
||||
|
||||
from src.utils.richtext import SemapSchilder, SemesterDocument
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from .dialog_sources.Ui_elsa_add_table_entry import Ui_Dialog
|
||||
from src.logic.webrequest import WebRequest, BibTextTransformer
|
||||
from src import Icon
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
from src.transformers.transformers import DictToTable
|
||||
from src.logic.zotero import ZoteroController
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from .dialog_sources.Ui_elsa_generate_citation import Ui_Dialog
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
|
||||
class ElsaCitation(QtWidgets.QDialog, Ui_Dialog):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from .dialog_sources.Ui_elsa_generator_confirm import Ui_Dialog
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
|
||||
class ElsaGenConfirm(QtWidgets.QDialog, Ui_Dialog):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'ui/dialogs/extend_apparat.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Frame(object):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
from src.logic.webrequest import BibTextTransformer, WebRequest
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import hashlib
|
||||
import sys
|
||||
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
import loguru
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
from src.backend.admin_console import AdminCommands
|
||||
from src import LOG_DIR, Icon
|
||||
from src.backend.database import Database
|
||||
|
||||
from .dialog_sources.login_ui import Ui_Dialog
|
||||
import sys
|
||||
import loguru
|
||||
from src import Icon
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
class LoginDialog(Ui_Dialog):
|
||||
@@ -52,6 +50,7 @@ class LoginDialog(Ui_Dialog):
|
||||
self.lineEdit_2.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
|
||||
self.lineEdit_2.setClearButtonEnabled(True)
|
||||
self.lineEdit_2.setObjectName("lineEdit_2")
|
||||
log.info("Calling database")
|
||||
self.db = Database()
|
||||
|
||||
self.retranslateUi(Dialog)
|
||||
@@ -76,6 +75,8 @@ class LoginDialog(Ui_Dialog):
|
||||
|
||||
hashed_password = hashlib.sha256(password.encode()).hexdigest()
|
||||
if len(self.db.getUsers()) == 0:
|
||||
from src.backend.admin_console import AdminCommands
|
||||
|
||||
AdminCommands().create_admin()
|
||||
self.lresult = 1 # Indicate successful login
|
||||
self.lusername = username
|
||||
@@ -89,14 +90,14 @@ class LoginDialog(Ui_Dialog):
|
||||
else:
|
||||
# Credentials are invalid, display a warning
|
||||
if username == "" or password == "":
|
||||
logger.warning("Invalid username or password. Login failed.")
|
||||
log.warning("Invalid username or password. Login failed.")
|
||||
warning_dialog = QtWidgets.QMessageBox()
|
||||
warning_dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
warning_dialog.setText("Please enter a username and password.")
|
||||
warning_dialog.setWindowTitle("Login Failed")
|
||||
warning_dialog.exec()
|
||||
else:
|
||||
logger.warning("Invalid username or password. Login failed.")
|
||||
log.warning("Invalid username or password. Login failed.")
|
||||
warning_dialog = QtWidgets.QMessageBox()
|
||||
warning_dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
|
||||
warning_dialog.setText(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
from src import Icon, settings as config
|
||||
|
||||
@@ -10,11 +10,11 @@ from .dialog_sources.Ui_mail_preview import Ui_eMailPreview as MailPreviewDialog
|
||||
from .mailTemplate import MailTemplateDialog
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
from src import LOG_DIR
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
from PyQt6 import QtGui, QtWidgets, QtCore
|
||||
from PySide6 import QtGui, QtWidgets, QtCore
|
||||
|
||||
from src import Icon
|
||||
|
||||
@@ -22,7 +22,7 @@ logger.add(sys.stdout)
|
||||
|
||||
|
||||
class MailTemplateDialog(QtWidgets.QDialog, NewMailTemplateDesignerDialog):
|
||||
updateSignal = QtCore.pyqtSignal()
|
||||
updateSignal = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent=None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'ui\dialogs\mail_preview.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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, QtWidgets
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\mail_preview.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.8.0
|
||||
# Created by: PySide6 UI code generator 6.8.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_eMailPreview(object):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from .dialog_sources.medianadder_ui import Ui_Dialog
|
||||
from src import Icon
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
from src.backend import AutoAdder
|
||||
|
||||
@@ -6,11 +6,11 @@ from src.backend import AutoAdder
|
||||
from .dialog_sources.parsed_titles_ui import Ui_Form
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
from src import LOG_DIR
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
class ParsedTitles(QtWidgets.QWidget, Ui_Form):
|
||||
def __init__(self, parent=None):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\parsed_titles.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.9.0
|
||||
# Created by: PySide6 UI code generator 6.9.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Form(object):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'ui\dialogs\confirm_extend.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.3.1
|
||||
# Created by: PySide6 UI code generator 6.3.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 QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
from src import Icon
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
from .dialog_sources.reminder_ui import Ui_Erinnerung as Ui_Dialog
|
||||
from src import Icon
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
from src import Icon, settings
|
||||
from .dialog_sources.settings_ui import Ui_Dialog as _settings
|
||||
from src.ui.widgets.iconLine import IconWidget
|
||||
import loguru
|
||||
import sys
|
||||
from src import LOG_DIR
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
|
||||
@@ -80,8 +81,8 @@ class Settings(QtWidgets.QDialog, _settings):
|
||||
|
||||
def load_config(self):
|
||||
self.db_name.setText(settings.database.name)
|
||||
self.db_path.setText(settings.database.path)
|
||||
self.save_path.setText(settings.database.temp)
|
||||
self.db_path.setText(str(settings.database.path.expanduser()))
|
||||
self.save_path.setText(str(settings.database.temp.expanduser()))
|
||||
self.smtp_address.setText(settings.mail.smtp_server)
|
||||
self.smtp_port.setText(str(settings.mail.port))
|
||||
self.sender_email.setText(settings.mail.sender)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PyQt6 import QtWidgets
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
|
||||
class ValidatorButton(QtWidgets.QToolButton):
|
||||
|
||||
@@ -1923,8 +1923,8 @@ Einige Angaben müssen ggf angepasst werden</string>
|
||||
<property name="title">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="actionDokumentation_lokal"/>
|
||||
<addaction name="actionAbout"/>
|
||||
<addaction name="actionDokumentation"/>
|
||||
</widget>
|
||||
<addaction name="menuDatei"/>
|
||||
<addaction name="menuEinstellungen"/>
|
||||
@@ -1956,17 +1956,6 @@ Einige Angaben müssen ggf angepasst werden</string>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDokumentation">
|
||||
<property name="text">
|
||||
<string>Dokumentation (online)</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F1</string>
|
||||
</property>
|
||||
<property name="shortcutContext">
|
||||
<enum>Qt::ApplicationShortcut</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
@@ -1975,9 +1964,9 @@ Einige Angaben müssen ggf angepasst werden</string>
|
||||
<enum>QAction::AboutRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDokumentation_lokal">
|
||||
<action name="actionDokumentation">
|
||||
<property name="text">
|
||||
<string>Dokumentation (lokal)</string>
|
||||
<string>Dokumentation</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F1</string>
|
||||
@@ -2009,8 +1998,6 @@ Einige Angaben müssen ggf angepasst werden</string>
|
||||
<tabstop>automation_add_selected_books</tabstop>
|
||||
<tabstop>saveandcreate</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../resources.qrc"/>
|
||||
</resources>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\sounds\semesterapparat_ui.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.9.0
|
||||
# Created by: PySide6 UI code generator 6.9.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
|
||||
@@ -1,74 +1,71 @@
|
||||
# encoding: utf-8
|
||||
import atexit
|
||||
import os
|
||||
import time
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import webbrowser
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Union
|
||||
|
||||
import loguru
|
||||
from natsort import natsorted
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PyQt6.QtCore import QThread
|
||||
from PyQt6.QtGui import QRegularExpressionValidator
|
||||
from PySide6 import QtCore, QtGui, QtWidgets
|
||||
from PySide6.QtCore import QThread, Qt
|
||||
from PySide6.QtGui import QRegularExpressionValidator
|
||||
|
||||
from src import Icon
|
||||
from src.backend import Database, BookGrabber, AvailChecker, DocumentationThread
|
||||
from src.backend.semester import Semester
|
||||
from src import LOG_DIR, Icon
|
||||
from src.backend import AvailChecker, BookGrabber, Database, DocumentationThread
|
||||
from src.backend.create_file import recreateFile
|
||||
from src.backend.delete_temp_contents import delete_temp_contents as tempdelete
|
||||
from src.ui import Ui_Semesterapparat
|
||||
from src.backend.semester import Semester
|
||||
from src.logic import (
|
||||
APP_NRS,
|
||||
# PROF_TITLES,
|
||||
Apparat,
|
||||
ApparatData,
|
||||
BookData,
|
||||
Prof,
|
||||
SemapDocument,
|
||||
csv_to_list,
|
||||
word_to_semap,
|
||||
SemapDocument,
|
||||
Prof,
|
||||
Apparat,
|
||||
)
|
||||
from src.ui import Ui_Semesterapparat
|
||||
from src.ui.dialogs import (
|
||||
popus_confirm,
|
||||
MedienAdder,
|
||||
About,
|
||||
ApparatExtendDialog,
|
||||
Mail_Dialog,
|
||||
Settings,
|
||||
BookDataUI,
|
||||
DocumentPrintDialog,
|
||||
LoginDialog,
|
||||
Mail_Dialog,
|
||||
MedienAdder,
|
||||
ParsedTitles,
|
||||
ReminderDialog,
|
||||
DocumentPrintDialog,
|
||||
Settings,
|
||||
popus_confirm,
|
||||
)
|
||||
from src.ui.widgets import (
|
||||
ElsaDialog,
|
||||
MessageCalendar,
|
||||
FilePicker,
|
||||
CalendarEntry,
|
||||
UserCreate,
|
||||
SearchStatisticPage,
|
||||
EditUser,
|
||||
EditProf,
|
||||
EditUser,
|
||||
ElsaDialog,
|
||||
FilePicker,
|
||||
MessageCalendar,
|
||||
SearchStatisticPage,
|
||||
UserCreate,
|
||||
)
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import loguru
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
log.add(
|
||||
f"logs/{datetime.now().strftime('%Y-%m-%d')}.log",
|
||||
f"{LOG_DIR}/{datetime.now().strftime('%Y-%m-%d')}.log",
|
||||
rotation="1 day",
|
||||
retention="1 month",
|
||||
)
|
||||
|
||||
log.critical("UI started")
|
||||
valid_input = (0, 0, 0, 0, 0, 0)
|
||||
|
||||
|
||||
@@ -120,7 +117,7 @@ class Ui(Ui_Semesterapparat):
|
||||
# Actions
|
||||
self.actionEinstellungen.triggered.connect(self.open_settings) # type:ignore
|
||||
Icon("settings", self.actionEinstellungen)
|
||||
self.actionDokumentation_lokal.triggered.connect(self.open_documentation) # type:ignore
|
||||
self.documentation_open = False
|
||||
Icon("offAction", self.actionBeenden)
|
||||
self.actionBeenden.triggered.connect(self.quit) # type:ignore
|
||||
self.actionAbout.triggered.connect(self.open_about) # type:ignore
|
||||
@@ -206,7 +203,7 @@ class Ui(Ui_Semesterapparat):
|
||||
self.add_medium.setEnabled(False)
|
||||
self.docu = DocumentationThread()
|
||||
|
||||
self.actionDokumentation_lokal.triggered.connect(self.open_documentation) # type:ignore
|
||||
self.actionDokumentation.triggered.connect(self.open_documentation) # type:ignore
|
||||
|
||||
# get all current apparats and cache them in a list
|
||||
self.apparats = self.get_apparats()
|
||||
@@ -331,10 +328,16 @@ class Ui(Ui_Semesterapparat):
|
||||
|
||||
def open_documentation(self):
|
||||
log.info("Opening Documentation")
|
||||
if not self.docu.isRunning():
|
||||
self.statusBar.showMessage("Dokumentation wird geöffnet", 5000)
|
||||
|
||||
if not self.documentation_open:
|
||||
# write "opening documentation in 5s into status bar"
|
||||
self.documentation_open = True
|
||||
self.docu.start()
|
||||
time.sleep(5)
|
||||
time.sleep(5)
|
||||
|
||||
webbrowser.open("http://localhost:8000")
|
||||
self.statusBar.showMessage("")
|
||||
|
||||
def update_calendar(self, data: list[dict[str, Any]]):
|
||||
self.calendarWidget.setMessages([data])
|
||||
@@ -556,6 +559,7 @@ class Ui(Ui_Semesterapparat):
|
||||
else:
|
||||
self.__setValidState(self.valid_check_semester, 0, self._mand, 5)
|
||||
self.check_eternal_app.setEnabled(False)
|
||||
return valid
|
||||
|
||||
def display_valid_semester(self):
|
||||
print(f"""
|
||||
@@ -563,6 +567,7 @@ class Ui(Ui_Semesterapparat):
|
||||
Sommer: {self.sem_sommer.isChecked()}
|
||||
Winter: {self.sem_winter.isChecked()}
|
||||
Eternal: {self.check_eternal_app.isChecked()}
|
||||
Valid: {self.validate_semester()}
|
||||
""")
|
||||
|
||||
def change_state(self, index, state):
|
||||
@@ -985,7 +990,7 @@ class Ui(Ui_Semesterapparat):
|
||||
self.confirm_popup("Bitte erst ein document auswählen!", title="Fehler")
|
||||
return
|
||||
if not _selected_doc_location == "Database":
|
||||
path = Path(_selected_doc_location + "/" + _selected_doc_name)
|
||||
path = Path(_selected_doc_location)
|
||||
# path: Path = path.resolve()
|
||||
# path.
|
||||
# path = path + "/" + _selected_doc_name
|
||||
@@ -1119,12 +1124,53 @@ class Ui(Ui_Semesterapparat):
|
||||
return
|
||||
|
||||
self.prof_mail.setText(data.mail)
|
||||
self.prof_tel_nr.setText(str(data.phoneNumber))
|
||||
self.app_name.setText(data.title)
|
||||
self.app_fach.setCurrentText(data.subject)
|
||||
self.prof_tel_nr.setText(str(data.phoneNumber).replace("-", ""))
|
||||
if len(data.title_suggestions) > 0:
|
||||
# create a dialog that has a dropdown with the suggestions, and oc and cancel button. on ok return the selected text and set it as title
|
||||
dialog = QtWidgets.QDialog()
|
||||
dialog.setWindowTitle("Titelvorschläge")
|
||||
dialog.setModal(True)
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
label = QtWidgets.QLabel(
|
||||
f"Bitte wählen Sie einen Titel aus:/nDer Titel darf max. {data.title_max_length} Zeichen lang sein."
|
||||
)
|
||||
layout.addWidget(label)
|
||||
dropdown = QtWidgets.QComboBox()
|
||||
titles = [f"{title} [{len(title)}]" for title in data.title_suggestions]
|
||||
dropdown.addItems(titles)
|
||||
layout.addWidget(dropdown)
|
||||
button_box = QtWidgets.QDialogButtonBox()
|
||||
button_box.setStandardButtons(
|
||||
QtWidgets.QDialogButtonBox.StandardButton.Cancel
|
||||
| QtWidgets.QDialogButtonBox.StandardButton.Ok
|
||||
)
|
||||
button_box.accepted.connect(dialog.accept)
|
||||
button_box.rejected.connect(dialog.reject)
|
||||
layout.addWidget(button_box)
|
||||
dialog.setLayout(layout)
|
||||
dialog.exec()
|
||||
|
||||
if dialog.result() == QtWidgets.QDialog.DialogCode.Accepted:
|
||||
print("Selected title:", dropdown.currentText())
|
||||
self.app_name.setText(dropdown.currentText().split(" [")[0].strip())
|
||||
else:
|
||||
self.app_name.setText("CHANGEME")
|
||||
# self.app_name.setText(data.title)
|
||||
subjects = self.db.getSubjects()
|
||||
subjects = [subject[1] for subject in subjects]
|
||||
self.app_fach.setCurrentText(data.subject if data.subject in subjects else "")
|
||||
self.prof_title.setText(data.personTitle)
|
||||
self.drpdwn_prof_name.setCurrentText(data.personName)
|
||||
self.sem_year.setText("20" + data.semester.year)
|
||||
self.sem_year.setText("20" + str(data.semester.year))
|
||||
if data.semester.semester == "SoSe":
|
||||
self.sem_sommer.setChecked(True)
|
||||
self.sem_winter.setChecked(False)
|
||||
else:
|
||||
self.sem_winter.setChecked(True)
|
||||
self.sem_sommer.setChecked(False)
|
||||
if data.eternal:
|
||||
self.check_eternal_app.setChecked(True)
|
||||
self.validate_semester()
|
||||
|
||||
def btn_check_file_threaded(self):
|
||||
for runner in self.bookGrabber:
|
||||
@@ -1771,7 +1817,11 @@ def launch_gui():
|
||||
# #log.debug("checking if database available")
|
||||
|
||||
log.info("Starting login dialog")
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
app = QtWidgets.QApplication.instance()
|
||||
if app is None:
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
else:
|
||||
log.info("Using existing QApplication instance")
|
||||
login_dialog = QtWidgets.QDialog()
|
||||
ui = LoginDialog()
|
||||
ui.setupUi(login_dialog)
|
||||
@@ -1787,8 +1837,14 @@ def launch_gui():
|
||||
# #log.debug(aui.active_user)
|
||||
MainWindow.show()
|
||||
# atexit.register()
|
||||
app.aboutToQuit.connect(
|
||||
aui.validate_thread.quit
|
||||
) # if that thread uses an event loop
|
||||
app.aboutToQuit.connect(aui.docu.terminate) # our new slot
|
||||
app.aboutToQuit.connect(aui.docu.wait)
|
||||
atexit.register(tempdelete)
|
||||
atexit.register(aui.validate_thread.quit)
|
||||
# atexit.register(aui.validate_thread.quit)
|
||||
# atexit.register(aui.docu.quit)
|
||||
sys.exit(app.exec())
|
||||
|
||||
elif ui.lresult == 0:
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
from PyQt6 import QtWidgets, QtCore
|
||||
from PyQt6.QtCore import QDate
|
||||
from PyQt6.QtGui import QColor, QPen
|
||||
from src.backend import Database
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
import darkdetect
|
||||
import loguru
|
||||
import sys
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
from PySide6.QtCore import QDate
|
||||
from PySide6.QtGui import QColor, QPen
|
||||
|
||||
from src import LOG_DIR
|
||||
from src.backend import Database
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add(sys.stdout)
|
||||
log.add("logs/application.log", rotation="1 MB", retention="10 days")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||
|
||||
|
||||
color = "#ddfb00" if darkdetect.isDark() else "#2204ff"
|
||||
|
||||
@@ -16,7 +16,7 @@ __all__ = [
|
||||
|
||||
from .collapse import StatusWidget
|
||||
from .filepicker import FilePicker
|
||||
from .graph import DataGraph
|
||||
from .graph import DataQtGraph
|
||||
from .calendar_entry import CalendarEntry
|
||||
from .MessageCalendar import MessageCalendar
|
||||
from .searchPage import SearchStatisticPage
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user