diff --git a/.version b/.version index e69de29..341cf11 100644 --- a/.version +++ b/.version @@ -0,0 +1 @@ +0.2.0 \ No newline at end of file diff --git a/config/settings.yaml b/config/settings.yaml index dcd0da8..56acffc 100644 --- a/config/settings.yaml +++ b/config/settings.yaml @@ -29,6 +29,4 @@ shortcuts: advanced_refresh: false catalogue: true debug: false -log_debug: false -ic_logging: false documentation: true \ No newline at end of file diff --git a/setup.py b/setup.py index be4b37a..9cf052d 100644 --- a/setup.py +++ b/setup.py @@ -8,16 +8,21 @@ with open("pyproject.toml", "r") as file: break with open(".version", "r") as file: version = file.read().strip() + print(f"Name: {name}, Version: {version}") def rename_folders(): for folder in os.listdir("dist"): if folder.endswith(".dist"): - if "-dev" in folder: - os.rename(f"dist/{folder}", f"dist/{name}-dev") + if "_dev" in folder: + os.rename( + os.path.join("dist", folder), + os.path.join("dist", f"{name}-{version}-dev"), + ) else: - os.rename(f"dist/{folder}", f"dist/{name}-{version}") - - + os.rename( + os.path.join("dist", folder), + os.path.join("dist", f"{name}-{version}"), + ) if __name__ == "__main__": rename_folders() diff --git a/src/__init__.py b/src/__init__.py index b2c3052..7b69f4f 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,5 +1,9 @@ import sys from config import Config +from loguru import logger +from datetime import datetime +import atexit +import argparse __version__ = "0.2.0" __author__ = "Alexander Kirchner" __email__ = "alexander.kirchner@ph-freiburg.de" @@ -8,54 +12,60 @@ docport = 6543 config = Config("config/settings.yaml") -valid_args = ["--debug", "--log", "--no-backup", "--ic-logging", "--version", "-h","--no-documentation"] +_config = config +# Store original values that might be overridden by CLI -args_description = { - "--debug": "Enable debug mode", - "--log": "Enable logging", - "--no-backup": "Disable database backup", - "--ic-logging": "Enable icecream logging (not available in production)", - "--version": "Show version", - "-h": "Show help message and exit", - "--no-documentation": "Disable documentation server and shortcut" -} -args = sys.argv[1:] -if any(arg not in valid_args for arg in args): - print("Invalid argument present") - print([arg for arg in args if arg not in valid_args]) - sys.exit() - -def help(): - print("Ausleihsystem") - print("Ein Ausleihsystem für Handbibliotheken") - print("Version: {}".format(__version__)) - print("Valide Argumente:") - print("args") - print("--------") - print("usage: main.py [-h] [--debug] [--log] [--no-backup] [--ic-logging] [--version] [--no-documentation]") - print("options:") - for arg in valid_args: - print(f"{arg} : {args_description[arg]}") - -# based on the arguments, set the config values -if"-h" in args: - help() - sys.exit() -if "--debug" in args: +args = argparse.ArgumentParser() +args.add_argument("--debug", help="Debugging mode", action="store_true") +args.add_argument("--no-backup", help="Disable backups", action="store_true") +args.add_argument("--version", help="Print version", action="store_true") +args.add_argument( + "--no-documentation", help="Disable documentation", action="store_true" +) +args = args.parse_args() + +if args.version: + print(f"Version: {__version__}") + sys.exit(0) + +# Override config values temporarily without saving +changes_made = False + +if args.debug: config.debug = True -if "--log" in args: - config.log_debug = True -if "--no-backup" in args: - config.no_backup = True -if "--ic-logging" in args: - config.ic_logging = True -if "--no-documentation" in args: + changes_made = True +if args.no_backup: + config.database.do_backup = False + changes_made = True +if args.no_documentation: config.documentation = False -if "--version" in args: - print(__version__) - sys.exit() + changes_made = True + +if changes_made: + config.save() +def restore_config(): + global _config + config._config = _config + config.save() + log.info("Restored configuration") +atexit.register(restore_config) +log = logger +log.remove() +log.add("logs/application.log", rotation="1 week", compression="zip") +# add a log for the current day +log.add( + f"logs/{datetime.now().strftime('%Y-%m-%d')}.log", + rotation="1 day", + compression="zip", +) +print(config.debug) +if config.debug: + import sys + log.add( + sys.stderr, + ) \ No newline at end of file diff --git a/src/logic/catalogue.py b/src/logic/catalogue.py index 12a5ba5..b47d3a3 100644 --- a/src/logic/catalogue.py +++ b/src/logic/catalogue.py @@ -1,12 +1,10 @@ import requests from bs4 import BeautifulSoup -from src import config +from src import config, log from src.schemas import Book -from src.utils import Log URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND" BASE = "https://rds.ibs-bw.de" -log = Log("Catalogue") class Catalogue: def __init__(self, timeout=5): diff --git a/src/logic/database.py b/src/logic/database.py index 7ba1d0d..5b00538 100644 --- a/src/logic/database.py +++ b/src/logic/database.py @@ -1,12 +1,11 @@ import sqlite3 as sql import os import shutil -from src import config +from src import config, log from src.schemas import USERS, MEDIA, LOANS, User, Book, Loan -from src.utils import stringToDate, Log, debugMessage as dbg +from src.utils import stringToDate from PyQt6 import QtCore -log = Log("Database") FILE = config.database.name class Database: def __init__(self, db_path: str = None): @@ -25,12 +24,12 @@ class Database: try: os.makedirs(config.database.path) except FileNotFoundError: - dbg(self.db_path) + log.error(f"Path {config.database.path} not found") if not os.path.exists(config.database.backupLocation): os.makedirs(config.database.backupLocation) #if main path does not exist, try to create it. if that fails, use the backuplocation - dbg(self.db_path) + log.debug("Checking Database Path", self.db_path) self.checkDatabaseStatus() def handle_folder_reachability(self, original_path, backup_path): @@ -59,7 +58,7 @@ class Database: return backup_path +"/" + FILE else: - dbg("Original Path Exists, using this path") + log.info("Original Path Exists, using this path") # Original folder is reachable, check for backup if os.path.exists(backup_file): # Restore backup diff --git a/src/logic/k10plus.py b/src/logic/k10plus.py new file mode 100644 index 0000000..052ea76 --- /dev/null +++ b/src/logic/k10plus.py @@ -0,0 +1,4 @@ +import requests + +URL = "https://sru.k10plus.de/opac-de-627!rec=1?version=1.1&operation=searchRetrieve&query=" + diff --git a/src/ui/createUser.py b/src/ui/createUser.py index b90cd97..0e4dcb9 100644 --- a/src/ui/createUser.py +++ b/src/ui/createUser.py @@ -1,8 +1,9 @@ +from src import log from .sources.Ui_dialog_createUser import Ui_Dialog from PyQt6 import QtCore, QtGui, QtWidgets from PyQt6.QtGui import QRegularExpressionValidator from src.logic import Database -from src.utils import Icon, Log +from src.utils import Icon class CreateUser(QtWidgets.QDialog, Ui_Dialog): def __init__(self, fieldname, data): @@ -38,6 +39,7 @@ class CreateUser(QtWidgets.QDialog, Ui_Dialog): self.userno.textChanged.connect( lambda: self.validateInputUserno(self.userno.text(), "int") ) + log.info("User creation dialog opened") def checkFields(self): if ( self.username.hasAcceptableInput() @@ -58,7 +60,11 @@ class CreateUser(QtWidgets.QDialog, Ui_Dialog): usermail = self.user_mail.text() if self.db.insertUser(username, userno, usermail): self.userid = userno + log.info(f"User {username} created") else: + log.error( + f"User {username} could not be created, input was: {username}, {userno}, {usermail}" + ) self.setStatusTipMessage( "Benutzer konnte nicht erstellt werden, bitte überprüfen Sie die Eingaben" ) diff --git a/src/ui/loans.py b/src/ui/loans.py index ce86e52..6c26076 100644 --- a/src/ui/loans.py +++ b/src/ui/loans.py @@ -1,9 +1,9 @@ from .sources.Ui_main_Loans import Ui_MainWindow from PyQt6 import QtCore, QtGui, QtWidgets +from src import log from .user import UserUI from src.logic import Database from src.utils import stringToDate, Icon -from src.utils import debugMessage as dbg from icecream import ic TABLETOFIELDTRANSLATE = { @@ -23,8 +23,7 @@ class LoanWindow(QtWidgets.QMainWindow, Ui_MainWindow): QtWidgets.QHeaderView.ResizeMode.Stretch ) self.db = Database() - self.loans = [] - self.loadLoans() + self.loans = self.loadLoans() # lineedits self.searchbar.textChanged.connect(self.limitResults) @@ -37,6 +36,7 @@ class LoanWindow(QtWidgets.QMainWindow, Ui_MainWindow): # table self.loanTable.doubleClicked.connect(self.showUser) + log.info("Loan history window opened") self.show() def selfpass(self): @@ -44,7 +44,7 @@ class LoanWindow(QtWidgets.QMainWindow, Ui_MainWindow): def insertRow(self, data): - dbg(contents=data) + log.debug(f"Inserting row: {data}") retdate = "" if data.returned_date != "": @@ -77,9 +77,10 @@ class LoanWindow(QtWidgets.QMainWindow, Ui_MainWindow): def loadLoans(self): loans = self.db.getAllLoans() + loanlist = [] for loan in loans: self.insertRow(loan) - self.loans = loans + return loanlist def filterResults(self): mode = ( @@ -112,7 +113,7 @@ class LoanWindow(QtWidgets.QMainWindow, Ui_MainWindow): limiter = str(limiter) searchfield = self.searchFields.currentText() searchfield = TABLETOFIELDTRANSLATE[searchfield] - dbg(limiter=limiter, search=searchfield) + log.debug(f"Searching for: {limiter} in {searchfield}") self.loanTable.setRowCount(0) for loan in self.loans: fielddata = eval(f"loan.{searchfield}") diff --git a/src/ui/main_ui.py b/src/ui/main_ui.py index f57936c..407f2a4 100644 --- a/src/ui/main_ui.py +++ b/src/ui/main_ui.py @@ -2,10 +2,9 @@ import sys import atexit import datetime import webbrowser -from src import config, __email__, docport +from src import config, __email__, docport, log from src.logic import Database, Catalogue, Backup -from src.utils import stringToDate, Icon, Log -from src.utils import debugMessage as dbg +from src.utils import stringToDate, Icon from src.utils.createReport import generate_report from src.schemas import Book from .sources.Ui_main_UserInterface import Ui_MainWindow @@ -22,8 +21,6 @@ from omegaconf import OmegaConf from src.logic.documentation_thread import DocumentationThread backup = Backup() cat = Catalogue() -log = Log("main") -dbg(backup=config.database.do_backup, catalogue=config.catalogue) def getShortcut(shortcuts, name): shortcut = [cut for cut in shortcuts if cut["name"] == name][0] @@ -183,7 +180,7 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): def restart(self): #log restart - dbg("Restarting Program") + log.info("Restarting Program") import os python_executable = sys.executable args = sys.argv[:] @@ -192,8 +189,7 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): def changeMode(self): - log.info("Changing Mode") - dbg(f"Current mode: {self.activeState}") + log.info("Changing Mode, current mode is {}", self.activeState) self.input_username.clear() stayReturn = False if config.advanced_refresh and self.userdata.toPlainText() != "": @@ -216,7 +212,7 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): self.activateReturnMode() def activateLoanMode(self): - dbg("Activating Loan Mode") + log.info("Activating Loan Mode") self.input_username.setEnabled(True) self.input_userno.setEnabled(True) self.duedate.setEnabled(True) @@ -234,7 +230,7 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): self.input_file_ident.setEnabled(True) def activateReturnMode(self): - dbg("Activating Return Mode") + log.info("Activating Return Mode") self.input_username.setEnabled(False) self.input_userno.setEnabled(False) # set mode background color to orange @@ -326,7 +322,7 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): self.mode.setText("Ausleihe") #print(self.activeUser.__dict__) loans = self.db.getActiveLoans(self.activeUser.id) - dbg(loans=loans) + log.debug("Active Loans", loans) self.btn_show_lentmedia.setText(loans) retdate = self.db.selectClosestReturnDate(self.activeUser.id) if retdate: @@ -375,16 +371,16 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): self.mediaAdd(value) self.input_file_ident.setFocus() def mediaAdd(self, identifier): - dbg("Adding Media", identifier = identifier) + log.info("Adding Media", identifier=identifier) self.input_file_ident.clear() self.input_file_ident.setEnabled(False) user_id = self.activeUser.id media = Book(signature=identifier) book_id = self.db.checkMediaExists(media) - dbg(f"Book ID: {book_id}, User ID: {user_id}", media=media) + log.debug(f"Book ID: {book_id}, User ID: {user_id}", media=media) if not book_id: - dbg("Book not found, searching catalogue") + log.info("Book not found, searching catalogue") if config.catalogue == True: media = cat.get_book(identifier) if not media: @@ -441,7 +437,7 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): for entry in newentries: book = self.db.getMedia(entry) self.loanMedia(user_id, [entry], book) - dbg("inserted duplicated book into database") + log.info("inserted duplicated book into database") return else: #print("Book not loaned, loaning now") @@ -506,7 +502,7 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): self.setStatusTipMessage("Buch nicht entliehen") self.input_file_ident.clear() else: - dbg("Book not found") + log.error("Book not found, identifier", identifier) #print("Book not found") #self.input_file_ident.setPlaceholderText(f"Buch {identifier} nicht gefunden") @@ -520,13 +516,13 @@ class MainUI(QtWidgets.QMainWindow, Ui_MainWindow): dialog.setText(message) dialog.exec() def exit_handler(): - dbg("Exiting, creating backup") + log.info("Exiting, creating backup") app = QtWidgets.QApplication(sys.argv) #print(backup.backup) # generate report if monday if datetime.datetime.now().weekday() == config.report.report_day: generate_report() - dbg("Generated Report") + log.info("Generated Report") Database().renameInactiveUsers() if config.database.do_backup: state = backup.createBackup() @@ -541,7 +537,7 @@ def exit_handler(): dialog.setText("Backup konnte nicht erstellt werden") dialog.exec() - dbg("Exiting", backupstate=state) + log.info("Exiting, backup:", state) else: dialog = QtWidgets.QMessageBox() # set icon diff --git a/src/ui/settings.py b/src/ui/settings.py index 8acce08..2fd36d9 100644 --- a/src/ui/settings.py +++ b/src/ui/settings.py @@ -1,8 +1,7 @@ from .sources.Ui_dialog_settings import Ui_Dialog from PyQt6 import QtWidgets, QtCore +from src import config, log from src.utils import Icon -from src import config -from src.utils import debugMessage as dbg from omegaconf import OmegaConf import os @@ -192,7 +191,7 @@ class Settings(QtWidgets.QDialog, Ui_Dialog): if changed == original: self.settingschanged = False self.restart_required = False - dbg("Settings not changed") + log.info("Settings not changed") else: self.settingschanged = True #compare if database or shortcuts were changed @@ -200,7 +199,11 @@ class Settings(QtWidgets.QDialog, Ui_Dialog): shortcuts = self.shortcuts == self.sortShortcuts(changed.shortcuts) if not database or not shortcuts: self.restart_required = True - dbg(f"Settings changed, restart required: {self.restart_required}",database=database,shortcuts=shortcuts) + log.info( + f"Settings changed, restart required: {self.restart_required}", + database=database, + shortcuts=shortcuts, + ) # save the new settings if self.settingschanged: diff --git a/src/ui/user.py b/src/ui/user.py index bc3a7b3..b6774ec 100644 --- a/src/ui/user.py +++ b/src/ui/user.py @@ -1,10 +1,10 @@ from .sources.Ui_main_userData import Ui_MainWindow from PyQt6 import QtCore, QtGui, QtWidgets +from src import log from src.logic import Database from src.schemas import User from .extendLoan import ExtendLoan from src.utils import stringToDate, Icon -from src.utils import debugMessage as dbg TABLETOFIELDTRANSLATE = { "Titel": "title", @@ -95,7 +95,6 @@ class UserUI(QtWidgets.QMainWindow, Ui_MainWindow): limiter = self.searchbox.text().lower() searchfield = self.searchfilter.currentText() searchfield = TABLETOFIELDTRANSLATE[searchfield] - # dbg(limiter=limiter, search=searchfield) self.UserMediaTable.setRowCount(0) for loan in self.userMedia: @@ -166,7 +165,7 @@ class UserUI(QtWidgets.QMainWindow, Ui_MainWindow): continue elif mode == "overdue": # book not returned and todays date is greater than todate - dbg(book=book) + log.debug("Book: {}".format(book)) if book.returned_date is not None: continue # if todate is greater than current date, continue diff --git a/src/utils/__init__.py b/src/utils/__init__.py index d004bfe..1753435 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -1,5 +1,3 @@ -from .log import Log from .icon import Icon -from .debug import debugMessage from .stringtodate import stringToDate from .documentation import launch_documentation \ No newline at end of file diff --git a/src/utils/debug.py b/src/utils/debug.py deleted file mode 100644 index 8616e92..0000000 --- a/src/utils/debug.py +++ /dev/null @@ -1,18 +0,0 @@ -from src import config -from icecream import ic -from src.utils import Log -log = Log("debugMessage") - -def debugMessage(*args, **kwargs): - startmessage = "Logging debug message" - # join args and kwargs to a string - message = " ".join(args) - for key, value in kwargs.items(): - message += f" {key}: {value}" - if config.debug: - if config.log_debug: - log.info(f"{startmessage}: {message}") - if config.ic_logging == True: - ic(message) - else: print(message) - return message \ No newline at end of file diff --git a/src/utils/documentation.py b/src/utils/documentation.py index f4a0cc8..8dbcbc5 100644 --- a/src/utils/documentation.py +++ b/src/utils/documentation.py @@ -1,7 +1,8 @@ from pyramid.config import Configurator from wsgiref.simple_server import make_server -from src import docport +from src import docport, log import os +import sys def website(): config = Configurator() @@ -14,8 +15,17 @@ def website(): def launch_documentation(): app = website() server = make_server('localhost', 6543, app) - print("Serving MkDocs documentation on http://0.0.0.0:{}".format(docport)) - server.serve_forever() + log.info("Serving MkDocs documentation on http://0.0.0.0:{}".format(docport)) + with open(os.devnull, "w") as devnull: + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdout = devnull + sys.stderr = devnull + try: + server.serve_forever() + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr if __name__ == '__main__': pass diff --git a/src/utils/log.py b/src/utils/log.py deleted file mode 100644 index 6ba3717..0000000 --- a/src/utils/log.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - - -class Log: - def __init__(self, name): - self.logger = logging.getLogger(name) - self.logger.setLevel(logging.DEBUG) - self.formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - ) - self.file_handler = logging.FileHandler("log.log") - self.file_handler.setLevel(logging.DEBUG) - self.file_handler.setFormatter(self.formatter) - self.logger.addHandler(self.file_handler) - - def info(self, message): - self.logger.info(message) - - def debug(self, message): - self.logger.debug(message) - - def error(self, message): - self.logger.error(message) - - def warning(self, message): - self.logger.warning(message) \ No newline at end of file diff --git a/src/utils/stringtodate.py b/src/utils/stringtodate.py index 98cabb3..d2333c4 100644 --- a/src/utils/stringtodate.py +++ b/src/utils/stringtodate.py @@ -1,6 +1,6 @@ # import qdate from PyQt6 import QtCore -from .debug import debugMessage +from src import log def stringToDate(date: str) -> QtCore.QDate: """Takes an input string and returns a QDate object. @@ -10,7 +10,7 @@ def stringToDate(date: str) -> QtCore.QDate: Returns: QtCore.QDate: the QDate object in string format DD.MM.yyyy """ - debugMessage(date=date) + log.debug(f"stringToDate: {date}") if not date: return "" if isinstance(date, QtCore.QDate): @@ -21,22 +21,3 @@ def stringToDate(date: str) -> QtCore.QDate: month = datedata[1] year = datedata[0] return QtCore.QDate(int(year), int(month), int(day)) - - # else: - # datedata = date.split(" ")[1:] - # month = datedata[0] - # day = datedata[1] - # year = datedata[2] - # month = month.replace("Jan", "01") - # month = month.replace("Feb", "02") - # month = month.replace("Mar", "03") - # month = month.replace("Apr", "04") - # month = month.replace("May", "05") - # month = month.replace("Jun", "06") - # month = month.replace("Jul", "07") - # month = month.replace("Aug", "08") - # month = month.replace("Sep", "09") - # month = month.replace("Oct", "10") - # month = month.replace("Nov", "11") - # month = month.replace("Dec", "12") - # return QtCore.QDate(int(year), int(month), int(day)).toString("dd.MM.yyyy")