Files
SemesterapparatsManager/config/config.py
2026-02-12 08:54:19 +01:00

276 lines
7.6 KiB
Python

from typing import Optional, Any, Union
from dataclasses import dataclass
from omegaconf import OmegaConf, DictConfig, ListConfig
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:
api_key: str
library_id: str
library_type: str
def getattr(self, name: str):
return getattr(self, name)
def _setattr(self, name: str, value: Any):
setattr(self, name, value)
@dataclass
class Database:
name: str
path: Union[str, Path, None]
temp: Union[str, Path, None]
def getattr(self, name: str):
return getattr(self, name)
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:
smtp_server: str
port: int
sender: str
sender_name: str
password: str
use_user_name: bool
printer_mail: str
user_name: str
signature: str | None = None
empty_signature = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style
type="text/css">
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: "\2610"; }
li.checked::marker { content: "\2612"; }
</style></head><body style=" font-family:''Segoe UI''; font-size:9pt; font-weight:400;
font-style:normal;">
<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: str):
return getattr(self, name)
def _setattr(self, name: str, value: Any):
setattr(self, name, value)
def setValue(self, **kwargs: Any):
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
else:
raise KeyError(f"Invalid option: {key}")
class Icons:
def __init__(self):
self._path = None
self._colors = None
self._icons = None
def assign(self, key: str, value: Any):
setattr(self, key, value)
@property
def path(self):
return self._path
@path.setter
def path(self, value: Any):
self._path = value
@property
def colors(self):
return self._colors
@colors.setter
def colors(self, value: Any):
self._colors = value
@property
def icons(self):
return self._icons
@icons.setter
def icons(self, value: Any):
self._icons = value
def get(self, name: str):
return self.icons.get(name)
class Config:
"""A class to handle the configuration of the application. After initializing, it will try to load the config file and store it for future access. Any changes made can be saved to the file using the .save() method. Changes are used in real time in the app, if a restart is required, the Application will show a window.
Raises:
RuntimeError: Configuration not loaded
KeyError: Invalid option
"""
_config: Optional[Union[DictConfig, ListConfig]] = None
config_exists: bool = True
def __init__(self, config_path: str):
"""
Loads the configuration file and stores it for future access.
Args:
config_path (str): Path to the YAML configuration file.
Raises:
FileNotFoundError: Configuration file not found
"""
if not os.path.exists(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.
Args:
config_path (str): Path to the YAML configuration file.
"""
OmegaConf.save(self._config, self.config_path)
def reload(self):
"""
Reloads the configuration from the file.
"""
if self.config_path is not None:
self._config = OmegaConf.load(self.config_path)
@property
def zotero(self):
if self._config is None:
raise RuntimeError("Configuration not loaded")
return Zotero(**self._config.zotero)
def get_zotero_attr(self, name: str):
return getattr(self.zotero, name)
def set_zotero_attr(self, name: str, value: Any):
self.zotero._setattr(name, value)
@property
def database(self):
if self._config is None:
raise RuntimeError("Configuration not loaded")
return Database(**self._config.database)
@property
def database_attr(self, name: str):
return getattr(self.database, name)
@database_attr.setter
def database_attr(self, name: str, value: Any):
self.database._setattr(name, value)
@property
def openAI(self):
if self._config is None:
raise RuntimeError("Configuration not loaded")
return OpenAI(**self._config.openAI)
@property
def mail(self):
if self._config is None:
raise RuntimeError("Configuration not loaded")
return Mail(**self._config.mail)
def mail_attr(self, name: str):
return getattr(self.mail, name)
def set_mail_attr(self, name: str, value: Any):
if self._config is not None:
OmegaConf.update(self._config, f"mail.{name}", value)
def set_database_attr(self, name: str, value: Any):
if self._config is not None:
OmegaConf.update(self._config, f"database.{name}", value)
def set_zotero_attr(self, name: str, value: Any):
if self._config is not None:
OmegaConf.update(self._config, f"zotero.{name}", value)
def set_openai_attr(self, name: str, value: Any):
if self._config is not None:
OmegaConf.update(self._config, f"openAI.{name}", value)
def set_icon_attr(self, name: str, value: Any):
if self._config is not None:
OmegaConf.update(self._config, f"icons.{name}", value)
@property
def save_path(self):
if self._config is None:
raise RuntimeError("Configuration not loaded")
return self._config.save_path
@save_path.setter
def save_path(self, value: str):
if self._config is not None:
self._config.save_path = value
def load_config(self, path, filename):
return OmegaConf.load(os.path.join(path, filename))
@property
def icons(self):
if self._config is None:
raise RuntimeError("Configuration not loaded")
icons = Icons()
icons.assign("path", self._config.icon_path)
icons.assign("colors", self._config.colors)
icons.assign("icons", self._config.icons)
return icons
def dict(self):
return OmegaConf.to_container(self._config)