From ac7c7ad60c78e7d5903379b86f7873379913086d Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 14 May 2025 15:06:24 +0200 Subject: [PATCH 01/42] fix typo #2 --- .gitea/workflows/release.yml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index acc5f08..a48d035 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -52,24 +52,7 @@ jobs: if: ${{ github.event.inputs.github_release == 'true' }} uses: softprops/action-gh-release@v1 with: - tag_name: - name: Add release notes - id: add_release_notes - run: | - echo "RELEASE_NOTES<> $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 - 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 From b1d523f574817f792a0c0099146afb0319e676a7 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Mon, 26 May 2025 13:22:47 +0200 Subject: [PATCH 02/42] update wordparser --- src/logic/wordparser.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/logic/wordparser.py b/src/logic/wordparser.py index c0ad35a..77391a1 100644 --- a/src/logic/wordparser.py +++ b/src/logic/wordparser.py @@ -3,13 +3,14 @@ from docx import Document from dataclasses import dataclass from src.backend import Semester from typing import Union, Any +from src.logic.openai import name_tester, run_shortener, semester_converter import loguru import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") @@ -75,12 +76,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 +114,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,7 +131,7 @@ 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", "") @@ -117,7 +143,6 @@ 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 @@ -220,6 +245,7 @@ def word_to_semap(word_path: str) -> SemapDocument: log.info("Parsing Word Document {}", word_path) semap = SemapDocument() df = word_docx_to_csv(word_path) + print(df) apparatdata = df[0] apparatdata = apparatdata.to_dict() @@ -238,6 +264,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 +282,6 @@ def word_to_semap(word_path: str) -> SemapDocument: booklist.append(book) log.info("Found {} books", len(booklist)) semap.books = booklist - return semap From bb4c4c40036683a3fed1fe94b43bfbefe3907804 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 13:13:50 +0200 Subject: [PATCH 03/42] add openai dependency --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index de369ce..7df402e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ 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", From e29b63040546a54fcfb61641c5a2be74cfca5441 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 13:14:13 +0200 Subject: [PATCH 04/42] include chatgpt section to config --- config/config.py | 67 +++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/config/config.py b/config/config.py index 8619b2a..9a50cb9 100644 --- a/config/config.py +++ b/config/config.py @@ -1,8 +1,18 @@ -from typing import Optional +from typing import Optional, Any from dataclasses import dataclass from omegaconf import OmegaConf, DictConfig import os +from pathlib import Path +@dataclass +class OpenAI: + api_key: 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 +20,30 @@ 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 + path: str | Path + temp: str | Path - 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 __post_init__(self): + if isinstance(self.path, str): + self.path = Path(self.path) + if isinstance(self.temp, str): + self.temp = Path(self.temp) @dataclass class Mail: @@ -59,13 +74,13 @@ class Mail:


""" - 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) @@ -151,11 +166,11 @@ class Config: 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 +178,34 @@ 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_icon_attr(self, name: str, value: Any): OmegaConf.update(self._config, f"icons.{name}", value) @property From d02a8a271fd040c227c48f4beb6f858178697e03 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 13:15:06 +0200 Subject: [PATCH 05/42] rework wordparser - add dropdown selector - fix bug where Telefon: key got overwritten --- src/logic/wordparser.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/logic/wordparser.py b/src/logic/wordparser.py index 77391a1..c113e16 100644 --- a/src/logic/wordparser.py +++ b/src/logic/wordparser.py @@ -4,7 +4,8 @@ from dataclasses import dataclass from src.backend import Semester from typing import Union, Any from src.logic.openai import name_tester, run_shortener, semester_converter - +import zipfile +from bs4 import BeautifulSoup import loguru import sys @@ -134,8 +135,11 @@ def word_docx_to_csv(path: str) -> list[pd.DataFrame]: 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] @@ -143,10 +147,27 @@ def word_docx_to_csv(path: str) -> list[pd.DataFrame]: m_data.append(df) - 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: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, @@ -245,13 +266,12 @@ def word_to_semap(word_path: str) -> SemapDocument: log.info("Parsing Word Document {}", word_path) semap = SemapDocument() df = word_docx_to_csv(word_path) - print(df) 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:"] From abe17d8c575f8d407184cb78de8a340159c589bd Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 13:16:03 +0200 Subject: [PATCH 06/42] fix path decoding --- src/__init__.py | 4 ++-- src/backend/database.py | 33 ++++++++++++++--------------- src/backend/delete_temp_contents.py | 5 +---- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index 2661405..fdef22f 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -4,8 +4,8 @@ import os settings = Config("config/config.yaml") -if not os.path.exists(settings.database.temp): - os.mkdir(settings.database.temp) +if not os.path.exists(settings.database.temp.expanduser()): + settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True) from .utils.icon import Icon __version__ = "0.2.1" diff --git a/src/backend/database.py b/src/backend/database.py index 645d023..2341db3 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -19,9 +19,11 @@ from src.backend.db import ( CREATE_TABLE_SUBJECTS, CREATE_TABLE_USER, ) +from pathlib import Path from src.errors import AppPresentError, NoResultError from src.logic import ApparatData, BookData, Prof, Apparat, ELSA 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 @@ -29,7 +31,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") @@ -44,7 +46,7 @@ class 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: Path = None): """ Default constructor for the database class @@ -52,17 +54,17 @@ 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())) + self.db_path = Path(self.database.path.expanduser(), self.database.name) + # self.db_path = self.db_path.replace("~", str(Path.home())) log.debug(self.db_path) else: self.db_path = db_path self.checkDatabaseStatus() def checkDatabaseStatus(self): - path = self.database.path - path = path.replace("~", str(Path.home())) - path = os.path.abspath(path) + path = self.database.path.expanduser() + # path = path.replace("~", str(Path.home())) + # path = os.path.abspath(path) if not os.path.exists(path): # create path # log.debug(path) @@ -503,11 +505,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 = self.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}" ) @@ -1463,11 +1463,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 = self.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}" ) diff --git a/src/backend/delete_temp_contents.py b/src/backend/delete_temp_contents.py index c4fbfc5..21f393c 100644 --- a/src/backend/delete_temp_contents.py +++ b/src/backend/delete_temp_contents.py @@ -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)) From b874656ebaaea87cb6bb4bdf9f31cabbcbb09a08 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 13:17:13 +0200 Subject: [PATCH 07/42] set log level to INFO for improved logging clarity --- src/backend/create_file.py | 2 +- src/backend/semester.py | 2 +- src/backend/thread_bookgrabber.py | 4 +-- src/backend/threads_autoadder.py | 2 +- src/backend/threads_availchecker.py | 2 +- src/logic/webrequest.py | 2 +- src/transformers/transformers.py | 2 +- src/ui/dialogs/login.py | 2 +- src/ui/dialogs/mail.py | 2 +- src/ui/dialogs/parsed_titles.py | 2 +- src/ui/dialogs/settings.py | 6 ++-- src/ui/userInterface.py | 55 +++++++++++++++++++++++++---- src/ui/widgets/MessageCalendar.py | 2 +- src/ui/widgets/admin_edit_prof.py | 2 +- src/ui/widgets/elsa_main.py | 2 +- src/ui/widgets/graph.py | 2 +- src/ui/widgets/searchPage.py | 2 +- 17 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/backend/create_file.py b/src/backend/create_file.py index b19e1e0..5be6d59 100644 --- a/src/backend/create_file.py +++ b/src/backend/create_file.py @@ -9,7 +9,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/backend/semester.py b/src/backend/semester.py index d2fb51e..42a101a 100644 --- a/src/backend/semester.py +++ b/src/backend/semester.py @@ -6,7 +6,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/backend/thread_bookgrabber.py b/src/backend/thread_bookgrabber.py index 5d5ab49..36c99f9 100644 --- a/src/backend/thread_bookgrabber.py +++ b/src/backend/thread_bookgrabber.py @@ -8,12 +8,12 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/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): diff --git a/src/backend/threads_autoadder.py b/src/backend/threads_autoadder.py index 2ceec0e..fa73c55 100644 --- a/src/backend/threads_autoadder.py +++ b/src/backend/threads_autoadder.py @@ -10,7 +10,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/backend/threads_availchecker.py b/src/backend/threads_availchecker.py index 5aebb4f..7d6b3b4 100644 --- a/src/backend/threads_availchecker.py +++ b/src/backend/threads_availchecker.py @@ -14,7 +14,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/logic/webrequest.py b/src/logic/webrequest.py index 5626ccf..ee12047 100644 --- a/src/logic/webrequest.py +++ b/src/logic/webrequest.py @@ -13,7 +13,7 @@ import loguru import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") # logger.add(sys.stderr, format="{time} {level} {message}", level="INFO") diff --git a/src/transformers/transformers.py b/src/transformers/transformers.py index 98d5758..84f8a22 100644 --- a/src/transformers/transformers.py +++ b/src/transformers/transformers.py @@ -13,7 +13,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/dialogs/login.py b/src/ui/dialogs/login.py index e1e563e..a446ec0 100644 --- a/src/ui/dialogs/login.py +++ b/src/ui/dialogs/login.py @@ -13,7 +13,7 @@ from src import Icon log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/dialogs/mail.py b/src/ui/dialogs/mail.py index d7e0362..7b59e9b 100644 --- a/src/ui/dialogs/mail.py +++ b/src/ui/dialogs/mail.py @@ -13,7 +13,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/dialogs/parsed_titles.py b/src/ui/dialogs/parsed_titles.py index 1142fe2..e1f2777 100644 --- a/src/ui/dialogs/parsed_titles.py +++ b/src/ui/dialogs/parsed_titles.py @@ -9,7 +9,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") class ParsedTitles(QtWidgets.QWidget, Ui_Form): diff --git a/src/ui/dialogs/settings.py b/src/ui/dialogs/settings.py index 5211a1b..51ed07b 100644 --- a/src/ui/dialogs/settings.py +++ b/src/ui/dialogs/settings.py @@ -7,7 +7,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") @@ -80,8 +80,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) diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index fb45198..f79c41f 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -60,7 +60,7 @@ import loguru log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") log.add( @@ -556,6 +556,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 +564,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 +987,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 +1121,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: diff --git a/src/ui/widgets/MessageCalendar.py b/src/ui/widgets/MessageCalendar.py index 487153e..00ee0d0 100644 --- a/src/ui/widgets/MessageCalendar.py +++ b/src/ui/widgets/MessageCalendar.py @@ -9,7 +9,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/widgets/admin_edit_prof.py b/src/ui/widgets/admin_edit_prof.py index dfb9195..d4f4384 100644 --- a/src/ui/widgets/admin_edit_prof.py +++ b/src/ui/widgets/admin_edit_prof.py @@ -8,7 +8,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/widgets/elsa_main.py b/src/ui/widgets/elsa_main.py index 0a0b5a4..ef3052f 100644 --- a/src/ui/widgets/elsa_main.py +++ b/src/ui/widgets/elsa_main.py @@ -14,7 +14,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/widgets/graph.py b/src/ui/widgets/graph.py index cc9cfc0..e17523c 100644 --- a/src/ui/widgets/graph.py +++ b/src/ui/widgets/graph.py @@ -8,7 +8,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 87fe6ea..27b556f 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -13,7 +13,7 @@ import sys log = loguru.logger log.remove() -log.add(sys.stdout) +log.add(sys.stdout, level="INFO") log.add("logs/application.log", rotation="1 MB", retention="10 days") From 771062ab7fe145f5789b7d3b25532851825b94b2 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 13:17:36 +0200 Subject: [PATCH 08/42] add openai implementation to take over some parsing functionality --- src/logic/openai.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/logic/openai.py diff --git a/src/logic/openai.py b/src/logic/openai.py new file mode 100644 index 0000000..f8b00a7 --- /dev/null +++ b/src/logic/openai.py @@ -0,0 +1,42 @@ +from openai import OpenAI +from src import settings +import json + +model = "gpt-4o" +api_key = settings.openAI.api_key +client = OpenAI(api_key = api_key) + +def run_shortener(title:str, length:int): + 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}}}' + ) + print(length) + answers = response.output_text + print(answers) + 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): + 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): + 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 \ No newline at end of file From a7b82ee3beb5437d52d4cbcb021f136e50676548 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 13:17:55 +0200 Subject: [PATCH 09/42] update lockfile --- uv.lock | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/uv.lock b/uv.lock index 51d6c6b..5eae4e2 100644 --- a/uv.lock +++ b/uv.lock @@ -252,6 +252,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl", hash = "sha256:a7509ccf517eaad92b31c214f593dbcf138ea8a43b2935406bbd565e15527a85", size = 8955 }, ] +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + [[package]] name = "docx2pdf" version = "0.1.8" @@ -406,6 +415,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] +[[package]] +name = "jiter" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262 }, + { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124 }, + { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330 }, + { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670 }, + { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057 }, + { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372 }, + { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038 }, + { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538 }, + { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557 }, + { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202 }, + { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781 }, + { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176 }, + { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617 }, + { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947 }, + { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618 }, + { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829 }, + { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034 }, + { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529 }, + { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671 }, + { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864 }, + { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989 }, + { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495 }, + { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289 }, + { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074 }, + { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225 }, + { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235 }, + { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278 }, + { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866 }, + { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772 }, + { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534 }, + { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694 }, + { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992 }, + { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723 }, + { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215 }, + { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762 }, + { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427 }, + { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127 }, + { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527 }, + { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, +] + [[package]] name = "loguru" version = "0.7.3" @@ -677,6 +734,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/94/1843518e420fa3ed6919835845df698c7e27e183cb997394e4a670973a65/omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b", size = 79500 }, ] +[[package]] +name = "openai" +version = "1.79.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/cf/4901077dbbfd0d82a814d721600fa0c3a61a093d7f0bf84d0e4732448dc9/openai-1.79.0.tar.gz", hash = "sha256:e3b627aa82858d3e42d16616edc22aa9f7477ee5eb3e6819e9f44a961d899a4c", size = 444736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/d2/e3992bb7c6641b765c1008e3c96e076e0b50381be2cce344e6ff177bad80/openai-1.79.0-py3-none-any.whl", hash = "sha256:d5050b92d5ef83f869cb8dcd0aca0b2291c3413412500eec40c66981b3966992", size = 683334 }, +] + [[package]] name = "ordered-set" version = "4.1.0" @@ -1156,6 +1232,7 @@ dependencies = [ { name = "mkdocs-material-extensions" }, { name = "natsort" }, { name = "omegaconf" }, + { name = "openai" }, { name = "pandas" }, { name = "playwright" }, { name = "pyqt6" }, @@ -1187,6 +1264,7 @@ requires-dist = [ { name = "mkdocs-material-extensions", specifier = ">=1.3.1" }, { name = "natsort", specifier = ">=8.4.0" }, { name = "omegaconf", specifier = ">=2.3.0" }, + { name = "openai", specifier = ">=1.79.0" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "playwright", specifier = ">=1.49.1" }, { name = "pyqt6", specifier = ">=6.8.0" }, From 9684229fc2120c7592ad43efa81fe3f3783caa46 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 15:27:31 +0200 Subject: [PATCH 10/42] add appdirs dependency --- pyproject.toml | 1 + uv.lock | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7df402e..59b20d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ 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", diff --git a/uv.lock b/uv.lock index 5eae4e2..ee8e501 100644 --- a/uv.lock +++ b/uv.lock @@ -30,6 +30,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, ] +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566 }, +] + [[package]] name = "appscript" version = "1.3.0" @@ -1220,6 +1229,7 @@ name = "semesterapparatsmanager" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "appdirs" }, { name = "beautifulsoup4" }, { name = "bump-my-version" }, { name = "chardet" }, @@ -1252,6 +1262,7 @@ dev = [ [package.metadata] requires-dist = [ + { name = "appdirs", specifier = ">=1.4.4" }, { name = "beautifulsoup4", specifier = ">=4.12.3" }, { name = "bump-my-version", specifier = ">=0.29.0" }, { name = "chardet", specifier = ">=5.2.0" }, From 3fbb8bbd52c3f038521ae79d3d169bd821c126fa Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 15:28:14 +0200 Subject: [PATCH 11/42] add openai model to config, rework logging to use appdirs logging dir --- config/config.py | 1 + src/__init__.py | 18 ++++++++--- src/backend/create_file.py | 4 +-- src/backend/database.py | 27 ++++++++-------- src/backend/semester.py | 4 +-- src/backend/thread_bookgrabber.py | 4 +-- src/backend/threads_autoadder.py | 4 +-- src/backend/threads_availchecker.py | 4 +-- src/logic/openai.py | 3 +- src/logic/webrequest.py | 3 +- src/logic/wordparser.py | 23 ++++++++------ src/transformers/transformers.py | 4 +-- src/ui/dialogs/login.py | 9 +++--- src/ui/dialogs/mail.py | 4 +-- src/ui/dialogs/parsed_titles.py | 4 +-- src/ui/dialogs/settings.py | 3 +- src/ui/userInterface.py | 48 ++++++++++++++--------------- src/ui/widgets/MessageCalendar.py | 3 +- src/ui/widgets/admin_edit_prof.py | 4 +-- src/ui/widgets/elsa_main.py | 4 +-- src/ui/widgets/graph.py | 3 +- src/ui/widgets/searchPage.py | 4 +-- 22 files changed, 101 insertions(+), 84 deletions(-) diff --git a/config/config.py b/config/config.py index 9a50cb9..d013607 100644 --- a/config/config.py +++ b/config/config.py @@ -7,6 +7,7 @@ from pathlib import Path @dataclass class OpenAI: api_key: str + model: str def getattr(self, name: str): return getattr(self, name) diff --git a/src/__init__.py b/src/__init__.py index fdef22f..32f9353 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,17 +1,25 @@ +__version__ = "0.2.1" +__author__ = "Alexander Kirchner" __all__ = ["__version__", "__author__", "Icon", "settings"] -from config import Config import os +from config import Config +from appdirs import AppDirs + +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("config/config.yaml") if not os.path.exists(settings.database.temp.expanduser()): settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True) from .utils.icon import Icon -__version__ = "0.2.1" -__author__ = "Alexander Kirchner" - - if not os.path.exists("logs"): os.mkdir("logs") # open and close the file to create it diff --git a/src/backend/create_file.py b/src/backend/create_file.py index 5be6d59..ce1e2b9 100644 --- a/src/backend/create_file.py +++ b/src/backend/create_file.py @@ -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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") db = Database() diff --git a/src/backend/database.py b/src/backend/database.py index 2341db3..90f765d 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -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 from src.backend.db import ( CREATE_ELSA_FILES_TABLE, CREATE_ELSA_MEDIA_TABLE, @@ -19,20 +25,17 @@ from src.backend.db import ( CREATE_TABLE_SUBJECTS, CREATE_TABLE_USER, ) -from pathlib import Path 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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/backend/semester.py b/src/backend/semester.py index 42a101a..392e593 100644 --- a/src/backend/semester.py +++ b/src/backend/semester.py @@ -3,11 +3,11 @@ import datetime from dataclasses import dataclass import loguru import sys - +from src import LOG_DIR log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/backend/thread_bookgrabber.py b/src/backend/thread_bookgrabber.py index 36c99f9..0db468e 100644 --- a/src/backend/thread_bookgrabber.py +++ b/src/backend/thread_bookgrabber.py @@ -5,11 +5,11 @@ 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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") # logger.add(sys.stderr, format="{time} {level} {message}", level="INFO") diff --git a/src/backend/threads_autoadder.py b/src/backend/threads_autoadder.py index fa73c55..6ac53bf 100644 --- a/src/backend/threads_autoadder.py +++ b/src/backend/threads_autoadder.py @@ -7,11 +7,11 @@ from PyQt6.QtCore import pyqtSignal 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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") # from src.transformers import RDS_AVAIL_DATA diff --git a/src/backend/threads_availchecker.py b/src/backend/threads_availchecker.py index 7d6b3b4..b3b08c5 100644 --- a/src/backend/threads_availchecker.py +++ b/src/backend/threads_availchecker.py @@ -5,7 +5,7 @@ from PyQt6.QtCore import QThread from PyQt6.QtCore import pyqtSignal 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 @@ -15,7 +15,7 @@ import sys log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/logic/openai.py b/src/logic/openai.py index f8b00a7..6ee11fe 100644 --- a/src/logic/openai.py +++ b/src/logic/openai.py @@ -1,8 +1,9 @@ from openai import OpenAI from src import settings import json +from src import LOG_DIR -model = "gpt-4o" +model = settings.openAI.model api_key = settings.openAI.api_key client = OpenAI(api_key = api_key) diff --git a/src/logic/webrequest.py b/src/logic/webrequest.py index ee12047..19505d1 100644 --- a/src/logic/webrequest.py +++ b/src/logic/webrequest.py @@ -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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") # logger.add(sys.stderr, format="{time} {level} {message}", level="INFO") diff --git a/src/logic/wordparser.py b/src/logic/wordparser.py index c113e16..e3741c9 100644 --- a/src/logic/wordparser.py +++ b/src/logic/wordparser.py @@ -1,18 +1,21 @@ -import pandas as pd -from docx import Document -from dataclasses import dataclass -from src.backend import Semester -from typing import Union, Any -from src.logic.openai import name_tester, run_shortener, semester_converter -import zipfile -from bs4 import BeautifulSoup -import loguru import sys +import zipfile +from dataclasses import dataclass +from typing import Any, Union + +import loguru +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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/transformers/transformers.py b/src/transformers/transformers.py index 84f8a22..a7ef88a 100644 --- a/src/transformers/transformers.py +++ b/src/transformers/transformers.py @@ -6,7 +6,7 @@ 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 @@ -14,7 +14,7 @@ import sys log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") ###Pydatnic models diff --git a/src/ui/dialogs/login.py b/src/ui/dialogs/login.py index a446ec0..39b8fd1 100644 --- a/src/ui/dialogs/login.py +++ b/src/ui/dialogs/login.py @@ -1,20 +1,19 @@ import hashlib +import sys +import loguru from PyQt6 import QtCore, QtWidgets - +from src import LOG_DIR, Icon from src.backend.admin_console import AdminCommands 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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") class LoginDialog(Ui_Dialog): diff --git a/src/ui/dialogs/mail.py b/src/ui/dialogs/mail.py index 7b59e9b..c631610 100644 --- a/src/ui/dialogs/mail.py +++ b/src/ui/dialogs/mail.py @@ -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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/dialogs/parsed_titles.py b/src/ui/dialogs/parsed_titles.py index e1f2777..cf4d50f 100644 --- a/src/ui/dialogs/parsed_titles.py +++ b/src/ui/dialogs/parsed_titles.py @@ -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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") class ParsedTitles(QtWidgets.QWidget, Ui_Form): def __init__(self, parent=None): diff --git a/src/ui/dialogs/settings.py b/src/ui/dialogs/settings.py index 51ed07b..bdd4502 100644 --- a/src/ui/dialogs/settings.py +++ b/src/ui/dialogs/settings.py @@ -4,11 +4,12 @@ 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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index f79c41f..8003f86 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -1,70 +1,68 @@ # 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 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, + Apparat, # PROF_TITLES, 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, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +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", ) diff --git a/src/ui/widgets/MessageCalendar.py b/src/ui/widgets/MessageCalendar.py index 00ee0d0..47b0a62 100644 --- a/src/ui/widgets/MessageCalendar.py +++ b/src/ui/widgets/MessageCalendar.py @@ -6,11 +6,12 @@ from typing import Any import darkdetect import loguru import sys +from src import LOG_DIR log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") color = "#ddfb00" if darkdetect.isDark() else "#2204ff" diff --git a/src/ui/widgets/admin_edit_prof.py b/src/ui/widgets/admin_edit_prof.py index d4f4384..4ef209a 100644 --- a/src/ui/widgets/admin_edit_prof.py +++ b/src/ui/widgets/admin_edit_prof.py @@ -5,11 +5,11 @@ from src.logic import Prof from src.backend import Database import loguru import sys - +from src import LOG_DIR log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/widgets/elsa_main.py b/src/ui/widgets/elsa_main.py index ef3052f..1c83a6d 100644 --- a/src/ui/widgets/elsa_main.py +++ b/src/ui/widgets/elsa_main.py @@ -11,11 +11,11 @@ from src.ui.widgets import FilePicker, DataGraph from src.backend import recreateElsaFile import loguru import sys - +from src import LOG_DIR log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") diff --git a/src/ui/widgets/graph.py b/src/ui/widgets/graph.py index e17523c..93f2cde 100644 --- a/src/ui/widgets/graph.py +++ b/src/ui/widgets/graph.py @@ -5,11 +5,12 @@ import pyqtgraph as pg from PyQt6 import QtWidgets import loguru import sys +from src import LOG_DIR log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") def mergedicts(d1: dict[str, Any], d2: dict[str, Any]): diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 27b556f..3a298d9 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -6,7 +6,7 @@ from src.backend import Database, Semester from src.logic import custom_sort, Prof, sort_semesters_list from src.ui.dialogs import Mail_Dialog, ApparatExtendDialog, ReminderDialog from src.ui.widgets import DataGraph, StatusWidget - +from src import LOG_DIR from natsort import natsorted import loguru import sys @@ -14,7 +14,7 @@ import sys log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") -log.add("logs/application.log", rotation="1 MB", retention="10 days") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") From 2eceb07c0b5b907302bf2fd2daa33b698378ab19 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 3 Jun 2025 15:28:47 +0200 Subject: [PATCH 12/42] delete unneeded files --- src/logic/get_msword_content.py | 27 ---- src/logic/get_pdf_content.py | 10 -- src/logic/mail.py | 1 - src/logic/threads.py | 194 ----------------------- src/logic/threads_copy.py | 266 -------------------------------- 5 files changed, 498 deletions(-) delete mode 100644 src/logic/get_msword_content.py delete mode 100644 src/logic/get_pdf_content.py delete mode 100644 src/logic/mail.py delete mode 100644 src/logic/threads.py delete mode 100644 src/logic/threads_copy.py diff --git a/src/logic/get_msword_content.py b/src/logic/get_msword_content.py deleted file mode 100644 index 9e508cc..0000000 --- a/src/logic/get_msword_content.py +++ /dev/null @@ -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}') diff --git a/src/logic/get_pdf_content.py b/src/logic/get_pdf_content.py deleted file mode 100644 index 1b0213d..0000000 --- a/src/logic/get_pdf_content.py +++ /dev/null @@ -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() diff --git a/src/logic/mail.py b/src/logic/mail.py deleted file mode 100644 index 8b13789..0000000 --- a/src/logic/mail.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/logic/threads.py b/src/logic/threads.py deleted file mode 100644 index 76d83b7..0000000 --- a/src/logic/threads.py +++ /dev/null @@ -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("")[0] - mail_html = mail_template.split("")[1] - mail_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) diff --git a/src/logic/threads_copy.py b/src/logic/threads_copy.py deleted file mode 100644 index e1e9452..0000000 --- a/src/logic/threads_copy.py +++ /dev/null @@ -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 From dbad7165bcca43713deba91794d1204c079ba199 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Fri, 6 Jun 2025 11:14:56 +0200 Subject: [PATCH 13/42] refactor: update configuration handling and OpenAI client initialization --- .bumpversion.toml | 22 ---------------------- config/config.py | 9 ++++++++- pyproject.toml | 26 ++++++++++++++++++++++++++ src/__init__.py | 2 +- src/logic/openai.py | 28 +++++++++++++++++++--------- src/logic/zotero.py | 2 ++ src/ui/widgets/MessageCalendar.py | 12 +++++++----- src/ui/widgets/graph.py | 7 ++++--- src/ui/widgets/searchPage.py | 24 +++++++++++++----------- 9 files changed, 80 insertions(+), 52 deletions(-) delete mode 100644 .bumpversion.toml diff --git a/.bumpversion.toml b/.bumpversion.toml deleted file mode 100644 index 0f4922f..0000000 --- a/.bumpversion.toml +++ /dev/null @@ -1,22 +0,0 @@ -[tool.bumpversion] -current_version = "0.2.1" -parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\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" diff --git a/config/config.py b/config/config.py index d013607..4a4a550 100644 --- a/config/config.py +++ b/config/config.py @@ -149,7 +149,14 @@ 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 = OmegaConf.load(config_path) self.config_path = config_path diff --git a/pyproject.toml b/pyproject.toml index 59b20d0..0ec7f41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,3 +35,29 @@ dev = [ "icecream>=2.1.4", "nuitka>=2.5.9", ] + +[tool.bumpversion] +current_version = "0.1.0" +parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" +serialize = ["{major}.{minor}.{patch}"] +search = "{current_version}" +replace = "{new_version}" +regex = false +ignore_missing_version = false +ignore_missing_files = false +tag = false +sign_tags = false +tag_name = "v{new_version}" +tag_message = "Bump version: {current_version} → {new_version}" +allow_dirty = false +commit = false +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" \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py index 32f9353..bb7639a 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -15,7 +15,7 @@ if not os.path.exists(CONFIG_DIR): os.makedirs(CONFIG_DIR) -settings = Config("config/config.yaml") +settings = Config(f"{CONFIG_DIR}/config.yaml") if not os.path.exists(settings.database.temp.expanduser()): settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True) from .utils.icon import Icon diff --git a/src/logic/openai.py b/src/logic/openai.py index 6ee11fe..4fda61d 100644 --- a/src/logic/openai.py +++ b/src/logic/openai.py @@ -1,28 +1,37 @@ from openai import OpenAI from src import settings import json -from src import LOG_DIR -model = settings.openAI.model -api_key = settings.openAI.api_key -client = OpenAI(api_key = api_key) + +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. + 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}}}' + input=f'{{"string":"{title}", "needed_length":{length}}}', ) - print(length) answers = response.output_text - print(answers) 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""", @@ -33,6 +42,7 @@ def name_tester(name: str): 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.""", diff --git a/src/logic/zotero.py b/src/logic/zotero.py index dfbe905..14860e0 100644 --- a/src/logic/zotero.py +++ b/src/logic/zotero.py @@ -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, diff --git a/src/ui/widgets/MessageCalendar.py b/src/ui/widgets/MessageCalendar.py index 47b0a62..9a424cd 100644 --- a/src/ui/widgets/MessageCalendar.py +++ b/src/ui/widgets/MessageCalendar.py @@ -1,12 +1,14 @@ -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 PyQt6 import QtCore, QtWidgets +from PyQt6.QtCore import QDate +from PyQt6.QtGui import QColor, QPen + from src import LOG_DIR +from src.backend import Database log = loguru.logger log.remove() diff --git a/src/ui/widgets/graph.py b/src/ui/widgets/graph.py index 93f2cde..d480cdb 100644 --- a/src/ui/widgets/graph.py +++ b/src/ui/widgets/graph.py @@ -1,10 +1,11 @@ import random -from typing import Union, Any +import sys +from typing import Any, Union +import loguru import pyqtgraph as pg from PyQt6 import QtWidgets -import loguru -import sys + from src import LOG_DIR log = loguru.logger diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 3a298d9..448f319 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -1,16 +1,18 @@ -from .widget_sources.search_statistic_page_ui import Ui_Dialog -from PyQt6 import QtWidgets, QtGui, QtCore -from PyQt6.QtCore import pyqtSignal -from src.backend import Database, Semester - -from src.logic import custom_sort, Prof, sort_semesters_list -from src.ui.dialogs import Mail_Dialog, ApparatExtendDialog, ReminderDialog -from src.ui.widgets import DataGraph, StatusWidget -from src import LOG_DIR -from natsort import natsorted -import loguru import sys +import loguru +from natsort import natsorted +from PyQt6 import QtCore, QtGui, QtWidgets +from PyQt6.QtCore import pyqtSignal + +from src import LOG_DIR +from src.backend import Database, Semester +from src.logic import Prof, custom_sort, sort_semesters_list +from src.ui.dialogs import ApparatExtendDialog, Mail_Dialog, ReminderDialog +from src.ui.widgets import DataGraph, StatusWidget + +from .widget_sources.search_statistic_page_ui import Ui_Dialog + log = loguru.logger log.remove() log.add(sys.stdout, level="INFO") From fdab4e5caa029628af3761ffd4f8eb3b821a8375 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 10 Jun 2025 16:23:29 +0200 Subject: [PATCH 14/42] start work on welcome wizard --- config/base_config.yaml | 57 +++ config/config.py | 17 +- src/__init__.py | 6 +- src/ui/widgets/welcome_wizard.py | 51 +++ .../widgets/widget_sources/welcome_wizard.ui | 348 ++++++++++++++++++ .../widget_sources/welcome_wizard_ui.py | 215 +++++++++++ 6 files changed, 690 insertions(+), 4 deletions(-) create mode 100644 config/base_config.yaml create mode 100644 src/ui/widgets/welcome_wizard.py create mode 100644 src/ui/widgets/widget_sources/welcome_wizard.ui create mode 100644 src/ui/widgets/widget_sources/welcome_wizard_ui.py diff --git a/config/base_config.yaml b/config/base_config.yaml new file mode 100644 index 0000000..9c07673 --- /dev/null +++ b/config/base_config.yaml @@ -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 diff --git a/config/config.py b/config/config.py index 4a4a550..51ba16a 100644 --- a/config/config.py +++ b/config/config.py @@ -42,9 +42,9 @@ class Database: def __post_init__(self): if isinstance(self.path, str): - self.path = Path(self.path) + self.path = Path(self.path).expanduser() if isinstance(self.temp, str): - self.temp = Path(self.temp) + self.temp = Path(self.temp).expanduser() @dataclass class Mail: @@ -137,7 +137,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. @@ -157,9 +157,14 @@ class Config: 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. @@ -169,6 +174,12 @@ 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) diff --git a/src/__init__.py b/src/__init__.py index bb7639a..1fcb8ef 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,11 +1,14 @@ __version__ = "0.2.1" __author__ = "Alexander Kirchner" __all__ = ["__version__", "__author__", "Icon", "settings"] + import os -from config import Config from appdirs import AppDirs +from config import Config + + app = AppDirs("SemesterApparatsManager", "SAM") LOG_DIR = app.user_log_dir CONFIG_DIR = app.user_config_dir @@ -16,6 +19,7 @@ if not os.path.exists(CONFIG_DIR): settings = Config(f"{CONFIG_DIR}/config.yaml") +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 diff --git a/src/ui/widgets/welcome_wizard.py b/src/ui/widgets/welcome_wizard.py new file mode 100644 index 0000000..d79fa5a --- /dev/null +++ b/src/ui/widgets/welcome_wizard.py @@ -0,0 +1,51 @@ +from .widget_sources.welcome_wizard_ui import Ui_Wizard +from PyQt6 import QtWidgets, QtCore, QtGui +from src import settings + +class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): + def __init__(self, parent=None): + super().__init__(parent) + self.setupUi(self) + self.btn_database.clicked.connect(self.open_database_settings) + self.btn_temp.clicked.connect(self.open_temp_settings) + + def open_database_settings(self): + #open filepicker dialog to select database file folder + file_dialog = QtWidgets.QFileDialog(self, "Select Database File") + file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.ExistingFile) + file_dialog.setNameFilter("Database Files (*.db *.sqlite *.sqlite3)") + file_dialog.setViewMode(QtWidgets.QFileDialog.ViewMode.List) + file_dialog.setDirectory(settings.database.path) # Set initial directory if needed + file_dialog.setOption(QtWidgets.QFileDialog.Option.DontUseNativeDialog, True) + file_dialog.setWindowFlags(file_dialog.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint) + if file_dialog.exec(): + selected_files = file_dialog.selectedFiles() + if selected_files: + # Do something with the selected database file + print("Selected database file:", selected_files[0]) + + def open_temp_settings(self): + #open filepicker dialog to select temporary directory + dir_dialog = QtWidgets.QFileDialog(self, "Select Temporary Directory") + dir_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory) + dir_dialog.setViewMode(QtWidgets.QFileDialog.ViewMode.List) + + if dir_dialog.exec(): + selected_dirs = dir_dialog.selectedFiles() + if selected_dirs: + # Do something with the selected temporary directory + print("Selected temporary directory:", selected_dirs[0]) + + +def launch_wizard(): + """Launch the welcome wizard.""" + app = QtWidgets.QApplication.instance() + if not app: + app = QtWidgets.QApplication([]) + wizard = WelcomeWizard() + # wizard.setWindowTitle("Welcome to the Semester Apparatus Manager") + # wizard.setWindowIcon(settings.Icon("welcome").icon) + wizard.setWizardStyle(QtWidgets.QWizard.WizardStyle.ModernStyle) + wizard.setStartId(0) + wizard.show() + return wizard.exec() \ No newline at end of file diff --git a/src/ui/widgets/widget_sources/welcome_wizard.ui b/src/ui/widgets/widget_sources/welcome_wizard.ui new file mode 100644 index 0000000..b2bcba6 --- /dev/null +++ b/src/ui/widgets/widget_sources/welcome_wizard.ui @@ -0,0 +1,348 @@ + + + Wizard + + + + 0 + 0 + 564 + 425 + + + + Wizard + + + false + + + QWizard::ClassicStyle + + + Qt::PlainText + + + 0 + + + + Willkommen + + + + + + 0 + + + + + + false + + + 0 + + + QTextEdit::AutoAll + + + true + + + <!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=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Es wurde erkannt, dass der SemesterApparatsManager (SAM) zum ersten Mal gestartet wurde. In den Folgenden Seiten werden die grundlegenden Einstellungen festgelegt, anschließend wird SAM geöffnet. Folgende Einstellungen werden über diesen Wizard festgelegt:</p> +<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> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Datenbank</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- eMail</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Zotero Integration</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- KI Integration</p> +<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> + + + + + + + + Datenbank + + + Hier werden die Einstellungen für die Datenbank und temporären Dateien festgelegt. Über den [...] Knopf können die Datenbank und der Speicherort für die temporären Daten festgelegt werden + + + 1 + + + + + + Datenbank + + + + + + + semesterapparate.db + + + + + + + ... + + + + + + + Temporäre Daten + + + + + + + C:\Users\[Nutzer]\AppData\Local\SAM\SemesterApparatsManager\Cache + + + + + + + ... + + + + + + + + Mail + + + Hier werden die Einstellungen für die Mailverbindung eingegeben + + + 2 + + + + + + SMTP Server + + + + + + + + + + + + + + + + + + + Nutzername + + + + + + + SMTP Port + + + + + + + Passwort + + + + + + + CheckBox + + + + + + + + + + Druckermail + + + + + + + Nutzername +für SMTP + + + + + + + + + + Signatur + + + + + + + Mail Adresse + + + + + + + + + + + Zotero + + + Hier werden die Einstellungen für Zotero verwaltet + + + 3 + + + + + + API-Key + + + + + + + + + + + + + ID + + + + + + + Anwendungstyp + + + + + + + user + + + + + + + + KI-Integration + + + Hier werden die Einstellungen für die KI Integration eingestellt. + + + 9 + + + + + + + + + API Key + + + + + + + ChatGPT Modell + + + + + + + gpt-4o-mini + + + + gpt3.5-turbo + + + + + gpt-4 + + + + + gpt-4o + + + + + gpt-4o-mini + + + + + gpt-4.1 + + + + + gpt-4.1-mini + + + + + gpt-4.1-nano + + + + + + + + + + diff --git a/src/ui/widgets/widget_sources/welcome_wizard_ui.py b/src/ui/widgets/widget_sources/welcome_wizard_ui.py new file mode 100644 index 0000000..3433e85 --- /dev/null +++ b/src/ui/widgets/widget_sources/welcome_wizard_ui.py @@ -0,0 +1,215 @@ +# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\welcome_wizard.ui' +# +# Created by: PyQt6 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 + + +class Ui_Wizard(object): + def setupUi(self, Wizard): + Wizard.setObjectName("Wizard") + Wizard.resize(564, 425) + Wizard.setSizeGripEnabled(False) + Wizard.setWizardStyle(QtWidgets.QWizard.WizardStyle.ClassicStyle) + Wizard.setSubTitleFormat(QtCore.Qt.TextFormat.PlainText) + Wizard.setCurrentId(0) + self.wizardPage1 = QtWidgets.QWizardPage() + self.wizardPage1.setSubTitle("") + self.wizardPage1.setObjectName("wizardPage1") + self.verticalLayout = QtWidgets.QVBoxLayout(self.wizardPage1) + self.verticalLayout.setObjectName("verticalLayout") + self.textEdit = QtWidgets.QTextEdit(parent=self.wizardPage1) + self.textEdit.setEnabled(False) + self.textEdit.setLineWidth(0) + self.textEdit.setAutoFormatting(QtWidgets.QTextEdit.AutoFormattingFlag.AutoAll) + self.textEdit.setReadOnly(True) + self.textEdit.setObjectName("textEdit") + self.verticalLayout.addWidget(self.textEdit) + Wizard.addPage(self.wizardPage1) + self.wizardPage2 = QtWidgets.QWizardPage() + self.wizardPage2.setObjectName("wizardPage2") + self.gridLayout = QtWidgets.QGridLayout(self.wizardPage2) + self.gridLayout.setObjectName("gridLayout") + self.label = QtWidgets.QLabel(parent=self.wizardPage2) + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.settings_database = QtWidgets.QLineEdit(parent=self.wizardPage2) + self.settings_database.setObjectName("settings_database") + self.gridLayout.addWidget(self.settings_database, 0, 1, 1, 1) + self.btn_database = QtWidgets.QToolButton(parent=self.wizardPage2) + self.btn_database.setObjectName("btn_database") + self.gridLayout.addWidget(self.btn_database, 0, 2, 1, 1) + self.label_2 = QtWidgets.QLabel(parent=self.wizardPage2) + self.label_2.setObjectName("label_2") + self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) + self.settings_temp = QtWidgets.QLineEdit(parent=self.wizardPage2) + self.settings_temp.setObjectName("settings_temp") + self.gridLayout.addWidget(self.settings_temp, 1, 1, 1, 1) + self.btn_temp = QtWidgets.QToolButton(parent=self.wizardPage2) + self.btn_temp.setObjectName("btn_temp") + self.gridLayout.addWidget(self.btn_temp, 1, 2, 1, 1) + Wizard.addPage(self.wizardPage2) + self.wizardPage3 = QtWidgets.QWizardPage() + self.wizardPage3.setObjectName("wizardPage3") + self.formLayout = QtWidgets.QFormLayout(self.wizardPage3) + self.formLayout.setObjectName("formLayout") + self.label_3 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_3.setObjectName("label_3") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_3) + self.lineEdit = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.lineEdit.setObjectName("lineEdit") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit) + self.lineEdit_2 = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.lineEdit_2.setObjectName("lineEdit_2") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_2) + self.lineEdit_3 = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.lineEdit_3.setObjectName("lineEdit_3") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_3) + self.lineEdit_4 = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.lineEdit_4.setObjectName("lineEdit_4") + self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_4) + self.label_4 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_4.setObjectName("label_4") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_4) + self.label_5 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_5.setObjectName("label_5") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_5) + self.label_6 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_6.setObjectName("label_6") + self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_6) + self.settings_mail_use_user_name = QtWidgets.QCheckBox(parent=self.wizardPage3) + self.settings_mail_use_user_name.setObjectName("settings_mail_use_user_name") + self.formLayout.setWidget(8, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_use_user_name) + self.settings_mail_ = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.settings_mail_.setObjectName("settings_mail_") + self.formLayout.setWidget(7, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_) + self.label_8 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_8.setObjectName("label_8") + self.formLayout.setWidget(7, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_8) + self.label_9 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_9.setObjectName("label_9") + self.formLayout.setWidget(8, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_9) + self.textEdit_2 = QtWidgets.QTextEdit(parent=self.wizardPage3) + self.textEdit_2.setObjectName("textEdit_2") + self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textEdit_2) + self.label_10 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_10.setObjectName("label_10") + self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_10) + self.label_7 = QtWidgets.QLabel(parent=self.wizardPage3) + self.label_7.setObjectName("label_7") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_7) + self.lineEdit_5 = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.lineEdit_5.setObjectName("lineEdit_5") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_5) + Wizard.addPage(self.wizardPage3) + self.wizardPage4 = QtWidgets.QWizardPage() + self.wizardPage4.setObjectName("wizardPage4") + self.formLayout_2 = QtWidgets.QFormLayout(self.wizardPage4) + self.formLayout_2.setObjectName("formLayout_2") + self.label_11 = QtWidgets.QLabel(parent=self.wizardPage4) + self.label_11.setObjectName("label_11") + self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_11) + self.settings_zotero_api_key = QtWidgets.QLineEdit(parent=self.wizardPage4) + self.settings_zotero_api_key.setObjectName("settings_zotero_api_key") + self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_zotero_api_key) + self.settings_zotero_library_id = QtWidgets.QLineEdit(parent=self.wizardPage4) + self.settings_zotero_library_id.setObjectName("settings_zotero_library_id") + self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_zotero_library_id) + self.label_12 = QtWidgets.QLabel(parent=self.wizardPage4) + self.label_12.setObjectName("label_12") + self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_12) + self.label_13 = QtWidgets.QLabel(parent=self.wizardPage4) + self.label_13.setObjectName("label_13") + self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_13) + self.settings_zotero_library_type = QtWidgets.QLineEdit(parent=self.wizardPage4) + self.settings_zotero_library_type.setObjectName("settings_zotero_library_type") + self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_zotero_library_type) + Wizard.addPage(self.wizardPage4) + self.wizardPage5 = QtWidgets.QWizardPage() + self.wizardPage5.setObjectName("wizardPage5") + self.formLayout_3 = QtWidgets.QFormLayout(self.wizardPage5) + self.formLayout_3.setObjectName("formLayout_3") + self.lineEdit_6 = QtWidgets.QLineEdit(parent=self.wizardPage5) + self.lineEdit_6.setObjectName("lineEdit_6") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_6) + self.label_14 = QtWidgets.QLabel(parent=self.wizardPage5) + self.label_14.setObjectName("label_14") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_14) + self.label_15 = QtWidgets.QLabel(parent=self.wizardPage5) + self.label_15.setObjectName("label_15") + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_15) + self.comboBox = QtWidgets.QComboBox(parent=self.wizardPage5) + self.comboBox.setObjectName("comboBox") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.comboBox) + Wizard.addPage(self.wizardPage5) + + self.retranslateUi(Wizard) + QtCore.QMetaObject.connectSlotsByName(Wizard) + + def retranslateUi(self, Wizard): + _translate = QtCore.QCoreApplication.translate + Wizard.setWindowTitle(_translate("Wizard", "Wizard")) + self.wizardPage1.setTitle(_translate("Wizard", "Willkommen")) + self.textEdit.setHtml(_translate("Wizard", "\n" +"\n" +"

Es wurde erkannt, dass der SemesterApparatsManager (SAM) zum ersten Mal gestartet wurde. In den Folgenden Seiten werden die grundlegenden Einstellungen festgelegt, anschließend wird SAM geöffnet. Folgende Einstellungen werden über diesen Wizard festgelegt:

\n" +"


\n" +"

- Datenbank

\n" +"

- eMail

\n" +"

- Zotero Integration

\n" +"

- KI Integration

\n" +"


")) + self.wizardPage2.setTitle(_translate("Wizard", "Datenbank")) + self.wizardPage2.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die Datenbank und temporären Dateien festgelegt. Über den [...] Knopf können die Datenbank und der Speicherort für die temporären Daten festgelegt werden")) + self.label.setText(_translate("Wizard", "Datenbank")) + self.settings_database.setPlaceholderText(_translate("Wizard", "semesterapparate.db")) + self.btn_database.setText(_translate("Wizard", "...")) + self.label_2.setText(_translate("Wizard", "Temporäre Daten")) + self.settings_temp.setPlaceholderText(_translate("Wizard", "C:\\Users\\[Nutzer]\\AppData\\Local\\SAM\\SemesterApparatsManager\\Cache")) + self.btn_temp.setText(_translate("Wizard", "...")) + self.wizardPage3.setTitle(_translate("Wizard", "Mail")) + self.wizardPage3.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die Mailverbindung eingegeben")) + self.label_3.setText(_translate("Wizard", "SMTP Server")) + self.label_4.setText(_translate("Wizard", "Nutzername")) + self.label_5.setText(_translate("Wizard", "SMTP Port")) + self.label_6.setText(_translate("Wizard", "Passwort")) + self.settings_mail_use_user_name.setText(_translate("Wizard", "CheckBox")) + self.label_8.setText(_translate("Wizard", "Druckermail")) + self.label_9.setText(_translate("Wizard", "Nutzername\n" +"für SMTP")) + self.label_10.setText(_translate("Wizard", "Signatur")) + self.label_7.setText(_translate("Wizard", "Mail Adresse")) + self.wizardPage4.setTitle(_translate("Wizard", "Zotero")) + self.wizardPage4.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für Zotero verwaltet")) + self.label_11.setText(_translate("Wizard", "API-Key")) + self.label_12.setText(_translate("Wizard", "ID")) + self.label_13.setText(_translate("Wizard", "Anwendungstyp")) + self.settings_zotero_library_type.setText(_translate("Wizard", "user")) + self.wizardPage5.setTitle(_translate("Wizard", "KI-Integration")) + self.wizardPage5.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die KI Integration eingestellt.")) + self.label_14.setText(_translate("Wizard", "API Key")) + self.label_15.setText(_translate("Wizard", "ChatGPT Modell")) + self.comboBox.setCurrentText(_translate("Wizard", "gpt-4o-mini")) + self.comboBox.setItemText(0, _translate("Wizard", "gpt3.5-turbo")) + self.comboBox.setItemText(1, _translate("Wizard", "gpt-4")) + self.comboBox.setItemText(2, _translate("Wizard", "gpt-4o")) + self.comboBox.setItemText(3, _translate("Wizard", "gpt-4o-mini")) + self.comboBox.setItemText(4, _translate("Wizard", "gpt-4.1")) + self.comboBox.setItemText(5, _translate("Wizard", "gpt-4.1-mini")) + self.comboBox.setItemText(6, _translate("Wizard", "gpt-4.1-nano")) From c3d9daa1b0e99a5cfef09b0b556e2769d677b24f Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 17 Jun 2025 16:13:20 +0200 Subject: [PATCH 15/42] replace datagraph with dataqtgraph implementation to fix a scrolling bug --- src/ui/widgets/graph.py | 110 +++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/src/ui/widgets/graph.py b/src/ui/widgets/graph.py index d480cdb..013916b 100644 --- a/src/ui/widgets/graph.py +++ b/src/ui/widgets/graph.py @@ -2,9 +2,12 @@ import random import sys from typing import Any, Union + import loguru -import pyqtgraph as pg -from PyQt6 import QtWidgets +from PySide6 import QtWidgets, QtCore, QtGui +from PySide6.QtCore import Qt +from PySide6.QtGui import QPainter, QPen, QColor +from PySide6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis, QCategoryAxis from src import LOG_DIR @@ -30,21 +33,24 @@ def mergedicts(d1: dict[str, Any], d2: dict[str, Any]): res.update(d2_dict) # type: ignore return res - -class DataGraph(QtWidgets.QWidget): +class DataQtGraph(QtWidgets.QWidget): def __init__( self, title: str, - data=Union[dict[list, list], dict[list[dict[str, list[Any]]]]], - generateMissing: bool = False, - label: str = None, + data: dict, + generateMissing: bool, + y_label: str, + x_rotation: int = 90, ): super().__init__() - log.debug( - "Initialized with options: {}, {}, {}, {}".format( - title, data, generateMissing, label - ) - ) + self.series = QLineSeries() + self.chart = QChart() + # scale the chart to fit the data + self.chart.setTitle(title) + self.chart.legend().setVisible(True) + + layout = QtWidgets.QVBoxLayout() + lst = [] if generateMissing: x_data = data["x"] @@ -71,54 +77,44 @@ class DataGraph(QtWidgets.QWidget): lst.append(data) x_data = lst[0]["x"] # xdict = dict(enumerate(x_data)) - stringaxis_x = pg.AxisItem(orientation="bottom") - stringaxis_x.setTicks([xdict.items()]) - graph = pg.PlotWidget(axisItems={"bottom": stringaxis_x}) - graph.addLegend() - colors = ["b", "r", "c", "m", "y", "k", "w"] - symbols = [ - "o", - "s", - "t", - "d", - "+", - "t1", - "t2", - "t3", - "p", - "h", - "star", - "x", - "arrow_up", - "arrow_down", - "arrow_left", - "arrow_right", - ] - color_index = 0 - index = 0 + print("xdict:", xdict) - for data in lst: - symbol = symbols[random.randint(0, len(symbols) - 1)] - if color_index >= len(colors): - color_index = 0 - # iterate over the list, use y-data and y-label to plot the graph - y_data = data["y"] - label = data["y-label"] if "y-label" in data else label + self.chart.createDefaultAxes() + for entry in lst: + print("entry:", entry) + entryseries = QLineSeries() + for x_val, y_val in zip(entry["x"], entry["y"]): + # + entryseries.append(entry["x"].index(x_val), y_val) + entryseries.setName(entry["y-label"] if "y-label" in entry else y_label) - pen = pg.mkPen(color=colors[color_index], width=2) - if isinstance(y_data, list): - graph.plot( - list(xdict.keys()), y_data, pen=pen, symbol=symbol, name=label - ) - color_index += 1 - index += 1 - else: - pass - graph.setBackground("#d3d3d3") - graph.setTitle(title) - layout = QtWidgets.QVBoxLayout() - layout.addWidget(graph) + self.chart.addSeries(entryseries) + + x_axis = QCategoryAxis() + for index, semester in enumerate(lst[0]["x"]): + x_axis.append(semester, index) + x_axis.setLabelsPosition( + QCategoryAxis.AxisLabelsPosition.AxisLabelsPositionOnValue + ) + # rotare the label by 45 degrees + x_axis.setLabelsAngle(x_rotation) + x_axis.setLabelsFont(QtGui.QFont("Arial", 8, QtGui.QFont.Weight.Normal, False)) + self.chart.setAxisX(x_axis, entryseries) + + # entryseries.append( + # str() + # ) + self.chart.legend().setVisible(True) + self.chart.legend().setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom) + # set legend labels + + self.chart.setAxisY(QValueAxis(self.chart), entryseries) + # the chart's x axis labels are not being displayed, as the chart only has limited space. scale down the x axis font + + chartview = QChartView(self.chart) + chartview.setRenderHint(QPainter.RenderHint.Antialiasing) + layout.addWidget(chartview) self.setLayout(layout) def generateMissingSemesters(self, data: dict[list]): From 7eb55c21d00f1f80a66658c45098889bd4a00725 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 17 Jun 2025 16:21:56 +0200 Subject: [PATCH 16/42] migrate from PyQt6 to PySide6, remove unneeded dependencies --- config/config.py | 4 +- pyproject.toml | 5 +- src/backend/admin_console.py | 13 + src/backend/database.py | 1 + src/backend/documentation_thread.py | 2 +- src/backend/thread_bookgrabber.py | 4 +- src/backend/threads_autoadder.py | 4 +- src/backend/threads_availchecker.py | 4 +- src/ui/dialogs/Ui_edit_bookdata.py | 4 +- src/ui/dialogs/Ui_fileparser.py | 6 +- src/ui/dialogs/Ui_login.py | 4 +- src/ui/dialogs/Ui_mail_preview.py | 4 +- src/ui/dialogs/Ui_medianadder.py | 4 +- src/ui/dialogs/Ui_new_subject.py | 4 +- src/ui/dialogs/Ui_parsed_titles.py | 4 +- src/ui/dialogs/Ui_reminder.py | 4 +- src/ui/dialogs/Ui_settings.py | 4 +- src/ui/dialogs/about.py | 6 +- src/ui/dialogs/app_ext.py | 2 +- src/ui/dialogs/bookdata.py | 2 +- src/ui/dialogs/confirm_extend.py | 2 +- src/ui/dialogs/confirm_extend_ui.py | 4 +- src/ui/dialogs/dialog_sources/Ui_about.py | 4 +- .../dialogs/dialog_sources/Ui_app_status.py | 4 +- .../dialog_sources/Ui_apparat_extend.py | 4 +- .../dialog_sources/Ui_confirm_extend.py | 4 +- .../dialog_sources/Ui_edit_bookdata.py | 4 +- .../dialog_sources/Ui_elsa_add_table_entry.py | 4 +- .../Ui_elsa_generate_citation.py | 4 +- .../Ui_elsa_generator_confirm.py | 4 +- src/ui/dialogs/dialog_sources/Ui_login.py | 4 +- .../dialogs/dialog_sources/Ui_mail_preview.py | 4 +- .../dialogs/dialog_sources/Ui_medianadder.py | 4 +- .../Ui_newMailTemplateDesigner.py | 4 +- .../dialog_sources/Ui_parsed_titles.py | 4 +- src/ui/dialogs/dialog_sources/Ui_reminder.py | 4 +- src/ui/dialogs/dialog_sources/Ui_settings.py | 4 +- src/ui/dialogs/dialog_sources/about_ui.py | 4 +- .../dialogs/dialog_sources/app_status_ui.py | 4 +- .../dialog_sources/apparat_extend_ui.py | 4 +- .../dialog_sources/confirm_extend_ui.py | 4 +- .../dialog_sources/documentprint_ui.py | 4 +- .../dialog_sources/edit_bookdata_ui.py | 4 +- .../dialog_sources/elsa_add_table_entry_ui.py | 4 +- .../elsa_generate_citation_ui.py | 4 +- .../elsa_generator_confirm_ui.py | 4 +- src/ui/dialogs/dialog_sources/login_ui.py | 4 +- .../dialogs/dialog_sources/mail_preview_ui.py | 4 +- .../dialogs/dialog_sources/medianadder_ui.py | 4 +- .../newMailTemplateDesigner_ui.py | 4 +- .../dialog_sources/parsed_titles_ui.py | 4 +- src/ui/dialogs/dialog_sources/reminder_ui.py | 4 +- src/ui/dialogs/dialog_sources/settings_ui.py | 4 +- src/ui/dialogs/docuprint.py | 2 +- src/ui/dialogs/elsa_add_entry.py | 2 +- src/ui/dialogs/elsa_citation.py | 2 +- src/ui/dialogs/elsa_gen_confirm.py | 2 +- src/ui/dialogs/ext_app.py | 4 +- src/ui/dialogs/fileparser.py | 2 +- src/ui/dialogs/login.py | 6 +- src/ui/dialogs/mail.py | 2 +- src/ui/dialogs/mailTemplate.py | 4 +- src/ui/dialogs/mail_preview.py | 4 +- src/ui/dialogs/mail_preview_ui.py | 4 +- src/ui/dialogs/medienadder.py | 2 +- src/ui/dialogs/parsed_titles.py | 2 +- src/ui/dialogs/parsed_titles_ui.py | 4 +- src/ui/dialogs/popup_confirm.py | 4 +- src/ui/dialogs/reminder.py | 2 +- src/ui/dialogs/settings.py | 2 +- src/ui/extensions/ValidatorButton.py | 2 +- src/ui/semesterapparat_ui_ui.py | 4 +- src/ui/sounds/semesterapparat_ui_ui.py | 4 +- src/ui/userInterface.py | 6 +- src/ui/widgets/MessageCalendar.py | 6 +- src/ui/widgets/__init__.py | 2 +- src/ui/widgets/admin_create_user.py | 6 +- src/ui/widgets/admin_edit_prof.py | 2 +- src/ui/widgets/admin_edit_user.py | 2 +- src/ui/widgets/admin_query.py | 2 +- src/ui/widgets/calendar_entry.py | 10 +- src/ui/widgets/collapse.py | 4 +- src/ui/widgets/elsa_main.py | 14 +- src/ui/widgets/filepicker.py | 4 +- src/ui/widgets/iconLine.py | 2 +- src/ui/widgets/searchPage.py | 21 +- src/ui/widgets/welcome_wizard.py | 145 +++++++- .../widget_sources/admin_create_user_ui.py | 4 +- .../widget_sources/admin_edit_prof_ui.py | 4 +- .../widget_sources/admin_edit_user_ui.py | 4 +- .../widget_sources/calendar_entry_ui.py | 4 +- .../widget_sources/elsa_maindialog_ui.py | 4 +- .../widgets/widget_sources/icon_widget_ui.py | 4 +- .../search_statistic_page_ui.py | 4 +- .../widgets/widget_sources/welcome_wizard.ui | 201 +++++++++-- .../widget_sources/welcome_wizard_ui.py | 278 ++++++++++---- src/utils/Ui_docs.py | 4 +- src/utils/docs_ui.py | 4 +- src/utils/documentationview.py | 4 +- src/utils/icon.py | 2 +- uv.lock | 340 +++++++++--------- 101 files changed, 898 insertions(+), 464 deletions(-) diff --git a/config/config.py b/config/config.py index 51ba16a..5f1324e 100644 --- a/config/config.py +++ b/config/config.py @@ -33,7 +33,6 @@ class Database: name: str path: str | Path temp: str | Path - def getattr(self, name: str): return getattr(self, name) @@ -224,6 +223,9 @@ class Config: def set_zotero_attr(self, name: str, value: Any): OmegaConf.update(self._config, f"zotero.{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) diff --git a/pyproject.toml b/pyproject.toml index 0ec7f41..2a03c91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,8 +21,7 @@ dependencies = [ "openai>=1.79.0", "pandas>=2.2.3", "playwright>=1.49.1", - "pyqt6>=6.8.0", - "pyqtgraph>=0.13.7", + "pyside6>=6.9.1", "python-docx>=1.1.2", "pyzotero>=1.6.4", "ratelimit>=2.2.1", @@ -60,4 +59,4 @@ post_commit_hooks = [] [[tool.bumpversion.files]] filename = "src/__init__.py" [[tool.bumpversion.files]] -filename = ".version" \ No newline at end of file +filename = ".version" diff --git a/src/backend/admin_console.py b/src/backend/admin_console.py index fee467b..c65bfa8 100644 --- a/src/backend/admin_console.py +++ b/src/backend/admin_console.py @@ -44,6 +44,19 @@ 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"): + """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) + self.db.createUser( + user=username, password=salt + hashed_password, role=role, salt=salt + ) + def hash_password(self, password: str) -> str: """Hash a password using SHA256. diff --git a/src/backend/database.py b/src/backend/database.py index 90f765d..2baecee 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -74,6 +74,7 @@ class Database: 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() diff --git a/src/backend/documentation_thread.py b/src/backend/documentation_thread.py index 14d9e15..e411ec3 100644 --- a/src/backend/documentation_thread.py +++ b/src/backend/documentation_thread.py @@ -1,4 +1,4 @@ -from PyQt6.QtCore import QThread +from PySide6.QtCore import QThread from src.utils.documentation import run_mkdocs diff --git a/src/backend/thread_bookgrabber.py b/src/backend/thread_bookgrabber.py index 0db468e..02fef02 100644 --- a/src/backend/thread_bookgrabber.py +++ b/src/backend/thread_bookgrabber.py @@ -1,5 +1,5 @@ -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 diff --git a/src/backend/threads_autoadder.py b/src/backend/threads_autoadder.py index 6ac53bf..b793ae6 100644 --- a/src/backend/threads_autoadder.py +++ b/src/backend/threads_autoadder.py @@ -1,8 +1,8 @@ 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 diff --git a/src/backend/threads_availchecker.py b/src/backend/threads_availchecker.py index b3b08c5..3fbf819 100644 --- a/src/backend/threads_availchecker.py +++ b/src/backend/threads_availchecker.py @@ -1,8 +1,8 @@ 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 diff --git a/src/ui/dialogs/Ui_edit_bookdata.py b/src/ui/dialogs/Ui_edit_bookdata.py index 728cd17..eba44e0 100644 --- a/src/ui/dialogs/Ui_edit_bookdata.py +++ b/src/ui/dialogs/Ui_edit_bookdata.py @@ -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 diff --git a/src/ui/dialogs/Ui_fileparser.py b/src/ui/dialogs/Ui_fileparser.py index 05459bd..da869db 100644 --- a/src/ui/dialogs/Ui_fileparser.py +++ b/src/ui/dialogs/Ui_fileparser.py @@ -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) diff --git a/src/ui/dialogs/Ui_login.py b/src/ui/dialogs/Ui_login.py index 3e570af..7e807ac 100644 --- a/src/ui/dialogs/Ui_login.py +++ b/src/ui/dialogs/Ui_login.py @@ -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 diff --git a/src/ui/dialogs/Ui_mail_preview.py b/src/ui/dialogs/Ui_mail_preview.py index 4b95e50..32ed57d 100644 --- a/src/ui/dialogs/Ui_mail_preview.py +++ b/src/ui/dialogs/Ui_mail_preview.py @@ -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 diff --git a/src/ui/dialogs/Ui_medianadder.py b/src/ui/dialogs/Ui_medianadder.py index 3578bab..a1c46b0 100644 --- a/src/ui/dialogs/Ui_medianadder.py +++ b/src/ui/dialogs/Ui_medianadder.py @@ -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): diff --git a/src/ui/dialogs/Ui_new_subject.py b/src/ui/dialogs/Ui_new_subject.py index 213eb1c..db6ebde 100644 --- a/src/ui/dialogs/Ui_new_subject.py +++ b/src/ui/dialogs/Ui_new_subject.py @@ -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): diff --git a/src/ui/dialogs/Ui_parsed_titles.py b/src/ui/dialogs/Ui_parsed_titles.py index 786c176..b05ad32 100644 --- a/src/ui/dialogs/Ui_parsed_titles.py +++ b/src/ui/dialogs/Ui_parsed_titles.py @@ -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 diff --git a/src/ui/dialogs/Ui_reminder.py b/src/ui/dialogs/Ui_reminder.py index f4dbc8f..283e509 100644 --- a/src/ui/dialogs/Ui_reminder.py +++ b/src/ui/dialogs/Ui_reminder.py @@ -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): diff --git a/src/ui/dialogs/Ui_settings.py b/src/ui/dialogs/Ui_settings.py index bc50dfe..36c39f2 100644 --- a/src/ui/dialogs/Ui_settings.py +++ b/src/ui/dialogs/Ui_settings.py @@ -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") diff --git a/src/ui/dialogs/about.py b/src/ui/dialogs/about.py index 6a6ba29..972495e 100644 --- a/src/ui/dialogs/about.py +++ b/src/ui/dialogs/about.py @@ -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) diff --git a/src/ui/dialogs/app_ext.py b/src/ui/dialogs/app_ext.py index 9cb0516..8db924f 100644 --- a/src/ui/dialogs/app_ext.py +++ b/src/ui/dialogs/app_ext.py @@ -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 diff --git a/src/ui/dialogs/bookdata.py b/src/ui/dialogs/bookdata.py index 60bc5ed..9561604 100644 --- a/src/ui/dialogs/bookdata.py +++ b/src/ui/dialogs/bookdata.py @@ -1,4 +1,4 @@ -from PyQt6 import QtWidgets +from PySide6 import QtWidgets from src.logic.dataclass import BookData diff --git a/src/ui/dialogs/confirm_extend.py b/src/ui/dialogs/confirm_extend.py index 2be36ea..3363bd5 100644 --- a/src/ui/dialogs/confirm_extend.py +++ b/src/ui/dialogs/confirm_extend.py @@ -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): diff --git a/src/ui/dialogs/confirm_extend_ui.py b/src/ui/dialogs/confirm_extend_ui.py index 5c7a0ed..cc2674e 100644 --- a/src/ui/dialogs/confirm_extend_ui.py +++ b/src/ui/dialogs/confirm_extend_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_about.py b/src/ui/dialogs/dialog_sources/Ui_about.py index e151ec1..6434828 100644 --- a/src/ui/dialogs/dialog_sources/Ui_about.py +++ b/src/ui/dialogs/dialog_sources/Ui_about.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_app_status.py b/src/ui/dialogs/dialog_sources/Ui_app_status.py index a927c1b..7dcc51b 100644 --- a/src/ui/dialogs/dialog_sources/Ui_app_status.py +++ b/src/ui/dialogs/dialog_sources/Ui_app_status.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_apparat_extend.py b/src/ui/dialogs/dialog_sources/Ui_apparat_extend.py index 62b28d7..828d1a8 100644 --- a/src/ui/dialogs/dialog_sources/Ui_apparat_extend.py +++ b/src/ui/dialogs/dialog_sources/Ui_apparat_extend.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py b/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py index 13f44e4..72ef74f 100644 --- a/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py +++ b/src/ui/dialogs/dialog_sources/Ui_confirm_extend.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_edit_bookdata.py b/src/ui/dialogs/dialog_sources/Ui_edit_bookdata.py index 97f0fd3..381cd43 100644 --- a/src/ui/dialogs/dialog_sources/Ui_edit_bookdata.py +++ b/src/ui/dialogs/dialog_sources/Ui_edit_bookdata.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_elsa_add_table_entry.py b/src/ui/dialogs/dialog_sources/Ui_elsa_add_table_entry.py index a608e77..a393508 100644 --- a/src/ui/dialogs/dialog_sources/Ui_elsa_add_table_entry.py +++ b/src/ui/dialogs/dialog_sources/Ui_elsa_add_table_entry.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_elsa_generate_citation.py b/src/ui/dialogs/dialog_sources/Ui_elsa_generate_citation.py index f3c7390..43944f4 100644 --- a/src/ui/dialogs/dialog_sources/Ui_elsa_generate_citation.py +++ b/src/ui/dialogs/dialog_sources/Ui_elsa_generate_citation.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_elsa_generator_confirm.py b/src/ui/dialogs/dialog_sources/Ui_elsa_generator_confirm.py index c9980a6..c56c6f3 100644 --- a/src/ui/dialogs/dialog_sources/Ui_elsa_generator_confirm.py +++ b/src/ui/dialogs/dialog_sources/Ui_elsa_generator_confirm.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_login.py b/src/ui/dialogs/dialog_sources/Ui_login.py index ff7dcd0..deb1a62 100644 --- a/src/ui/dialogs/dialog_sources/Ui_login.py +++ b/src/ui/dialogs/dialog_sources/Ui_login.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_mail_preview.py b/src/ui/dialogs/dialog_sources/Ui_mail_preview.py index 333103c..e852ebf 100644 --- a/src/ui/dialogs/dialog_sources/Ui_mail_preview.py +++ b/src/ui/dialogs/dialog_sources/Ui_mail_preview.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_medianadder.py b/src/ui/dialogs/dialog_sources/Ui_medianadder.py index 44a884d..db20569 100644 --- a/src/ui/dialogs/dialog_sources/Ui_medianadder.py +++ b/src/ui/dialogs/dialog_sources/Ui_medianadder.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_newMailTemplateDesigner.py b/src/ui/dialogs/dialog_sources/Ui_newMailTemplateDesigner.py index df436d4..ee2e682 100644 --- a/src/ui/dialogs/dialog_sources/Ui_newMailTemplateDesigner.py +++ b/src/ui/dialogs/dialog_sources/Ui_newMailTemplateDesigner.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_parsed_titles.py b/src/ui/dialogs/dialog_sources/Ui_parsed_titles.py index b18b94a..bf60db7 100644 --- a/src/ui/dialogs/dialog_sources/Ui_parsed_titles.py +++ b/src/ui/dialogs/dialog_sources/Ui_parsed_titles.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_reminder.py b/src/ui/dialogs/dialog_sources/Ui_reminder.py index 45cc18c..5f62089 100644 --- a/src/ui/dialogs/dialog_sources/Ui_reminder.py +++ b/src/ui/dialogs/dialog_sources/Ui_reminder.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/Ui_settings.py b/src/ui/dialogs/dialog_sources/Ui_settings.py index 8ee3c85..f6cdc11 100644 --- a/src/ui/dialogs/dialog_sources/Ui_settings.py +++ b/src/ui/dialogs/dialog_sources/Ui_settings.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/about_ui.py b/src/ui/dialogs/dialog_sources/about_ui.py index 1a7c657..4713898 100644 --- a/src/ui/dialogs/dialog_sources/about_ui.py +++ b/src/ui/dialogs/dialog_sources/about_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/app_status_ui.py b/src/ui/dialogs/dialog_sources/app_status_ui.py index 423eb50..d845f0e 100644 --- a/src/ui/dialogs/dialog_sources/app_status_ui.py +++ b/src/ui/dialogs/dialog_sources/app_status_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/apparat_extend_ui.py b/src/ui/dialogs/dialog_sources/apparat_extend_ui.py index a9a9a81..c197967 100644 --- a/src/ui/dialogs/dialog_sources/apparat_extend_ui.py +++ b/src/ui/dialogs/dialog_sources/apparat_extend_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/confirm_extend_ui.py b/src/ui/dialogs/dialog_sources/confirm_extend_ui.py index 1fe54b9..bef4a6b 100644 --- a/src/ui/dialogs/dialog_sources/confirm_extend_ui.py +++ b/src/ui/dialogs/dialog_sources/confirm_extend_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/documentprint_ui.py b/src/ui/dialogs/dialog_sources/documentprint_ui.py index 030e9c5..aa7e341 100644 --- a/src/ui/dialogs/dialog_sources/documentprint_ui.py +++ b/src/ui/dialogs/dialog_sources/documentprint_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/edit_bookdata_ui.py b/src/ui/dialogs/dialog_sources/edit_bookdata_ui.py index 89e4ce0..be8db53 100644 --- a/src/ui/dialogs/dialog_sources/edit_bookdata_ui.py +++ b/src/ui/dialogs/dialog_sources/edit_bookdata_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/elsa_add_table_entry_ui.py b/src/ui/dialogs/dialog_sources/elsa_add_table_entry_ui.py index de48dbb..3a77b77 100644 --- a/src/ui/dialogs/dialog_sources/elsa_add_table_entry_ui.py +++ b/src/ui/dialogs/dialog_sources/elsa_add_table_entry_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/elsa_generate_citation_ui.py b/src/ui/dialogs/dialog_sources/elsa_generate_citation_ui.py index dbed3f6..2315132 100644 --- a/src/ui/dialogs/dialog_sources/elsa_generate_citation_ui.py +++ b/src/ui/dialogs/dialog_sources/elsa_generate_citation_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/elsa_generator_confirm_ui.py b/src/ui/dialogs/dialog_sources/elsa_generator_confirm_ui.py index fcd4a4b..9c38f0f 100644 --- a/src/ui/dialogs/dialog_sources/elsa_generator_confirm_ui.py +++ b/src/ui/dialogs/dialog_sources/elsa_generator_confirm_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/login_ui.py b/src/ui/dialogs/dialog_sources/login_ui.py index c3ee6d5..2a2c72b 100644 --- a/src/ui/dialogs/dialog_sources/login_ui.py +++ b/src/ui/dialogs/dialog_sources/login_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/mail_preview_ui.py b/src/ui/dialogs/dialog_sources/mail_preview_ui.py index 37109b2..a7ce30d 100644 --- a/src/ui/dialogs/dialog_sources/mail_preview_ui.py +++ b/src/ui/dialogs/dialog_sources/mail_preview_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/medianadder_ui.py b/src/ui/dialogs/dialog_sources/medianadder_ui.py index adf8edd..52e90ac 100644 --- a/src/ui/dialogs/dialog_sources/medianadder_ui.py +++ b/src/ui/dialogs/dialog_sources/medianadder_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/newMailTemplateDesigner_ui.py b/src/ui/dialogs/dialog_sources/newMailTemplateDesigner_ui.py index 4ad337a..0892c34 100644 --- a/src/ui/dialogs/dialog_sources/newMailTemplateDesigner_ui.py +++ b/src/ui/dialogs/dialog_sources/newMailTemplateDesigner_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/parsed_titles_ui.py b/src/ui/dialogs/dialog_sources/parsed_titles_ui.py index c99e92d..5d5cadf 100644 --- a/src/ui/dialogs/dialog_sources/parsed_titles_ui.py +++ b/src/ui/dialogs/dialog_sources/parsed_titles_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/reminder_ui.py b/src/ui/dialogs/dialog_sources/reminder_ui.py index 6ae8f4a..25b7bf8 100644 --- a/src/ui/dialogs/dialog_sources/reminder_ui.py +++ b/src/ui/dialogs/dialog_sources/reminder_ui.py @@ -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): diff --git a/src/ui/dialogs/dialog_sources/settings_ui.py b/src/ui/dialogs/dialog_sources/settings_ui.py index 92015d7..0ac4637 100644 --- a/src/ui/dialogs/dialog_sources/settings_ui.py +++ b/src/ui/dialogs/dialog_sources/settings_ui.py @@ -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): diff --git a/src/ui/dialogs/docuprint.py b/src/ui/dialogs/docuprint.py index 4556ebd..3f87c1c 100644 --- a/src/ui/dialogs/docuprint.py +++ b/src/ui/dialogs/docuprint.py @@ -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 diff --git a/src/ui/dialogs/elsa_add_entry.py b/src/ui/dialogs/elsa_add_entry.py index 597718e..c15a0ab 100644 --- a/src/ui/dialogs/elsa_add_entry.py +++ b/src/ui/dialogs/elsa_add_entry.py @@ -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 diff --git a/src/ui/dialogs/elsa_citation.py b/src/ui/dialogs/elsa_citation.py index eae7288..8a7c2e0 100644 --- a/src/ui/dialogs/elsa_citation.py +++ b/src/ui/dialogs/elsa_citation.py @@ -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): diff --git a/src/ui/dialogs/elsa_gen_confirm.py b/src/ui/dialogs/elsa_gen_confirm.py index b56a20a..8b15842 100644 --- a/src/ui/dialogs/elsa_gen_confirm.py +++ b/src/ui/dialogs/elsa_gen_confirm.py @@ -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): diff --git a/src/ui/dialogs/ext_app.py b/src/ui/dialogs/ext_app.py index 5ff330d..425067e 100644 --- a/src/ui/dialogs/ext_app.py +++ b/src/ui/dialogs/ext_app.py @@ -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): diff --git a/src/ui/dialogs/fileparser.py b/src/ui/dialogs/fileparser.py index c049429..c0c29cd 100644 --- a/src/ui/dialogs/fileparser.py +++ b/src/ui/dialogs/fileparser.py @@ -1,4 +1,4 @@ -from PyQt6 import QtWidgets +from PySide6 import QtWidgets from src.logic.webrequest import BibTextTransformer, WebRequest diff --git a/src/ui/dialogs/login.py b/src/ui/dialogs/login.py index 39b8fd1..b7b820b 100644 --- a/src/ui/dialogs/login.py +++ b/src/ui/dialogs/login.py @@ -2,7 +2,7 @@ import hashlib import sys import loguru -from PyQt6 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from src import LOG_DIR, Icon from src.backend.admin_console import AdminCommands @@ -88,14 +88,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( diff --git a/src/ui/dialogs/mail.py b/src/ui/dialogs/mail.py index c631610..833233a 100644 --- a/src/ui/dialogs/mail.py +++ b/src/ui/dialogs/mail.py @@ -1,7 +1,7 @@ import os import sys -from PyQt6 import QtWidgets +from PySide6 import QtWidgets from src import Icon, settings as config diff --git a/src/ui/dialogs/mailTemplate.py b/src/ui/dialogs/mailTemplate.py index 9c55b7c..c8aae04 100644 --- a/src/ui/dialogs/mailTemplate.py +++ b/src/ui/dialogs/mailTemplate.py @@ -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) diff --git a/src/ui/dialogs/mail_preview.py b/src/ui/dialogs/mail_preview.py index e4a6560..7d8dd36 100644 --- a/src/ui/dialogs/mail_preview.py +++ b/src/ui/dialogs/mail_preview.py @@ -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): diff --git a/src/ui/dialogs/mail_preview_ui.py b/src/ui/dialogs/mail_preview_ui.py index 473a6de..da6b448 100644 --- a/src/ui/dialogs/mail_preview_ui.py +++ b/src/ui/dialogs/mail_preview_ui.py @@ -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): diff --git a/src/ui/dialogs/medienadder.py b/src/ui/dialogs/medienadder.py index 1f00c71..ecdeac6 100644 --- a/src/ui/dialogs/medienadder.py +++ b/src/ui/dialogs/medienadder.py @@ -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 diff --git a/src/ui/dialogs/parsed_titles.py b/src/ui/dialogs/parsed_titles.py index cf4d50f..d5ba73b 100644 --- a/src/ui/dialogs/parsed_titles.py +++ b/src/ui/dialogs/parsed_titles.py @@ -1,4 +1,4 @@ -from PyQt6 import QtWidgets +from PySide6 import QtWidgets from src.backend import AutoAdder diff --git a/src/ui/dialogs/parsed_titles_ui.py b/src/ui/dialogs/parsed_titles_ui.py index 7f643c8..59f38d2 100644 --- a/src/ui/dialogs/parsed_titles_ui.py +++ b/src/ui/dialogs/parsed_titles_ui.py @@ -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): diff --git a/src/ui/dialogs/popup_confirm.py b/src/ui/dialogs/popup_confirm.py index 6f47a8f..8b7f19c 100644 --- a/src/ui/dialogs/popup_confirm.py +++ b/src/ui/dialogs/popup_confirm.py @@ -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 diff --git a/src/ui/dialogs/reminder.py b/src/ui/dialogs/reminder.py index 54ec66e..f92e8b3 100644 --- a/src/ui/dialogs/reminder.py +++ b/src/ui/dialogs/reminder.py @@ -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 diff --git a/src/ui/dialogs/settings.py b/src/ui/dialogs/settings.py index bdd4502..df918d0 100644 --- a/src/ui/dialogs/settings.py +++ b/src/ui/dialogs/settings.py @@ -1,4 +1,4 @@ -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 diff --git a/src/ui/extensions/ValidatorButton.py b/src/ui/extensions/ValidatorButton.py index 85fa7b8..bbdecb5 100644 --- a/src/ui/extensions/ValidatorButton.py +++ b/src/ui/extensions/ValidatorButton.py @@ -1,4 +1,4 @@ -from PyQt6 import QtWidgets +from PySide6 import QtWidgets class ValidatorButton(QtWidgets.QToolButton): diff --git a/src/ui/semesterapparat_ui_ui.py b/src/ui/semesterapparat_ui_ui.py index 4c0af77..077fb02 100644 --- a/src/ui/semesterapparat_ui_ui.py +++ b/src/ui/semesterapparat_ui_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\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): diff --git a/src/ui/sounds/semesterapparat_ui_ui.py b/src/ui/sounds/semesterapparat_ui_ui.py index 657741f..302f6f3 100644 --- a/src/ui/sounds/semesterapparat_ui_ui.py +++ b/src/ui/sounds/semesterapparat_ui_ui.py @@ -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): diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index 8003f86..b6b8fc1 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -11,9 +11,9 @@ 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 +from PySide6.QtGui import QRegularExpressionValidator from src import LOG_DIR, Icon from src.backend import AvailChecker, BookGrabber, Database, DocumentationThread diff --git a/src/ui/widgets/MessageCalendar.py b/src/ui/widgets/MessageCalendar.py index 9a424cd..f14fcbe 100644 --- a/src/ui/widgets/MessageCalendar.py +++ b/src/ui/widgets/MessageCalendar.py @@ -3,9 +3,9 @@ from typing import Any import darkdetect import loguru -from PyQt6 import QtCore, QtWidgets -from PyQt6.QtCore import QDate -from PyQt6.QtGui import QColor, QPen +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 diff --git a/src/ui/widgets/__init__.py b/src/ui/widgets/__init__.py index 3eb366b..43b611a 100644 --- a/src/ui/widgets/__init__.py +++ b/src/ui/widgets/__init__.py @@ -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 diff --git a/src/ui/widgets/admin_create_user.py b/src/ui/widgets/admin_create_user.py index 2be5e2a..d9a0e68 100644 --- a/src/ui/widgets/admin_create_user.py +++ b/src/ui/widgets/admin_create_user.py @@ -1,11 +1,11 @@ -from PyQt6 import QtWidgets -from PyQt6.QtCore import pyqtSignal +from PySide6 import QtWidgets +from PySide6.QtCore import Signal from .widget_sources.admin_create_user_ui import Ui_Dialog from src.backend import AdminCommands, Database class UserCreate(QtWidgets.QDialog, Ui_Dialog): - admin_action_changed = pyqtSignal() + admin_action_changed = Signal() def __init__(self): super(UserCreate, self).__init__() diff --git a/src/ui/widgets/admin_edit_prof.py b/src/ui/widgets/admin_edit_prof.py index 4ef209a..0e327c3 100644 --- a/src/ui/widgets/admin_edit_prof.py +++ b/src/ui/widgets/admin_edit_prof.py @@ -1,5 +1,5 @@ from .widget_sources.admin_edit_prof_ui import Ui_Dialog # -from PyQt6 import QtWidgets +from PySide6 import QtWidgets from src.logic import Prof from src.backend import Database diff --git a/src/ui/widgets/admin_edit_user.py b/src/ui/widgets/admin_edit_user.py index 7ce5914..6768dcb 100644 --- a/src/ui/widgets/admin_edit_user.py +++ b/src/ui/widgets/admin_edit_user.py @@ -1,5 +1,5 @@ from .widget_sources.admin_edit_user_ui import Ui_Dialog -from PyQt6 import QtWidgets +from PySide6 import QtWidgets from src.backend import Database from src.backend import AdminCommands diff --git a/src/ui/widgets/admin_query.py b/src/ui/widgets/admin_query.py index 46389ee..5c933f4 100644 --- a/src/ui/widgets/admin_query.py +++ b/src/ui/widgets/admin_query.py @@ -1,6 +1,6 @@ from .widget_sources.admin_query_ui import Ui_Form -from PyQt6 import QtWidgets, QtCore +from PySide6 import QtWidgets, QtCore from src import Icon from src.backend import Database diff --git a/src/ui/widgets/calendar_entry.py b/src/ui/widgets/calendar_entry.py index 1c70b94..566bd6f 100644 --- a/src/ui/widgets/calendar_entry.py +++ b/src/ui/widgets/calendar_entry.py @@ -1,15 +1,13 @@ from .widget_sources.calendar_entry_ui import Ui_Dialog -from PyQt6 import QtWidgets -from PyQt6.QtCore import pyqtSignal +from PySide6 import QtWidgets +from PySide6.QtCore import Signal from src.backend.database import Database from src import Icon class CalendarEntry(QtWidgets.QDialog, Ui_Dialog): - deleteSignal = pyqtSignal( - int - ) # when emit, send the id of the message to be deleted - repaintSignal = pyqtSignal(str) + deleteSignal = Signal(int) # when emit, send the id of the message to be deleted + repaintSignal = Signal(str) def __init__(self, parent=None, messages=None, date=None): super().__init__(parent) diff --git a/src/ui/widgets/collapse.py b/src/ui/widgets/collapse.py index cd5ceae..e6a1ca7 100644 --- a/src/ui/widgets/collapse.py +++ b/src/ui/widgets/collapse.py @@ -1,6 +1,6 @@ # import pysignal pyslot -from PyQt6.QtCore import pyqtSignal as Signal -from PyQt6.QtWidgets import ( +from PySide6.QtCore import Signal as Signal +from PySide6.QtWidgets import ( QApplication, QTreeWidget, QTreeWidgetItem, diff --git a/src/ui/widgets/elsa_main.py b/src/ui/widgets/elsa_main.py index 1c83a6d..ccad176 100644 --- a/src/ui/widgets/elsa_main.py +++ b/src/ui/widgets/elsa_main.py @@ -1,13 +1,13 @@ import os from .widget_sources.elsa_maindialog_ui import Ui_Dialog -from PyQt6 import QtCore, QtWidgets, QtGui -from PyQt6.QtGui import QRegularExpressionValidator -from PyQt6.QtCore import QDate +from PySide6 import QtCore, QtWidgets, QtGui +from PySide6.QtGui import QRegularExpressionValidator +from PySide6.QtCore import QDate from src import Icon from src.backend import Semester, Database from src.logic import elsa_word_to_csv, Prof from src.ui.dialogs import ElsaAddEntry, popus_confirm -from src.ui.widgets import FilePicker, DataGraph +from src.ui.widgets import FilePicker, DataQtGraph from src.backend import recreateElsaFile import loguru import sys @@ -463,10 +463,12 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog): self.graph_data["x"].pop(0) self.graph_data["y"].pop(0) # generateMissing = True if len(self.graph_data["x"]) > 2 else False - graph = DataGraph( + graph = DataQtGraph( title="ELSA Apparate pro Semester", data=self.graph_data, - label="Anzahl der Apparate", + generateMissing=False, + y_label="Anzahl der Apparate", + x_rotation=0, ) log.debug(self.graph_data) self.elsa_statistics_table.setRowCount(0) diff --git a/src/ui/widgets/filepicker.py b/src/ui/widgets/filepicker.py index fb89d82..e44a1fe 100644 --- a/src/ui/widgets/filepicker.py +++ b/src/ui/widgets/filepicker.py @@ -1,7 +1,7 @@ import sys -from PyQt6.QtCore import QSettings -from PyQt6.QtWidgets import QApplication, QFileDialog +from PySide6.QtCore import QSettings +from PySide6.QtWidgets import QApplication, QFileDialog class FilePicker: diff --git a/src/ui/widgets/iconLine.py b/src/ui/widgets/iconLine.py index d4f1478..7a1e9b7 100644 --- a/src/ui/widgets/iconLine.py +++ b/src/ui/widgets/iconLine.py @@ -1,5 +1,5 @@ from .widget_sources.icon_widget_ui import Ui_Dialog -from PyQt6 import QtWidgets +from PySide6 import QtWidgets import sys from loguru import logger as log diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 448f319..4d10292 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -2,14 +2,14 @@ import sys import loguru from natsort import natsorted -from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt6.QtCore import pyqtSignal +from PySide6 import QtCore, QtGui, QtWidgets +from PySide6.QtCore import Signal from src import LOG_DIR from src.backend import Database, Semester from src.logic import Prof, custom_sort, sort_semesters_list from src.ui.dialogs import ApparatExtendDialog, Mail_Dialog, ReminderDialog -from src.ui.widgets import DataGraph, StatusWidget +from src.ui.widgets import DataQtGraph, StatusWidget from .widget_sources.search_statistic_page_ui import Ui_Dialog @@ -26,10 +26,10 @@ class MyComboBox(QtWidgets.QComboBox): class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): - apparat_open = pyqtSignal(str) - reloadSignal = pyqtSignal() - refreshSignal = pyqtSignal() - updateCalendar = pyqtSignal(int, list) + apparat_open = Signal(str) + reloadSignal = Signal() + refreshSignal = Signal() + updateCalendar = Signal(int, list) def __init__(self): log.info("SearchStatisticPage started") @@ -310,7 +310,12 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): "x": [i[0] for i in data], "y": {"Erstellt": [i[1] for i in data], "Gelöscht": [i[2] for i in data]}, } - graph = DataGraph(title="Erstellte und gelöschte Apparate", data=graph_data) + graph = DataQtGraph( + title="Erstellte und gelöschte Apparate", + data=graph_data, + generateMissing=False, + y_label="Anzahl der Apparate", + ) # place the graph into tabWidget_3 self.tabWidget_3.addTab(graph, "Graph") diff --git a/src/ui/widgets/welcome_wizard.py b/src/ui/widgets/welcome_wizard.py index d79fa5a..f72e6e3 100644 --- a/src/ui/widgets/welcome_wizard.py +++ b/src/ui/widgets/welcome_wizard.py @@ -1,6 +1,10 @@ from .widget_sources.welcome_wizard_ui import Ui_Wizard -from PyQt6 import QtWidgets, QtCore, QtGui +from PySide6 import QtWidgets, QtCore, QtGui from src import settings +from src.backend import AdminCommands +from appdirs import AppDirs + +appdirs = AppDirs("SemesterApparatsManager", "SAM") class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): def __init__(self, parent=None): @@ -8,21 +12,146 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): self.setupUi(self) self.btn_database.clicked.connect(self.open_database_settings) self.btn_temp.clicked.connect(self.open_temp_settings) + self.btn_test.clicked.connect(self.test_login_data) + self.btn_create.clicked.connect(self.create_sam_user) + # mail password field + self.settings_mail_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) + # allow user to toggle password visibility + self.settings_mail_password.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.CustomContextMenu + ) + self.settings_mail_password.customContextMenuRequested.connect( + self.toggle_password_visibility + ) + # if button for next page is clicked, run function to store settings + self.button(QtWidgets.QWizard.WizardButton.FinishButton).clicked.connect( + self.store_settings + ) + self.settings_mail_use_user_name.toggled.connect(self.set_check_text) + # set initial values for checkbox, database + self.set_check_text(self.settings_mail_use_user_name.isChecked()) + self.settings_database.setText( + str(settings.database.path) or str(appdirs.user_data_dir) + ) + + self.settings_temp.setText( + str(settings.database.temp) or str(appdirs.user_cache_dir) + ) + + def test_login_data(self): + sam_users = AdminCommands().list_users() + sam_users = [user[2] for user in sam_users] + if not sam_users: + QtWidgets.QMessageBox.critical( + self, + "Error", + "No SAM users found. Please create a SAM user first.", + ) + return + username = self.sam_username.text() + if username in sam_users: + QtWidgets.QMessageBox.information( + self, + "Error", + f"User '{username}' exists in SAM.", + ) + + def create_sam_user(self): + """Create a SAM user in the database.""" + admin = AdminCommands() + if not admin.create_user(self.sam_username.text(), self.sam_password.text()): + QtWidgets.QMessageBox.critical( + self, + "Error", + "Failed to create SAM user", + ) + else: + QtWidgets.QMessageBox.information( + self, + "Success", + "SAM user created successfully", + ) + + def set_check_text(self, checked): + """Set the text of the checkbox based on its state.""" + if checked: + self.settings_mail_use_user_name.setText("Nutzername wird verwendet") + else: + self.settings_mail_use_user_name.setText("Nutzername wird nicht verwendet") + + def toggle_password_visibility(self, pos): + """Toggle the visibility of the password field.""" + if ( + self.settings_mail_password.echoMode() + == QtWidgets.QLineEdit.EchoMode.Password + ): + self.settings_mail_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) + else: + self.settings_mail_password.setEchoMode( + QtWidgets.QLineEdit.EchoMode.Password + ) + + def store_settings(self): + # database page + db_path = self.settings_database.text() + temp_path = self.settings_temp.text() + db_name = self.settings_database_name.text() + + # mail page + smtp_server = self.settings_mail_smtp_server.text() + smtp_port = self.settings_mail_smtp_port.text() + mail_sender = self.settings_mail_address.text() + mail_user_name = self.settings_mail_user_name.text() + mail_password = self.settings_mail_password.text() + mail_printer_mail = self.settings_mail_printer.text() + mail_signature = self.settings_mail_signature.toPlainText() + use_user_name = self.settings_mail_use_user_name.isChecked() + + # zotero page + zotero__api_key = self.settings_zotero_api_key.text() + zotero_user_id = self.settings_zotero_library_id.text() + zotero_library_type = self.settings_zotero_library_type.text() + + # openai page + openai_api_key = self.settings_openai_api_key.text() + openai_model = self.settings_openai_model.currentText() + + # store settings in config + settings.set_database_attr("path", db_path) + settings.set_database_attr("name", db_name) + settings.set_database_attr("temp", temp_path) + settings.set_mail_attr("smtp_server", smtp_server) + settings.set_mail_attr("port", smtp_port) + settings.set_mail_attr("sender", mail_sender) + settings.set_mail_attr("user_name", mail_user_name) + settings.set_mail_attr("password", mail_password) + settings.set_mail_attr("printer_mail", mail_printer_mail) + settings.set_mail_attr("signature", mail_signature) + settings.set_mail_attr("use_user_name", use_user_name) + settings.set_zotero_attr("api_key", zotero__api_key) + settings.set_zotero_attr("library_id", zotero_user_id) + settings.set_zotero_attr("library_type", zotero_library_type) + settings.set_openai_attr("api_key", openai_api_key) + settings.set_openai_attr("model", openai_model) + # save settings to file + print("Saving settings...") + print(settings) + settings.save() def open_database_settings(self): #open filepicker dialog to select database file folder file_dialog = QtWidgets.QFileDialog(self, "Select Database File") - file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.ExistingFile) - file_dialog.setNameFilter("Database Files (*.db *.sqlite *.sqlite3)") + file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory) file_dialog.setViewMode(QtWidgets.QFileDialog.ViewMode.List) - file_dialog.setDirectory(settings.database.path) # Set initial directory if needed - file_dialog.setOption(QtWidgets.QFileDialog.Option.DontUseNativeDialog, True) file_dialog.setWindowFlags(file_dialog.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint) + # set start dir to appdir.user_data_dir + file_dialog.setDirectory(str(appdirs.user_data_dir)) if file_dialog.exec(): selected_files = file_dialog.selectedFiles() if selected_files: # Do something with the selected database file - print("Selected database file:", selected_files[0]) + db_path = selected_files[0] + self.settings_database.setText(db_path) def open_temp_settings(self): #open filepicker dialog to select temporary directory @@ -34,8 +163,8 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): selected_dirs = dir_dialog.selectedFiles() if selected_dirs: # Do something with the selected temporary directory - print("Selected temporary directory:", selected_dirs[0]) - + temp_path = selected_dirs[0] + self.settings_temp.setText(temp_path) def launch_wizard(): """Launch the welcome wizard.""" diff --git a/src/ui/widgets/widget_sources/admin_create_user_ui.py b/src/ui/widgets/widget_sources/admin_create_user_ui.py index a43d4bb..935fc8b 100644 --- a/src/ui/widgets/widget_sources/admin_create_user_ui.py +++ b/src/ui/widgets/widget_sources/admin_create_user_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\admin_create_user.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): diff --git a/src/ui/widgets/widget_sources/admin_edit_prof_ui.py b/src/ui/widgets/widget_sources/admin_edit_prof_ui.py index 1ecaddc..3cbb6ce 100644 --- a/src/ui/widgets/widget_sources/admin_edit_prof_ui.py +++ b/src/ui/widgets/widget_sources/admin_edit_prof_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\admin_edit_prof.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): diff --git a/src/ui/widgets/widget_sources/admin_edit_user_ui.py b/src/ui/widgets/widget_sources/admin_edit_user_ui.py index 8094d24..a33fce7 100644 --- a/src/ui/widgets/widget_sources/admin_edit_user_ui.py +++ b/src/ui/widgets/widget_sources/admin_edit_user_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\admin_edit_user.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): diff --git a/src/ui/widgets/widget_sources/calendar_entry_ui.py b/src/ui/widgets/widget_sources/calendar_entry_ui.py index e5ea069..8041d1f 100644 --- a/src/ui/widgets/widget_sources/calendar_entry_ui.py +++ b/src/ui/widgets/widget_sources/calendar_entry_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\calendar_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): diff --git a/src/ui/widgets/widget_sources/elsa_maindialog_ui.py b/src/ui/widgets/widget_sources/elsa_maindialog_ui.py index 2822004..0edfbe5 100644 --- a/src/ui/widgets/widget_sources/elsa_maindialog_ui.py +++ b/src/ui/widgets/widget_sources/elsa_maindialog_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\elsa_maindialog.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): diff --git a/src/ui/widgets/widget_sources/icon_widget_ui.py b/src/ui/widgets/widget_sources/icon_widget_ui.py index 82f9c01..66b7475 100644 --- a/src/ui/widgets/widget_sources/icon_widget_ui.py +++ b/src/ui/widgets/widget_sources/icon_widget_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\icon_widget.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): diff --git a/src/ui/widgets/widget_sources/search_statistic_page_ui.py b/src/ui/widgets/widget_sources/search_statistic_page_ui.py index 210d580..4cced76 100644 --- a/src/ui/widgets/widget_sources/search_statistic_page_ui.py +++ b/src/ui/widgets/widget_sources/search_statistic_page_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\search_statistic_page.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): diff --git a/src/ui/widgets/widget_sources/welcome_wizard.ui b/src/ui/widgets/widget_sources/welcome_wizard.ui index b2bcba6..0f1b382 100644 --- a/src/ui/widgets/widget_sources/welcome_wizard.ui +++ b/src/ui/widgets/widget_sources/welcome_wizard.ui @@ -23,7 +23,7 @@ Qt::PlainText - 0 + 5 @@ -41,6 +41,9 @@ false + + Qt::NoFocus + 0 @@ -64,7 +67,7 @@ li.checked::marker { content: "\2612"; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- eMail</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Zotero Integration</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- KI Integration</p> -<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> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Anschließend kann ein Nutzeraccount für SAM erstellt werden</p></body></html> @@ -81,17 +84,43 @@ li.checked::marker { content: "\2612"; } 1 + + + + Qt::NoFocus + + + true + + + C:\Users\[Nutzer]\AppData\Local\SAM\SemesterApparatsManager\Cache + + + - Datenbank + Datenbankpfad + + Qt::NoFocus + + + true + - semesterapparate.db + + + + + + + + Temporäre Daten @@ -102,24 +131,24 @@ li.checked::marker { content: "\2612"; } - - + + - Temporäre Daten + ... + + + + + + + Datenbankname - + - C:\Users\[Nutzer]\AppData\Local\SAM\SemesterApparatsManager\Cache - - - - - - - ... + semesterapparate.db @@ -144,16 +173,41 @@ li.checked::marker { content: "\2612"; } - + + + true + + - + + + Qt::ImhDigitsOnly + + + true + + - + + + true + + - + + + Rechtsklick, um passwort anzuzeigen + + + + + + + + @@ -184,7 +238,11 @@ li.checked::marker { content: "\2612"; } - + + + true + + @@ -202,7 +260,7 @@ für SMTP - + @@ -219,7 +277,11 @@ für SMTP - + + + true + + @@ -282,7 +344,7 @@ für SMTP - + @@ -299,9 +361,9 @@ für SMTP - + - gpt-4o-mini + gpt3.5-turbo @@ -342,7 +404,94 @@ für SMTP + + + SAM Nutzer + + + Hier kann ein Nutzer für SAM erstellt werden + + + + + + Nutzername + + + + + + + + + + Passwort + + + + + + + + + + + + Prüfen + + + + + + + Anlegen + + + true + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + settings_database + btn_database + settings_database_name + settings_temp + btn_temp + settings_mail_smtp_server + settings_mail_smtp_port + settings_mail_address + settings_mail_user_name + settings_mail_password + settings_mail_signature + settings_mail_printer + settings_mail_use_user_name + settings_zotero_api_key + settings_zotero_library_id + settings_zotero_library_type + settings_openai_api_key + settings_openai_model + diff --git a/src/ui/widgets/widget_sources/welcome_wizard_ui.py b/src/ui/widgets/widget_sources/welcome_wizard_ui.py index 3433e85..eaf5851 100644 --- a/src/ui/widgets/widget_sources/welcome_wizard_ui.py +++ b/src/ui/widgets/widget_sources/welcome_wizard_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\welcome_wizard.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_Wizard(object): @@ -16,7 +16,7 @@ class Ui_Wizard(object): Wizard.setSizeGripEnabled(False) Wizard.setWizardStyle(QtWidgets.QWizard.WizardStyle.ClassicStyle) Wizard.setSubTitleFormat(QtCore.Qt.TextFormat.PlainText) - Wizard.setCurrentId(0) + Wizard.setCurrentId(5) self.wizardPage1 = QtWidgets.QWizardPage() self.wizardPage1.setSubTitle("") self.wizardPage1.setObjectName("wizardPage1") @@ -24,6 +24,7 @@ class Ui_Wizard(object): self.verticalLayout.setObjectName("verticalLayout") self.textEdit = QtWidgets.QTextEdit(parent=self.wizardPage1) self.textEdit.setEnabled(False) + self.textEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.textEdit.setLineWidth(0) self.textEdit.setAutoFormatting(QtWidgets.QTextEdit.AutoFormattingFlag.AutoAll) self.textEdit.setReadOnly(True) @@ -34,24 +35,35 @@ class Ui_Wizard(object): self.wizardPage2.setObjectName("wizardPage2") self.gridLayout = QtWidgets.QGridLayout(self.wizardPage2) self.gridLayout.setObjectName("gridLayout") + self.settings_temp = QtWidgets.QLineEdit(parent=self.wizardPage2) + self.settings_temp.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + self.settings_temp.setReadOnly(True) + self.settings_temp.setObjectName("settings_temp") + self.gridLayout.addWidget(self.settings_temp, 2, 1, 1, 1) self.label = QtWidgets.QLabel(parent=self.wizardPage2) self.label.setObjectName("label") self.gridLayout.addWidget(self.label, 0, 0, 1, 1) self.settings_database = QtWidgets.QLineEdit(parent=self.wizardPage2) + self.settings_database.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + self.settings_database.setReadOnly(True) + self.settings_database.setPlaceholderText("") self.settings_database.setObjectName("settings_database") self.gridLayout.addWidget(self.settings_database, 0, 1, 1, 1) + self.label_2 = QtWidgets.QLabel(parent=self.wizardPage2) + self.label_2.setObjectName("label_2") + self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) self.btn_database = QtWidgets.QToolButton(parent=self.wizardPage2) self.btn_database.setObjectName("btn_database") self.gridLayout.addWidget(self.btn_database, 0, 2, 1, 1) - self.label_2 = QtWidgets.QLabel(parent=self.wizardPage2) - self.label_2.setObjectName("label_2") - self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) - self.settings_temp = QtWidgets.QLineEdit(parent=self.wizardPage2) - self.settings_temp.setObjectName("settings_temp") - self.gridLayout.addWidget(self.settings_temp, 1, 1, 1, 1) self.btn_temp = QtWidgets.QToolButton(parent=self.wizardPage2) self.btn_temp.setObjectName("btn_temp") - self.gridLayout.addWidget(self.btn_temp, 1, 2, 1, 1) + self.gridLayout.addWidget(self.btn_temp, 2, 2, 1, 1) + self.label_16 = QtWidgets.QLabel(parent=self.wizardPage2) + self.label_16.setObjectName("label_16") + self.gridLayout.addWidget(self.label_16, 1, 0, 1, 1) + self.settings_database_name = QtWidgets.QLineEdit(parent=self.wizardPage2) + self.settings_database_name.setObjectName("settings_database_name") + self.gridLayout.addWidget(self.settings_database_name, 1, 1, 1, 1) Wizard.addPage(self.wizardPage2) self.wizardPage3 = QtWidgets.QWizardPage() self.wizardPage3.setObjectName("wizardPage3") @@ -60,18 +72,34 @@ class Ui_Wizard(object): self.label_3 = QtWidgets.QLabel(parent=self.wizardPage3) self.label_3.setObjectName("label_3") self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_3) - self.lineEdit = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.lineEdit.setObjectName("lineEdit") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit) - self.lineEdit_2 = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.lineEdit_2.setObjectName("lineEdit_2") - self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_2) - self.lineEdit_3 = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.lineEdit_3.setObjectName("lineEdit_3") - self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_3) - self.lineEdit_4 = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.lineEdit_4.setObjectName("lineEdit_4") - self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_4) + self.settings_mail_smtp_server = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.settings_mail_smtp_server.setClearButtonEnabled(True) + self.settings_mail_smtp_server.setObjectName("settings_mail_smtp_server") + self.formLayout.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_smtp_server + ) + self.settings_mail_smtp_port = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.settings_mail_smtp_port.setInputMethodHints( + QtCore.Qt.InputMethodHint.ImhDigitsOnly + ) + self.settings_mail_smtp_port.setClearButtonEnabled(True) + self.settings_mail_smtp_port.setObjectName("settings_mail_smtp_port") + self.formLayout.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_smtp_port + ) + self.settings_mail_user_name = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.settings_mail_user_name.setClearButtonEnabled(True) + self.settings_mail_user_name.setObjectName("settings_mail_user_name") + self.formLayout.setWidget( + 3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_user_name + ) + self.settings_mail_password = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.settings_mail_password.setStatusTip("") + self.settings_mail_password.setWhatsThis("") + self.settings_mail_password.setObjectName("settings_mail_password") + self.formLayout.setWidget( + 4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_password + ) self.label_4 = QtWidgets.QLabel(parent=self.wizardPage3) self.label_4.setObjectName("label_4") self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_4) @@ -84,27 +112,35 @@ class Ui_Wizard(object): self.settings_mail_use_user_name = QtWidgets.QCheckBox(parent=self.wizardPage3) self.settings_mail_use_user_name.setObjectName("settings_mail_use_user_name") self.formLayout.setWidget(8, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_use_user_name) - self.settings_mail_ = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.settings_mail_.setObjectName("settings_mail_") - self.formLayout.setWidget(7, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_) + self.settings_mail_printer = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.settings_mail_printer.setClearButtonEnabled(True) + self.settings_mail_printer.setObjectName("settings_mail_printer") + self.formLayout.setWidget( + 7, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_printer + ) self.label_8 = QtWidgets.QLabel(parent=self.wizardPage3) self.label_8.setObjectName("label_8") self.formLayout.setWidget(7, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_8) self.label_9 = QtWidgets.QLabel(parent=self.wizardPage3) self.label_9.setObjectName("label_9") self.formLayout.setWidget(8, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_9) - self.textEdit_2 = QtWidgets.QTextEdit(parent=self.wizardPage3) - self.textEdit_2.setObjectName("textEdit_2") - self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textEdit_2) + self.settings_mail_signature = QtWidgets.QTextEdit(parent=self.wizardPage3) + self.settings_mail_signature.setObjectName("settings_mail_signature") + self.formLayout.setWidget( + 5, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_signature + ) self.label_10 = QtWidgets.QLabel(parent=self.wizardPage3) self.label_10.setObjectName("label_10") self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_10) self.label_7 = QtWidgets.QLabel(parent=self.wizardPage3) self.label_7.setObjectName("label_7") self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_7) - self.lineEdit_5 = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.lineEdit_5.setObjectName("lineEdit_5") - self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_5) + self.settings_mail_address = QtWidgets.QLineEdit(parent=self.wizardPage3) + self.settings_mail_address.setClearButtonEnabled(True) + self.settings_mail_address.setObjectName("settings_mail_address") + self.formLayout.setWidget( + 2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_address + ) Wizard.addPage(self.wizardPage3) self.wizardPage4 = QtWidgets.QWizardPage() self.wizardPage4.setObjectName("wizardPage4") @@ -133,59 +169,151 @@ class Ui_Wizard(object): self.wizardPage5.setObjectName("wizardPage5") self.formLayout_3 = QtWidgets.QFormLayout(self.wizardPage5) self.formLayout_3.setObjectName("formLayout_3") - self.lineEdit_6 = QtWidgets.QLineEdit(parent=self.wizardPage5) - self.lineEdit_6.setObjectName("lineEdit_6") - self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.lineEdit_6) + self.settings_openai_api_key = QtWidgets.QLineEdit(parent=self.wizardPage5) + self.settings_openai_api_key.setObjectName("settings_openai_api_key") + self.formLayout_3.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_openai_api_key + ) self.label_14 = QtWidgets.QLabel(parent=self.wizardPage5) self.label_14.setObjectName("label_14") self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_14) self.label_15 = QtWidgets.QLabel(parent=self.wizardPage5) self.label_15.setObjectName("label_15") self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_15) - self.comboBox = QtWidgets.QComboBox(parent=self.wizardPage5) - self.comboBox.setObjectName("comboBox") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.comboBox) + self.settings_openai_model = QtWidgets.QComboBox(parent=self.wizardPage5) + self.settings_openai_model.setObjectName("settings_openai_model") + self.settings_openai_model.addItem("") + self.settings_openai_model.addItem("") + self.settings_openai_model.addItem("") + self.settings_openai_model.addItem("") + self.settings_openai_model.addItem("") + self.settings_openai_model.addItem("") + self.settings_openai_model.addItem("") + self.formLayout_3.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_openai_model + ) Wizard.addPage(self.wizardPage5) + self.wizardPage6 = QtWidgets.QWizardPage() + self.wizardPage6.setObjectName("wizardPage6") + self.formLayout_4 = QtWidgets.QFormLayout(self.wizardPage6) + self.formLayout_4.setObjectName("formLayout_4") + self.label_17 = QtWidgets.QLabel(parent=self.wizardPage6) + self.label_17.setObjectName("label_17") + self.formLayout_4.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_17 + ) + self.sam_username = QtWidgets.QLineEdit(parent=self.wizardPage6) + self.sam_username.setObjectName("sam_username") + self.formLayout_4.setWidget( + 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.sam_username + ) + self.label_18 = QtWidgets.QLabel(parent=self.wizardPage6) + self.label_18.setObjectName("label_18") + self.formLayout_4.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_18 + ) + self.sam_password = QtWidgets.QLineEdit(parent=self.wizardPage6) + self.sam_password.setObjectName("sam_password") + self.formLayout_4.setWidget( + 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.sam_password + ) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.btn_test = QtWidgets.QPushButton(parent=self.wizardPage6) + self.btn_test.setObjectName("btn_test") + self.horizontalLayout.addWidget(self.btn_test) + self.btn_create = QtWidgets.QPushButton(parent=self.wizardPage6) + self.btn_create.setDefault(True) + self.btn_create.setFlat(False) + self.btn_create.setObjectName("btn_create") + self.horizontalLayout.addWidget(self.btn_create) + self.formLayout_4.setLayout( + 2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.horizontalLayout + ) + spacerItem = QtWidgets.QSpacerItem( + 20, + 40, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Expanding, + ) + self.formLayout_4.setItem( + 3, QtWidgets.QFormLayout.ItemRole.FieldRole, spacerItem + ) + Wizard.addPage(self.wizardPage6) self.retranslateUi(Wizard) QtCore.QMetaObject.connectSlotsByName(Wizard) + Wizard.setTabOrder(self.settings_database, self.btn_database) + Wizard.setTabOrder(self.btn_database, self.settings_database_name) + Wizard.setTabOrder(self.settings_database_name, self.settings_temp) + Wizard.setTabOrder(self.settings_temp, self.btn_temp) + Wizard.setTabOrder(self.btn_temp, self.settings_mail_smtp_server) + Wizard.setTabOrder(self.settings_mail_smtp_server, self.settings_mail_smtp_port) + Wizard.setTabOrder(self.settings_mail_smtp_port, self.settings_mail_address) + Wizard.setTabOrder(self.settings_mail_address, self.settings_mail_user_name) + Wizard.setTabOrder(self.settings_mail_user_name, self.settings_mail_password) + Wizard.setTabOrder(self.settings_mail_password, self.settings_mail_signature) + Wizard.setTabOrder(self.settings_mail_signature, self.settings_mail_printer) + Wizard.setTabOrder(self.settings_mail_printer, self.settings_mail_use_user_name) + Wizard.setTabOrder( + self.settings_mail_use_user_name, self.settings_zotero_api_key + ) + Wizard.setTabOrder( + self.settings_zotero_api_key, self.settings_zotero_library_id + ) + Wizard.setTabOrder( + self.settings_zotero_library_id, self.settings_zotero_library_type + ) + Wizard.setTabOrder( + self.settings_zotero_library_type, self.settings_openai_api_key + ) + Wizard.setTabOrder(self.settings_openai_api_key, self.settings_openai_model) def retranslateUi(self, Wizard): _translate = QtCore.QCoreApplication.translate Wizard.setWindowTitle(_translate("Wizard", "Wizard")) self.wizardPage1.setTitle(_translate("Wizard", "Willkommen")) - self.textEdit.setHtml(_translate("Wizard", "\n" -"\n" -"

Es wurde erkannt, dass der SemesterApparatsManager (SAM) zum ersten Mal gestartet wurde. In den Folgenden Seiten werden die grundlegenden Einstellungen festgelegt, anschließend wird SAM geöffnet. Folgende Einstellungen werden über diesen Wizard festgelegt:

\n" -"


\n" -"

- Datenbank

\n" -"

- eMail

\n" -"

- Zotero Integration

\n" -"

- KI Integration

\n" -"


")) + self.textEdit.setHtml( + _translate( + "Wizard", + '\n' + '\n" + '

Es wurde erkannt, dass der SemesterApparatsManager (SAM) zum ersten Mal gestartet wurde. In den Folgenden Seiten werden die grundlegenden Einstellungen festgelegt, anschließend wird SAM geöffnet. Folgende Einstellungen werden über diesen Wizard festgelegt:

\n' + '


\n' + '

- Datenbank

\n' + '

- eMail

\n' + '

- Zotero Integration

\n' + '

- KI Integration

\n' + '

Anschließend kann ein Nutzeraccount für SAM erstellt werden

', + ) + ) self.wizardPage2.setTitle(_translate("Wizard", "Datenbank")) - self.wizardPage2.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die Datenbank und temporären Dateien festgelegt. Über den [...] Knopf können die Datenbank und der Speicherort für die temporären Daten festgelegt werden")) - self.label.setText(_translate("Wizard", "Datenbank")) - self.settings_database.setPlaceholderText(_translate("Wizard", "semesterapparate.db")) - self.btn_database.setText(_translate("Wizard", "...")) - self.label_2.setText(_translate("Wizard", "Temporäre Daten")) + self.wizardPage2.setSubTitle( + _translate( + "Wizard", + "Hier werden die Einstellungen für die Datenbank und temporären Dateien festgelegt. Über den [...] Knopf können die Datenbank und der Speicherort für die temporären Daten festgelegt werden", + ) + ) self.settings_temp.setPlaceholderText(_translate("Wizard", "C:\\Users\\[Nutzer]\\AppData\\Local\\SAM\\SemesterApparatsManager\\Cache")) + self.label.setText(_translate("Wizard", "Datenbankpfad")) + self.label_2.setText(_translate("Wizard", "Temporäre Daten")) + self.btn_database.setText(_translate("Wizard", "...")) self.btn_temp.setText(_translate("Wizard", "...")) + self.label_16.setText(_translate("Wizard", "Datenbankname")) + self.settings_database_name.setPlaceholderText( + _translate("Wizard", "semesterapparate.db") + ) self.wizardPage3.setTitle(_translate("Wizard", "Mail")) self.wizardPage3.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die Mailverbindung eingegeben")) self.label_3.setText(_translate("Wizard", "SMTP Server")) + self.settings_mail_password.setToolTip( + _translate("Wizard", "Rechtsklick, um passwort anzuzeigen") + ) self.label_4.setText(_translate("Wizard", "Nutzername")) self.label_5.setText(_translate("Wizard", "SMTP Port")) self.label_6.setText(_translate("Wizard", "Passwort")) @@ -205,11 +333,19 @@ class Ui_Wizard(object): self.wizardPage5.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die KI Integration eingestellt.")) self.label_14.setText(_translate("Wizard", "API Key")) self.label_15.setText(_translate("Wizard", "ChatGPT Modell")) - self.comboBox.setCurrentText(_translate("Wizard", "gpt-4o-mini")) - self.comboBox.setItemText(0, _translate("Wizard", "gpt3.5-turbo")) - self.comboBox.setItemText(1, _translate("Wizard", "gpt-4")) - self.comboBox.setItemText(2, _translate("Wizard", "gpt-4o")) - self.comboBox.setItemText(3, _translate("Wizard", "gpt-4o-mini")) - self.comboBox.setItemText(4, _translate("Wizard", "gpt-4.1")) - self.comboBox.setItemText(5, _translate("Wizard", "gpt-4.1-mini")) - self.comboBox.setItemText(6, _translate("Wizard", "gpt-4.1-nano")) + self.settings_openai_model.setCurrentText(_translate("Wizard", "gpt3.5-turbo")) + self.settings_openai_model.setItemText(0, _translate("Wizard", "gpt3.5-turbo")) + self.settings_openai_model.setItemText(1, _translate("Wizard", "gpt-4")) + self.settings_openai_model.setItemText(2, _translate("Wizard", "gpt-4o")) + self.settings_openai_model.setItemText(3, _translate("Wizard", "gpt-4o-mini")) + self.settings_openai_model.setItemText(4, _translate("Wizard", "gpt-4.1")) + self.settings_openai_model.setItemText(5, _translate("Wizard", "gpt-4.1-mini")) + self.settings_openai_model.setItemText(6, _translate("Wizard", "gpt-4.1-nano")) + self.wizardPage6.setTitle(_translate("Wizard", "SAM Nutzer")) + self.wizardPage6.setSubTitle( + _translate("Wizard", "Hier kann ein Nutzer für SAM erstellt werden") + ) + self.label_17.setText(_translate("Wizard", "Nutzername")) + self.label_18.setText(_translate("Wizard", "Passwort")) + self.btn_test.setText(_translate("Wizard", "Prüfen")) + self.btn_create.setText(_translate("Wizard", "Anlegen")) diff --git a/src/utils/Ui_docs.py b/src/utils/Ui_docs.py index b849a8d..e4b744c 100644 --- a/src/utils/Ui_docs.py +++ b/src/utils/Ui_docs.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\utils\docs.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): diff --git a/src/utils/docs_ui.py b/src/utils/docs_ui.py index 3e11e63..1f0523a 100644 --- a/src/utils/docs_ui.py +++ b/src/utils/docs_ui.py @@ -1,12 +1,12 @@ # Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\utils\docs.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): diff --git a/src/utils/documentationview.py b/src/utils/documentationview.py index 2ad2283..ca180f2 100644 --- a/src/utils/documentationview.py +++ b/src/utils/documentationview.py @@ -1,8 +1,8 @@ import os import sys -# from PyQt6 import Webview -from PyQt6.QtWidgets import QApplication, QMainWindow, QTabWidget +# from PySide6 import Webview +from PySide6.QtWidgets import QApplication, QMainWindow, QTabWidget documentation_path = "docs" diff --git a/src/utils/icon.py b/src/utils/icon.py index f281a27..b659d75 100644 --- a/src/utils/icon.py +++ b/src/utils/icon.py @@ -1,5 +1,5 @@ import darkdetect -from PyQt6 import QtGui +from PySide6 import QtGui import re from src import settings diff --git a/uv.lock b/uv.lock index ee8e501..a58dca0 100644 --- a/uv.lock +++ b/uv.lock @@ -118,7 +118,7 @@ wheels = [ [[package]] name = "bump-my-version" -version = "1.1.2" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -131,9 +131,9 @@ dependencies = [ { name = "tomlkit" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/13/0a/544e8eb6d46baa99bf16d180b4ddb4509631fa8476e686c8e6c47681afb4/bump_my_version-1.1.2.tar.gz", hash = "sha256:0122845a78502b5a5a635ca17c1efb3e1ec05e77d72d13b2314186b9806882fb", size = 1120309 } +sdist = { url = "https://files.pythonhosted.org/packages/a4/b6/c45043a404e0878e3abeff1c25c87df78777c33760e7459901e0504f003a/bump_my_version-1.2.0.tar.gz", hash = "sha256:5120d798aaf26468a37ca0f127992dc036688b8e5e106adc8870b13c2a2df22d", size = 1136170 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/a9/026894e86ce2838d029af1344c71fd57560d1b6e2ce6513c340cbf8e00cb/bump_my_version-1.1.2-py3-none-any.whl", hash = "sha256:71a2a8c3940c87749c4cc404b2ada2fafbeab4e478e0ef54537686905ae58e0d", size = 59495 }, + { url = "https://files.pythonhosted.org/packages/1f/e4/715484178fa80279cd67fd06c6641a4611c5ea580acdc221ad715b39d85c/bump_my_version-1.2.0-py3-none-any.whl", hash = "sha256:201e6b103ff0f2b240c9d0a6eb83db382840b1f78eb78f6d77726bed39a326d8", size = 59560 }, ] [[package]] @@ -224,14 +224,14 @@ wheels = [ [[package]] name = "click" -version = "8.1.8" +version = "8.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "platform_system == 'Windows'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215 }, ] [[package]] @@ -245,11 +245,11 @@ wheels = [ [[package]] name = "comtypes" -version = "1.4.10" +version = "1.4.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/07/7e/34f4941ab5ec7d1d4c496282b1944a5119fc52641c5770a78e6fa0ca32ec/comtypes-1.4.10.zip", hash = "sha256:b92372e76299836177b41aeda784225e18c5071c6bacdab88a7433224a4dc912", size = 267293 } +sdist = { url = "https://files.pythonhosted.org/packages/3c/b9/4431119a640f568190499fd76fac4b4ccb5695659c4b3aac570329c150ea/comtypes-1.4.11.zip", hash = "sha256:0a4259370ec48b685fe4483b0944ba1df0aa45163922073fe9b7df1d187db09e", size = 272448 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/44/72009bb0a0d8286f6408c9cb70552350e21e9c280bfa1ef30784b30dfc0f/comtypes-1.4.10-py3-none-any.whl", hash = "sha256:e078555721ee7ab40648a3363697d420b845b323e5944b55846e96aff97d2534", size = 241481 }, + { url = "https://files.pythonhosted.org/packages/f1/f4/7b7fdbb613992013c4518a0bf8fee2915f79ec07bcfa6180569bca7fa8ef/comtypes-1.4.11-py3-none-any.whl", hash = "sha256:1760d5059ca7ca1d61b574c998378d879c271a86c41f88926619ea97497592bb", size = 246365 }, ] [[package]] @@ -319,36 +319,35 @@ wheels = [ [[package]] name = "greenlet" -version = "3.2.1" +version = "3.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/74/907bb43af91782e0366b0960af62a8ce1f9398e4291cac7beaeffbee0c04/greenlet-3.2.1.tar.gz", hash = "sha256:9f4dd4b4946b14bb3bf038f81e1d2e535b7d94f1b2a59fdba1293cd9c1a0a4d7", size = 184475 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365", size = 185752 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/d1/e4777b188a04726f6cf69047830d37365b9191017f54caf2f7af336a6f18/greenlet-3.2.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:0ba2811509a30e5f943be048895a983a8daf0b9aa0ac0ead526dfb5d987d80ea", size = 270381 }, - { url = "https://files.pythonhosted.org/packages/59/e7/b5b738f5679247ddfcf2179c38945519668dced60c3164c20d55c1a7bb4a/greenlet-3.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4245246e72352b150a1588d43ddc8ab5e306bef924c26571aafafa5d1aaae4e8", size = 637195 }, - { url = "https://files.pythonhosted.org/packages/6c/9f/57968c88a5f6bc371364baf983a2e5549cca8f503bfef591b6dd81332cbc/greenlet-3.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7abc0545d8e880779f0c7ce665a1afc3f72f0ca0d5815e2b006cafc4c1cc5840", size = 651381 }, - { url = "https://files.pythonhosted.org/packages/40/81/1533c9a458e9f2ebccb3ae22f1463b2093b0eb448a88aac36182f1c2cd3d/greenlet-3.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6dcc6d604a6575c6225ac0da39df9335cc0c6ac50725063fa90f104f3dbdb2c9", size = 646110 }, - { url = "https://files.pythonhosted.org/packages/06/66/25f7e4b1468ebe4a520757f2e41c2a36a2f49a12e963431b82e9f98df2a0/greenlet-3.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2273586879affca2d1f414709bb1f61f0770adcabf9eda8ef48fd90b36f15d12", size = 648070 }, - { url = "https://files.pythonhosted.org/packages/d7/4c/49d366565c4c4d29e6f666287b9e2f471a66c3a3d8d5066692e347f09e27/greenlet-3.2.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff38c869ed30fff07f1452d9a204ece1ec6d3c0870e0ba6e478ce7c1515acf22", size = 603816 }, - { url = "https://files.pythonhosted.org/packages/04/15/1612bb61506f44b6b8b6bebb6488702b1fe1432547e95dda57874303a1f5/greenlet-3.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e934591a7a4084fa10ee5ef50eb9d2ac8c4075d5c9cf91128116b5dca49d43b1", size = 1119572 }, - { url = "https://files.pythonhosted.org/packages/cc/2f/002b99dacd1610e825876f5cbbe7f86740aa2a6b76816e5eca41c8457e85/greenlet-3.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:063bcf7f8ee28eb91e7f7a8148c65a43b73fbdc0064ab693e024b5a940070145", size = 1147442 }, - { url = "https://files.pythonhosted.org/packages/c0/ba/82a2c3b9868644ee6011da742156247070f30e952f4d33f33857458450f2/greenlet-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7132e024ebeeeabbe661cf8878aac5d2e643975c4feae833142592ec2f03263d", size = 296207 }, - { url = "https://files.pythonhosted.org/packages/77/2a/581b3808afec55b2db838742527c40b4ce68b9b64feedff0fd0123f4b19a/greenlet-3.2.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:e1967882f0c42eaf42282a87579685c8673c51153b845fde1ee81be720ae27ac", size = 269119 }, - { url = "https://files.pythonhosted.org/packages/b0/f3/1c4e27fbdc84e13f05afc2baf605e704668ffa26e73a43eca93e1120813e/greenlet-3.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e77ae69032a95640a5fe8c857ec7bee569a0997e809570f4c92048691ce4b437", size = 637314 }, - { url = "https://files.pythonhosted.org/packages/fc/1a/9fc43cb0044f425f7252da9847893b6de4e3b20c0a748bce7ab3f063d5bc/greenlet-3.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3227c6ec1149d4520bc99edac3b9bc8358d0034825f3ca7572165cb502d8f29a", size = 651421 }, - { url = "https://files.pythonhosted.org/packages/8a/65/d47c03cdc62c6680206b7420c4a98363ee997e87a5e9da1e83bd7eeb57a8/greenlet-3.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ddda0197c5b46eedb5628d33dad034c455ae77708c7bf192686e760e26d6a0c", size = 645789 }, - { url = "https://files.pythonhosted.org/packages/2f/40/0faf8bee1b106c241780f377b9951dd4564ef0972de1942ef74687aa6bba/greenlet-3.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de62b542e5dcf0b6116c310dec17b82bb06ef2ceb696156ff7bf74a7a498d982", size = 648262 }, - { url = "https://files.pythonhosted.org/packages/e0/a8/73305f713183c2cb08f3ddd32eaa20a6854ba9c37061d682192db9b021c3/greenlet-3.2.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c07a0c01010df42f1f058b3973decc69c4d82e036a951c3deaf89ab114054c07", size = 606770 }, - { url = "https://files.pythonhosted.org/packages/c3/05/7d726e1fb7f8a6ac55ff212a54238a36c57db83446523c763e20cd30b837/greenlet-3.2.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2530bfb0abcd451ea81068e6d0a1aac6dabf3f4c23c8bd8e2a8f579c2dd60d95", size = 1117960 }, - { url = "https://files.pythonhosted.org/packages/bf/9f/2b6cb1bd9f1537e7b08c08705c4a1d7bd4f64489c67d102225c4fd262bda/greenlet-3.2.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c472adfca310f849903295c351d297559462067f618944ce2650a1878b84123", size = 1145500 }, - { url = "https://files.pythonhosted.org/packages/e4/f6/339c6e707062319546598eb9827d3ca8942a3eccc610d4a54c1da7b62527/greenlet-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:24a496479bc8bd01c39aa6516a43c717b4cee7196573c47b1f8e1011f7c12495", size = 295994 }, - { url = "https://files.pythonhosted.org/packages/f1/72/2a251d74a596af7bb1717e891ad4275a3fd5ac06152319d7ad8c77f876af/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:175d583f7d5ee57845591fc30d852b75b144eb44b05f38b67966ed6df05c8526", size = 629889 }, - { url = "https://files.pythonhosted.org/packages/29/2e/d7ed8bf97641bf704b6a43907c0e082cdf44d5bc026eb8e1b79283e7a719/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ecc9d33ca9428e4536ea53e79d781792cee114d2fa2695b173092bdbd8cd6d5", size = 635261 }, - { url = "https://files.pythonhosted.org/packages/1e/75/802aa27848a6fcb5e566f69c64534f572e310f0f12d41e9201a81e741551/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f56382ac4df3860ebed8ed838f268f03ddf4e459b954415534130062b16bc32", size = 632523 }, - { url = "https://files.pythonhosted.org/packages/56/09/f7c1c3bab9b4c589ad356503dd71be00935e9c4db4db516ed88fc80f1187/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc45a7189c91c0f89aaf9d69da428ce8301b0fd66c914a499199cfb0c28420fc", size = 628816 }, - { url = "https://files.pythonhosted.org/packages/79/e0/1bb90d30b5450eac2dffeaac6b692857c4bd642c21883b79faa8fa056cf2/greenlet-3.2.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51a2f49da08cff79ee42eb22f1658a2aed60c72792f0a0a95f5f0ca6d101b1fb", size = 593687 }, - { url = "https://files.pythonhosted.org/packages/c5/b5/adbe03c8b4c178add20cc716021183ae6b0326d56ba8793d7828c94286f6/greenlet-3.2.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:0c68bbc639359493420282d2f34fa114e992a8724481d700da0b10d10a7611b8", size = 1105754 }, - { url = "https://files.pythonhosted.org/packages/39/93/84582d7ef38dec009543ccadec6ab41079a6cbc2b8c0566bcd07bf1aaf6c/greenlet-3.2.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:e775176b5c203a1fa4be19f91da00fd3bff536868b77b237da3f4daa5971ae5d", size = 1125160 }, - { url = "https://files.pythonhosted.org/packages/01/e6/f9d759788518a6248684e3afeb3691f3ab0276d769b6217a1533362298c8/greenlet-3.2.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d6668caf15f181c1b82fb6406f3911696975cc4c37d782e19cb7ba499e556189", size = 269897 }, + { url = "https://files.pythonhosted.org/packages/f3/94/ad0d435f7c48debe960c53b8f60fb41c2026b1d0fa4a99a1cb17c3461e09/greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d", size = 271992 }, + { url = "https://files.pythonhosted.org/packages/93/5d/7c27cf4d003d6e77749d299c7c8f5fd50b4f251647b5c2e97e1f20da0ab5/greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b", size = 638820 }, + { url = "https://files.pythonhosted.org/packages/c6/7e/807e1e9be07a125bb4c169144937910bf59b9d2f6d931578e57f0bce0ae2/greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d", size = 653046 }, + { url = "https://files.pythonhosted.org/packages/9d/ab/158c1a4ea1068bdbc78dba5a3de57e4c7aeb4e7fa034320ea94c688bfb61/greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264", size = 647701 }, + { url = "https://files.pythonhosted.org/packages/cc/0d/93729068259b550d6a0288da4ff72b86ed05626eaf1eb7c0d3466a2571de/greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688", size = 649747 }, + { url = "https://files.pythonhosted.org/packages/f6/f6/c82ac1851c60851302d8581680573245c8fc300253fc1ff741ae74a6c24d/greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb", size = 605461 }, + { url = "https://files.pythonhosted.org/packages/98/82/d022cf25ca39cf1200650fc58c52af32c90f80479c25d1cbf57980ec3065/greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c", size = 1121190 }, + { url = "https://files.pythonhosted.org/packages/f5/e1/25297f70717abe8104c20ecf7af0a5b82d2f5a980eb1ac79f65654799f9f/greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163", size = 1149055 }, + { url = "https://files.pythonhosted.org/packages/1f/8f/8f9e56c5e82eb2c26e8cde787962e66494312dc8cb261c460e1f3a9c88bc/greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849", size = 297817 }, + { url = "https://files.pythonhosted.org/packages/b1/cf/f5c0b23309070ae93de75c90d29300751a5aacefc0a3ed1b1d8edb28f08b/greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad", size = 270732 }, + { url = "https://files.pythonhosted.org/packages/48/ae/91a957ba60482d3fecf9be49bc3948f341d706b52ddb9d83a70d42abd498/greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef", size = 639033 }, + { url = "https://files.pythonhosted.org/packages/6f/df/20ffa66dd5a7a7beffa6451bdb7400d66251374ab40b99981478c69a67a8/greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3", size = 652999 }, + { url = "https://files.pythonhosted.org/packages/51/b4/ebb2c8cb41e521f1d72bf0465f2f9a2fd803f674a88db228887e6847077e/greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95", size = 647368 }, + { url = "https://files.pythonhosted.org/packages/8e/6a/1e1b5aa10dced4ae876a322155705257748108b7fd2e4fae3f2a091fe81a/greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb", size = 650037 }, + { url = "https://files.pythonhosted.org/packages/26/f2/ad51331a157c7015c675702e2d5230c243695c788f8f75feba1af32b3617/greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b", size = 608402 }, + { url = "https://files.pythonhosted.org/packages/26/bc/862bd2083e6b3aff23300900a956f4ea9a4059de337f5c8734346b9b34fc/greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0", size = 1119577 }, + { url = "https://files.pythonhosted.org/packages/86/94/1fc0cc068cfde885170e01de40a619b00eaa8f2916bf3541744730ffb4c3/greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36", size = 1147121 }, + { url = "https://files.pythonhosted.org/packages/27/1a/199f9587e8cb08a0658f9c30f3799244307614148ffe8b1e3aa22f324dea/greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3", size = 297603 }, + { url = "https://files.pythonhosted.org/packages/d8/ca/accd7aa5280eb92b70ed9e8f7fd79dc50a2c21d8c73b9a0856f5b564e222/greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86", size = 271479 }, + { url = "https://files.pythonhosted.org/packages/55/71/01ed9895d9eb49223280ecc98a557585edfa56b3d0e965b9fa9f7f06b6d9/greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97", size = 683952 }, + { url = "https://files.pythonhosted.org/packages/ea/61/638c4bdf460c3c678a0a1ef4c200f347dff80719597e53b5edb2fb27ab54/greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728", size = 696917 }, + { url = "https://files.pythonhosted.org/packages/22/cc/0bd1a7eb759d1f3e3cc2d1bc0f0b487ad3cc9f34d74da4b80f226fde4ec3/greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a", size = 692443 }, + { url = "https://files.pythonhosted.org/packages/67/10/b2a4b63d3f08362662e89c103f7fe28894a51ae0bc890fabf37d1d780e52/greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892", size = 692995 }, + { url = "https://files.pythonhosted.org/packages/5a/c6/ad82f148a4e3ce9564056453a71529732baf5448ad53fc323e37efe34f66/greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141", size = 655320 }, + { url = "https://files.pythonhosted.org/packages/5c/4f/aab73ecaa6b3086a4c89863d94cf26fa84cbff63f52ce9bc4342b3087a06/greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a", size = 301236 }, ] [[package]] @@ -644,7 +643,7 @@ wheels = [ [[package]] name = "mkdocs-material" -version = "9.6.12" +version = "9.6.14" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "babel" }, @@ -659,9 +658,9 @@ dependencies = [ { name = "pymdown-extensions" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2d/ef/25fc10dbbb8faeeeb10ed7734d84a347cd2ec5d7200733f11c5553c02608/mkdocs_material-9.6.12.tar.gz", hash = "sha256:add6a6337b29f9ea7912cb1efc661de2c369060b040eb5119855d794ea85b473", size = 3951532 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fa/0101de32af88f87cf5cc23ad5f2e2030d00995f74e616306513431b8ab4b/mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754", size = 3951707 } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/00/592940f4d150327a4f455171b2c9d4c3be7779a88e18b0a086183fcd8f06/mkdocs_material-9.6.12-py3-none-any.whl", hash = "sha256:92b4fbdc329e4febc267ca6e2c51e8501fa97b2225c5f4deb4d4e43550f8e61e", size = 8703654 }, + { url = "https://files.pythonhosted.org/packages/3a/a1/7fdb959ad592e013c01558822fd3c22931a95a0f08cf0a7c36da13a5b2b5/mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b", size = 8703767 }, ] [[package]] @@ -684,50 +683,53 @@ wheels = [ [[package]] name = "nuitka" -version = "2.7.1" +version = "2.7.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ordered-set" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/f7/f1d87901ab90be0bc1dd96d9f899a07f626f5f2bebe3b09e475fdc4b7fdb/Nuitka-2.7.1.tar.gz", hash = "sha256:1e7ff9208f8d8262302a6eee05d299650f30813dbecb4501d365a712c3169209", size = 3882402 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/3f/3294699ad8ebca820127b5606c77e52bb1bd5d7eea97e9dd5a6228884e80/Nuitka-2.7.7.tar.gz", hash = "sha256:327e697e1a3eea2608ca7dce228c2d7686d65e38af9907c98646695ba5df9edf", size = 3886105 } [[package]] name = "numpy" -version = "2.2.5" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/b2/ce4b867d8cd9c0ee84938ae1e6a6f7926ebf928c9090d036fc3c6a04f946/numpy-2.2.5.tar.gz", hash = "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291", size = 20273920 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/db/8e12381333aea300890829a0a36bfa738cac95475d88982d538725143fd9/numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6", size = 20382813 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/f7/1fd4ff108cd9d7ef929b8882692e23665dc9c23feecafbb9c6b80f4ec583/numpy-2.2.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ee461a4eaab4f165b68780a6a1af95fb23a29932be7569b9fab666c407969051", size = 20948633 }, - { url = "https://files.pythonhosted.org/packages/12/03/d443c278348371b20d830af155ff2079acad6a9e60279fac2b41dbbb73d8/numpy-2.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec31367fd6a255dc8de4772bd1658c3e926d8e860a0b6e922b615e532d320ddc", size = 14176123 }, - { url = "https://files.pythonhosted.org/packages/2b/0b/5ca264641d0e7b14393313304da48b225d15d471250376f3fbdb1a2be603/numpy-2.2.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:47834cde750d3c9f4e52c6ca28a7361859fcaf52695c7dc3cc1a720b8922683e", size = 5163817 }, - { url = "https://files.pythonhosted.org/packages/04/b3/d522672b9e3d28e26e1613de7675b441bbd1eaca75db95680635dd158c67/numpy-2.2.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:2c1a1c6ccce4022383583a6ded7bbcda22fc635eb4eb1e0a053336425ed36dfa", size = 6698066 }, - { url = "https://files.pythonhosted.org/packages/a0/93/0f7a75c1ff02d4b76df35079676b3b2719fcdfb39abdf44c8b33f43ef37d/numpy-2.2.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d75f338f5f79ee23548b03d801d28a505198297534f62416391857ea0479571", size = 14087277 }, - { url = "https://files.pythonhosted.org/packages/b0/d9/7c338b923c53d431bc837b5b787052fef9ae68a56fe91e325aac0d48226e/numpy-2.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a801fef99668f309b88640e28d261991bfad9617c27beda4a3aec4f217ea073", size = 16135742 }, - { url = "https://files.pythonhosted.org/packages/2d/10/4dec9184a5d74ba9867c6f7d1e9f2e0fb5fe96ff2bf50bb6f342d64f2003/numpy-2.2.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:abe38cd8381245a7f49967a6010e77dbf3680bd3627c0fe4362dd693b404c7f8", size = 15581825 }, - { url = "https://files.pythonhosted.org/packages/80/1f/2b6fcd636e848053f5b57712a7d1880b1565eec35a637fdfd0a30d5e738d/numpy-2.2.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a0ac90e46fdb5649ab6369d1ab6104bfe5854ab19b645bf5cda0127a13034ae", size = 17899600 }, - { url = "https://files.pythonhosted.org/packages/ec/87/36801f4dc2623d76a0a3835975524a84bd2b18fe0f8835d45c8eae2f9ff2/numpy-2.2.5-cp312-cp312-win32.whl", hash = "sha256:0cd48122a6b7eab8f06404805b1bd5856200e3ed6f8a1b9a194f9d9054631beb", size = 6312626 }, - { url = "https://files.pythonhosted.org/packages/8b/09/4ffb4d6cfe7ca6707336187951992bd8a8b9142cf345d87ab858d2d7636a/numpy-2.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:ced69262a8278547e63409b2653b372bf4baff0870c57efa76c5703fd6543282", size = 12645715 }, - { url = "https://files.pythonhosted.org/packages/e2/a0/0aa7f0f4509a2e07bd7a509042967c2fab635690d4f48c6c7b3afd4f448c/numpy-2.2.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4", size = 20935102 }, - { url = "https://files.pythonhosted.org/packages/7e/e4/a6a9f4537542912ec513185396fce52cdd45bdcf3e9d921ab02a93ca5aa9/numpy-2.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f", size = 14191709 }, - { url = "https://files.pythonhosted.org/packages/be/65/72f3186b6050bbfe9c43cb81f9df59ae63603491d36179cf7a7c8d216758/numpy-2.2.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9", size = 5149173 }, - { url = "https://files.pythonhosted.org/packages/e5/e9/83e7a9432378dde5802651307ae5e9ea07bb72b416728202218cd4da2801/numpy-2.2.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191", size = 6684502 }, - { url = "https://files.pythonhosted.org/packages/ea/27/b80da6c762394c8ee516b74c1f686fcd16c8f23b14de57ba0cad7349d1d2/numpy-2.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372", size = 14084417 }, - { url = "https://files.pythonhosted.org/packages/aa/fc/ebfd32c3e124e6a1043e19c0ab0769818aa69050ce5589b63d05ff185526/numpy-2.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d", size = 16133807 }, - { url = "https://files.pythonhosted.org/packages/bf/9b/4cc171a0acbe4666f7775cfd21d4eb6bb1d36d3a0431f48a73e9212d2278/numpy-2.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7", size = 15575611 }, - { url = "https://files.pythonhosted.org/packages/a3/45/40f4135341850df48f8edcf949cf47b523c404b712774f8855a64c96ef29/numpy-2.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73", size = 17895747 }, - { url = "https://files.pythonhosted.org/packages/f8/4c/b32a17a46f0ffbde8cc82df6d3daeaf4f552e346df143e1b188a701a8f09/numpy-2.2.5-cp313-cp313-win32.whl", hash = "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b", size = 6309594 }, - { url = "https://files.pythonhosted.org/packages/13/ae/72e6276feb9ef06787365b05915bfdb057d01fceb4a43cb80978e518d79b/numpy-2.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471", size = 12638356 }, - { url = "https://files.pythonhosted.org/packages/79/56/be8b85a9f2adb688e7ded6324e20149a03541d2b3297c3ffc1a73f46dedb/numpy-2.2.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6", size = 20963778 }, - { url = "https://files.pythonhosted.org/packages/ff/77/19c5e62d55bff507a18c3cdff82e94fe174957bad25860a991cac719d3ab/numpy-2.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba", size = 14207279 }, - { url = "https://files.pythonhosted.org/packages/75/22/aa11f22dc11ff4ffe4e849d9b63bbe8d4ac6d5fae85ddaa67dfe43be3e76/numpy-2.2.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133", size = 5199247 }, - { url = "https://files.pythonhosted.org/packages/4f/6c/12d5e760fc62c08eded0394f62039f5a9857f758312bf01632a81d841459/numpy-2.2.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376", size = 6711087 }, - { url = "https://files.pythonhosted.org/packages/ef/94/ece8280cf4218b2bee5cec9567629e61e51b4be501e5c6840ceb593db945/numpy-2.2.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19", size = 14059964 }, - { url = "https://files.pythonhosted.org/packages/39/41/c5377dac0514aaeec69115830a39d905b1882819c8e65d97fc60e177e19e/numpy-2.2.5-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0", size = 16121214 }, - { url = "https://files.pythonhosted.org/packages/db/54/3b9f89a943257bc8e187145c6bc0eb8e3d615655f7b14e9b490b053e8149/numpy-2.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a", size = 15575788 }, - { url = "https://files.pythonhosted.org/packages/b1/c4/2e407e85df35b29f79945751b8f8e671057a13a376497d7fb2151ba0d290/numpy-2.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066", size = 17893672 }, - { url = "https://files.pythonhosted.org/packages/29/7e/d0b44e129d038dba453f00d0e29ebd6eaf2f06055d72b95b9947998aca14/numpy-2.2.5-cp313-cp313t-win32.whl", hash = "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e", size = 6377102 }, - { url = "https://files.pythonhosted.org/packages/63/be/b85e4aa4bf42c6502851b971f1c326d583fcc68227385f92089cf50a7b45/numpy-2.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8", size = 12750096 }, + { url = "https://files.pythonhosted.org/packages/89/59/9df493df81ac6f76e9f05cdbe013cdb0c9a37b434f6e594f5bd25e278908/numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba", size = 20897025 }, + { url = "https://files.pythonhosted.org/packages/2f/86/4ff04335901d6cf3a6bb9c748b0097546ae5af35e455ae9b962ebff4ecd7/numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e", size = 14129882 }, + { url = "https://files.pythonhosted.org/packages/71/8d/a942cd4f959de7f08a79ab0c7e6cecb7431d5403dce78959a726f0f57aa1/numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2", size = 5110181 }, + { url = "https://files.pythonhosted.org/packages/86/5d/45850982efc7b2c839c5626fb67fbbc520d5b0d7c1ba1ae3651f2f74c296/numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459", size = 6647581 }, + { url = "https://files.pythonhosted.org/packages/1a/c0/c871d4a83f93b00373d3eebe4b01525eee8ef10b623a335ec262b58f4dc1/numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a", size = 14262317 }, + { url = "https://files.pythonhosted.org/packages/b7/f6/bc47f5fa666d5ff4145254f9e618d56e6a4ef9b874654ca74c19113bb538/numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a", size = 16633919 }, + { url = "https://files.pythonhosted.org/packages/f5/b4/65f48009ca0c9b76df5f404fccdea5a985a1bb2e34e97f21a17d9ad1a4ba/numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67", size = 15567651 }, + { url = "https://files.pythonhosted.org/packages/f1/62/5367855a2018578e9334ed08252ef67cc302e53edc869666f71641cad40b/numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc", size = 18361723 }, + { url = "https://files.pythonhosted.org/packages/d4/75/5baed8cd867eabee8aad1e74d7197d73971d6a3d40c821f1848b8fab8b84/numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570", size = 6318285 }, + { url = "https://files.pythonhosted.org/packages/bc/49/d5781eaa1a15acb3b3a3f49dc9e2ff18d92d0ce5c2976f4ab5c0a7360250/numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd", size = 12732594 }, + { url = "https://files.pythonhosted.org/packages/c2/1c/6d343e030815c7c97a1f9fbad00211b47717c7fe446834c224bd5311e6f1/numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea", size = 9891498 }, + { url = "https://files.pythonhosted.org/packages/73/fc/1d67f751fd4dbafc5780244fe699bc4084268bad44b7c5deb0492473127b/numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a", size = 20889633 }, + { url = "https://files.pythonhosted.org/packages/e8/95/73ffdb69e5c3f19ec4530f8924c4386e7ba097efc94b9c0aff607178ad94/numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959", size = 14151683 }, + { url = "https://files.pythonhosted.org/packages/64/d5/06d4bb31bb65a1d9c419eb5676173a2f90fd8da3c59f816cc54c640ce265/numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe", size = 5102683 }, + { url = "https://files.pythonhosted.org/packages/12/8b/6c2cef44f8ccdc231f6b56013dff1d71138c48124334aded36b1a1b30c5a/numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb", size = 6640253 }, + { url = "https://files.pythonhosted.org/packages/62/aa/fca4bf8de3396ddb59544df9b75ffe5b73096174de97a9492d426f5cd4aa/numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0", size = 14258658 }, + { url = "https://files.pythonhosted.org/packages/1c/12/734dce1087eed1875f2297f687e671cfe53a091b6f2f55f0c7241aad041b/numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f", size = 16628765 }, + { url = "https://files.pythonhosted.org/packages/48/03/ffa41ade0e825cbcd5606a5669962419528212a16082763fc051a7247d76/numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8", size = 15564335 }, + { url = "https://files.pythonhosted.org/packages/07/58/869398a11863310aee0ff85a3e13b4c12f20d032b90c4b3ee93c3b728393/numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270", size = 18360608 }, + { url = "https://files.pythonhosted.org/packages/2f/8a/5756935752ad278c17e8a061eb2127c9a3edf4ba2c31779548b336f23c8d/numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f", size = 6310005 }, + { url = "https://files.pythonhosted.org/packages/08/60/61d60cf0dfc0bf15381eaef46366ebc0c1a787856d1db0c80b006092af84/numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5", size = 12729093 }, + { url = "https://files.pythonhosted.org/packages/66/31/2f2f2d2b3e3c32d5753d01437240feaa32220b73258c9eef2e42a0832866/numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e", size = 9885689 }, + { url = "https://files.pythonhosted.org/packages/f1/89/c7828f23cc50f607ceb912774bb4cff225ccae7131c431398ad8400e2c98/numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8", size = 20986612 }, + { url = "https://files.pythonhosted.org/packages/dd/46/79ecf47da34c4c50eedec7511e53d57ffdfd31c742c00be7dc1d5ffdb917/numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3", size = 14298953 }, + { url = "https://files.pythonhosted.org/packages/59/44/f6caf50713d6ff4480640bccb2a534ce1d8e6e0960c8f864947439f0ee95/numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f", size = 5225806 }, + { url = "https://files.pythonhosted.org/packages/a6/43/e1fd1aca7c97e234dd05e66de4ab7a5be54548257efcdd1bc33637e72102/numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808", size = 6735169 }, + { url = "https://files.pythonhosted.org/packages/84/89/f76f93b06a03177c0faa7ca94d0856c4e5c4bcaf3c5f77640c9ed0303e1c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8", size = 14330701 }, + { url = "https://files.pythonhosted.org/packages/aa/f5/4858c3e9ff7a7d64561b20580cf7cc5d085794bd465a19604945d6501f6c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad", size = 16692983 }, + { url = "https://files.pythonhosted.org/packages/08/17/0e3b4182e691a10e9483bcc62b4bb8693dbf9ea5dc9ba0b77a60435074bb/numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b", size = 15641435 }, + { url = "https://files.pythonhosted.org/packages/4e/d5/463279fda028d3c1efa74e7e8d507605ae87f33dbd0543cf4c4527c8b882/numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555", size = 18433798 }, + { url = "https://files.pythonhosted.org/packages/0e/1e/7a9d98c886d4c39a2b4d3a7c026bffcf8fbcaf518782132d12a301cfc47a/numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61", size = 6438632 }, + { url = "https://files.pythonhosted.org/packages/fe/ab/66fc909931d5eb230107d016861824f335ae2c0533f422e654e5ff556784/numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb", size = 12868491 }, + { url = "https://files.pythonhosted.org/packages/ee/e8/2c8a1c9e34d6f6d600c83d5ce5b71646c32a13f34ca5c518cc060639841c/numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944", size = 9935345 }, ] [[package]] @@ -745,7 +747,7 @@ wheels = [ [[package]] name = "openai" -version = "1.79.0" +version = "1.86.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -757,9 +759,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/cf/4901077dbbfd0d82a814d721600fa0c3a61a093d7f0bf84d0e4732448dc9/openai-1.79.0.tar.gz", hash = "sha256:e3b627aa82858d3e42d16616edc22aa9f7477ee5eb3e6819e9f44a961d899a4c", size = 444736 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/7a/9ad4a61f1502f0e59d8c27fb629e28a63259a44d8d31cd2314e1534a2d9f/openai-1.86.0.tar.gz", hash = "sha256:c64d5b788359a8fdf69bd605ae804ce41c1ce2e78b8dd93e2542e0ee267f1e4b", size = 468272 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/d2/e3992bb7c6641b765c1008e3c96e076e0b50381be2cce344e6ff177bad80/openai-1.79.0-py3-none-any.whl", hash = "sha256:d5050b92d5ef83f869cb8dcd0aca0b2291c3413412500eec40c66981b3966992", size = 683334 }, + { url = "https://files.pythonhosted.org/packages/58/c1/dfb16b3432810fc9758564f9d1a4dbce6b93b7fb763ba57530c7fc48316d/openai-1.86.0-py3-none-any.whl", hash = "sha256:c8889c39410621fe955c230cc4c21bfe36ec887f4e60a957de05f507d7e1f349", size = 730296 }, ] [[package]] @@ -791,7 +793,7 @@ wheels = [ [[package]] name = "pandas" -version = "2.2.3" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -799,28 +801,28 @@ dependencies = [ { name = "pytz" }, { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +sdist = { url = "https://files.pythonhosted.org/packages/72/51/48f713c4c728d7c55ef7444ba5ea027c26998d96d1a40953b346438602fc/pandas-2.3.0.tar.gz", hash = "sha256:34600ab34ebf1131a7613a260a61dbe8b62c188ec0ea4c296da7c9a06b004133", size = 4484490 } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, - { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, - { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, - { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 }, - { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, - { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, - { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, - { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, - { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, - { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, - { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, - { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, - { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, - { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, - { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, - { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, - { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, - { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, - { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, - { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, + { url = "https://files.pythonhosted.org/packages/94/46/24192607058dd607dbfacdd060a2370f6afb19c2ccb617406469b9aeb8e7/pandas-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2eb4728a18dcd2908c7fccf74a982e241b467d178724545a48d0caf534b38ebf", size = 11573865 }, + { url = "https://files.pythonhosted.org/packages/9f/cc/ae8ea3b800757a70c9fdccc68b67dc0280a6e814efcf74e4211fd5dea1ca/pandas-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9d8c3187be7479ea5c3d30c32a5d73d62a621166675063b2edd21bc47614027", size = 10702154 }, + { url = "https://files.pythonhosted.org/packages/d8/ba/a7883d7aab3d24c6540a2768f679e7414582cc389876d469b40ec749d78b/pandas-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ff730713d4c4f2f1c860e36c005c7cefc1c7c80c21c0688fd605aa43c9fcf09", size = 11262180 }, + { url = "https://files.pythonhosted.org/packages/01/a5/931fc3ad333d9d87b10107d948d757d67ebcfc33b1988d5faccc39c6845c/pandas-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba24af48643b12ffe49b27065d3babd52702d95ab70f50e1b34f71ca703e2c0d", size = 11991493 }, + { url = "https://files.pythonhosted.org/packages/d7/bf/0213986830a92d44d55153c1d69b509431a972eb73f204242988c4e66e86/pandas-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:404d681c698e3c8a40a61d0cd9412cc7364ab9a9cc6e144ae2992e11a2e77a20", size = 12470733 }, + { url = "https://files.pythonhosted.org/packages/a4/0e/21eb48a3a34a7d4bac982afc2c4eb5ab09f2d988bdf29d92ba9ae8e90a79/pandas-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6021910b086b3ca756755e86ddc64e0ddafd5e58e076c72cb1585162e5ad259b", size = 13212406 }, + { url = "https://files.pythonhosted.org/packages/1f/d9/74017c4eec7a28892d8d6e31ae9de3baef71f5a5286e74e6b7aad7f8c837/pandas-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:094e271a15b579650ebf4c5155c05dcd2a14fd4fdd72cf4854b2f7ad31ea30be", size = 10976199 }, + { url = "https://files.pythonhosted.org/packages/d3/57/5cb75a56a4842bbd0511c3d1c79186d8315b82dac802118322b2de1194fe/pandas-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c7e2fc25f89a49a11599ec1e76821322439d90820108309bf42130d2f36c983", size = 11518913 }, + { url = "https://files.pythonhosted.org/packages/05/01/0c8785610e465e4948a01a059562176e4c8088aa257e2e074db868f86d4e/pandas-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6da97aeb6a6d233fb6b17986234cc723b396b50a3c6804776351994f2a658fd", size = 10655249 }, + { url = "https://files.pythonhosted.org/packages/e8/6a/47fd7517cd8abe72a58706aab2b99e9438360d36dcdb052cf917b7bf3bdc/pandas-2.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb32dc743b52467d488e7a7c8039b821da2826a9ba4f85b89ea95274f863280f", size = 11328359 }, + { url = "https://files.pythonhosted.org/packages/2a/b3/463bfe819ed60fb7e7ddffb4ae2ee04b887b3444feee6c19437b8f834837/pandas-2.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:213cd63c43263dbb522c1f8a7c9d072e25900f6975596f883f4bebd77295d4f3", size = 12024789 }, + { url = "https://files.pythonhosted.org/packages/04/0c/e0704ccdb0ac40aeb3434d1c641c43d05f75c92e67525df39575ace35468/pandas-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1d2b33e68d0ce64e26a4acc2e72d747292084f4e8db4c847c6f5f6cbe56ed6d8", size = 12480734 }, + { url = "https://files.pythonhosted.org/packages/e9/df/815d6583967001153bb27f5cf075653d69d51ad887ebbf4cfe1173a1ac58/pandas-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:430a63bae10b5086995db1b02694996336e5a8ac9a96b4200572b413dfdfccb9", size = 13223381 }, + { url = "https://files.pythonhosted.org/packages/79/88/ca5973ed07b7f484c493e941dbff990861ca55291ff7ac67c815ce347395/pandas-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4930255e28ff5545e2ca404637bcc56f031893142773b3468dc021c6c32a1390", size = 10970135 }, + { url = "https://files.pythonhosted.org/packages/24/fb/0994c14d1f7909ce83f0b1fb27958135513c4f3f2528bde216180aa73bfc/pandas-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f925f1ef673b4bd0271b1809b72b3270384f2b7d9d14a189b12b7fc02574d575", size = 12141356 }, + { url = "https://files.pythonhosted.org/packages/9d/a2/9b903e5962134497ac4f8a96f862ee3081cb2506f69f8e4778ce3d9c9d82/pandas-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78ad363ddb873a631e92a3c063ade1ecfb34cae71e9a2be6ad100f875ac1042", size = 11474674 }, + { url = "https://files.pythonhosted.org/packages/81/3a/3806d041bce032f8de44380f866059437fb79e36d6b22c82c187e65f765b/pandas-2.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951805d146922aed8357e4cc5671b8b0b9be1027f0619cea132a9f3f65f2f09c", size = 11439876 }, + { url = "https://files.pythonhosted.org/packages/15/aa/3fc3181d12b95da71f5c2537c3e3b3af6ab3a8c392ab41ebb766e0929bc6/pandas-2.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a881bc1309f3fce34696d07b00f13335c41f5f5a8770a33b09ebe23261cfc67", size = 11966182 }, + { url = "https://files.pythonhosted.org/packages/37/e7/e12f2d9b0a2c4a2cc86e2aabff7ccfd24f03e597d770abfa2acd313ee46b/pandas-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1991bbb96f4050b09b5f811253c4f3cf05ee89a589379aa36cd623f21a31d6f", size = 12547686 }, + { url = "https://files.pythonhosted.org/packages/39/c2/646d2e93e0af70f4e5359d870a63584dacbc324b54d73e6b3267920ff117/pandas-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bb3be958022198531eb7ec2008cfc78c5b1eed51af8600c6c5d9160d89d8d249", size = 13231847 }, ] [[package]] @@ -883,7 +885,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.4" +version = "2.11.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -891,9 +893,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900 }, + { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229 }, ] [[package]] @@ -996,63 +998,51 @@ wheels = [ ] [[package]] -name = "pyqt6" -version = "6.9.0" +name = "pyside6" +version = "6.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyqt6-qt6" }, - { name = "pyqt6-sip" }, + { name = "pyside6-addons" }, + { name = "pyside6-essentials" }, + { name = "shiboken6" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/de/102e8e66149085acf38bbf01df572a2cd53259bcd99b7d8ecef0d6b36172/pyqt6-6.9.0.tar.gz", hash = "sha256:6a8ff8e3cd18311bb7d937f7d741e787040ae7ff47ce751c28a94c5cddc1b4e6", size = 1066831 } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/e5/f9e2b5326d6103bce4894a969be54ce3be4b0a7a6ff848228e6a61a9993f/PyQt6-6.9.0-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:5344240747e81bde1a4e0e98d4e6e2d96ad56a985d8f36b69cd529c1ca9ff760", size = 12257215 }, - { url = "https://files.pythonhosted.org/packages/ed/3a/bcc7687c5a11079bbd1606a015514562f2ac8cb01c5e3e4a3b30fcbdad36/PyQt6-6.9.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e344868228c71fc89a0edeb325497df4ff731a89cfa5fe57a9a4e9baecc9512b", size = 8259731 }, - { url = "https://files.pythonhosted.org/packages/e1/47/13ab0b916b5bad07ab04767b412043f5c1ca206bf38a906b1d8d5c520a98/PyQt6-6.9.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:1cbc5a282454cf19691be09eadbde019783f1ae0523e269b211b0173b67373f6", size = 8207593 }, - { url = "https://files.pythonhosted.org/packages/d1/a8/955cfd880f2725a218ee7b272c005658e857e9224823d49c32c93517f6d9/PyQt6-6.9.0-cp39-abi3-win_amd64.whl", hash = "sha256:d36482000f0cd7ce84a35863766f88a5e671233d5f1024656b600cd8915b3752", size = 6748279 }, - { url = "https://files.pythonhosted.org/packages/9f/38/586ce139b1673a27607f7b85c594878e1bba215abdca3de67732b463f7b2/PyQt6-6.9.0-cp39-abi3-win_arm64.whl", hash = "sha256:0c8b7251608e05b479cfe731f95857e853067459f7cbbcfe90f89de1bcf04280", size = 5478122 }, + { url = "https://files.pythonhosted.org/packages/14/91/8e9c7f7e90431297de9856e90a156ade9420977e26d87996909c63f30bd2/PySide6-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:f843ef39970a2f79757810fffd7b8e93ac42a3de9ea62f2a03648cde57648aed", size = 558097 }, + { url = "https://files.pythonhosted.org/packages/d7/ff/04d1b6b30edd24d761cc30d964860f997bdf37d06620694bf9aab35eec3a/PySide6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:db44ac08b8f7ac1b421bc1c6a44200d03f08d80dc7b3f68dfdb1684f30f41c17", size = 558239 }, + { url = "https://files.pythonhosted.org/packages/3c/b4/ca076c55c11a8e473363e05aa82c5c03dd7ba8f17b77cc9311ce17213193/PySide6-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:531a6e67c429b045674d57fe9864b711eb59e4cded753c2640982e368fd468d1", size = 558239 }, + { url = "https://files.pythonhosted.org/packages/83/ff/95c941f53b0faebc27dbe361d8e971b77f504b9cf36f8f5d750fd82cd6fc/PySide6-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:c82dbb7d32bbdd465e01059174f71bddc97de152ab71bded3f1907c40f9a5f16", size = 564571 }, + { url = "https://files.pythonhosted.org/packages/d1/ef/0aa5e910fa4e9770db6b45c23e360a52313922e0ca71fc060a57db613de1/PySide6-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:1525d63dc6dc425b8c2dc5bc01a8cb1d67530401449f3a3490c09a14c095b9f9", size = 401793 }, ] [[package]] -name = "pyqt6-qt6" -version = "6.9.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/11/8c450442bf4702ed810689a045f9c5d9236d709163886f09374fd8d84143/PyQt6_Qt6-6.9.0-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:b1c4e4a78f0f22fbf88556e3d07c99e5ce93032feae5c1e575958d914612e0f9", size = 66804297 }, - { url = "https://files.pythonhosted.org/packages/6e/be/191ba4402c24646f6b98c326ff0ee22e820096c69e67ba5860a687057616/PyQt6_Qt6-6.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d3875119dec6bf5f799facea362aa0ad39bb23aa9654112faa92477abccb5ff", size = 60943708 }, - { url = "https://files.pythonhosted.org/packages/0f/70/ec018b6e979b3914c984e5ab7e130918930d5423735ac96c70c328227b9b/PyQt6_Qt6-6.9.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:9c0e603c934e4f130c110190fbf2c482ff1221a58317266570678bc02db6b152", size = 81846956 }, - { url = "https://files.pythonhosted.org/packages/ac/ed/2d78cd08be415a21dac2e7277967b90b0c05afc4782100f0a037447bb1c6/PyQt6_Qt6-6.9.0-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:cf840e8ae20a0704e0343810cf0e485552db28bf09ea976e58ec0e9b7bb27fcd", size = 80295982 }, - { url = "https://files.pythonhosted.org/packages/6e/24/6b6168a75c7b6a55b9f6b5c897e6164ec15e94594af11a6f358c49845442/PyQt6_Qt6-6.9.0-py3-none-win_amd64.whl", hash = "sha256:c825a6f5a9875ef04ef6681eda16aa3a9e9ad71847aa78dfafcf388c8007aa0a", size = 73652485 }, - { url = "https://files.pythonhosted.org/packages/44/fd/1238931df039e46e128d53974c0cfc9d34da3d54c5662bd589fe7b0a67c2/PyQt6_Qt6-6.9.0-py3-none-win_arm64.whl", hash = "sha256:1188f118d1c570d27fba39707e3d8a48525f979816e73de0da55b9e6fa9ad0a1", size = 49568913 }, -] - -[[package]] -name = "pyqt6-sip" -version = "13.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/18/0405c54acba0c8e276dd6f0601890e6e735198218d031a6646104870fe22/pyqt6_sip-13.10.0.tar.gz", hash = "sha256:d6daa95a0bd315d9ec523b549e0ce97455f61ded65d5eafecd83ed2aa4ae5350", size = 92464 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/81/66d9bdacb790592a0641378749a047f12e3b254cdc2cb51f7ed636cf01d2/PyQt6_sip-13.10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:48791db2914fc39c3218519a02d2a5fd3fcd354a1be3141a57bf2880701486f2", size = 112334 }, - { url = "https://files.pythonhosted.org/packages/26/2c/4796c209009a018e0d4a5c406d5a519234c5a378f370dc679d0ad5f455b2/PyQt6_sip-13.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:466d6b4791973c9fcbdc2e0087ed194b9ea802a8c3948867a849498f0841c70c", size = 322334 }, - { url = "https://files.pythonhosted.org/packages/99/34/2ec54bd475f0a811df1d32be485f2344cf9e8b388ce7adb26b46ce5552d4/PyQt6_sip-13.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ae15358941f127cd3d1ab09c1ebd45c4dabb0b2e91587b9eebde0279d0039c54", size = 303798 }, - { url = "https://files.pythonhosted.org/packages/0c/e4/82099bb4ab8bc152b5718541e93c0b3adf7566c0f307c9e58e2368b8c517/PyQt6_sip-13.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad573184fa8b00041944e5a17d150ab0d08db2d2189e39c9373574ebab3f2e58", size = 53569 }, - { url = "https://files.pythonhosted.org/packages/e3/09/90e0378887a3cb9664da77061229cf8e97e6ec25a5611b7dbc9cc3e02c78/PyQt6_sip-13.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:2d579d810d0047d40bde9c6aef281d6ed218db93c9496ebc9e55b9e6f27a229d", size = 45430 }, - { url = "https://files.pythonhosted.org/packages/6b/0c/8d1de48b45b565a46bf4757341f13f9b1853a7d2e6b023700f0af2c213ab/PyQt6_sip-13.10.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7b6e250c2e7c14702a623f2cc1479d7fb8db2b6eee9697cac10d06fe79c281bb", size = 112343 }, - { url = "https://files.pythonhosted.org/packages/af/13/e2cc2b667a9f5d44c2d0e18fa6e1066fca3f4521dcb301f4b5374caeb33e/PyQt6_sip-13.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fcb30756568f8cd59290f9ef2ae5ee3e72ff9cdd61a6f80c9e3d3b95ae676be", size = 322527 }, - { url = "https://files.pythonhosted.org/packages/20/1a/5c6fcae85edb65cf236c9dc6d23b279b5316e94cdca1abdee6d0a217ddbb/PyQt6_sip-13.10.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:757ac52c92b2ef0b56ecc7cd763b55a62d3c14271d7ea8d03315af85a70090ff", size = 303407 }, - { url = "https://files.pythonhosted.org/packages/b9/db/6924ec985be7d746772806b96ab81d24263ef72f0249f0573a82adaed75e/PyQt6_sip-13.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:571900c44a3e38738d696234d94fe2043972b9de0633505451c99e2922cb6a34", size = 53580 }, - { url = "https://files.pythonhosted.org/packages/77/c3/9e44729b582ee7f1d45160e8c292723156889f3e38ce6574f88d5ab8fa02/PyQt6_sip-13.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:39cba2cc71cf80a99b4dc8147b43508d4716e128f9fb99f5eb5860a37f082282", size = 45446 }, -] - -[[package]] -name = "pyqtgraph" -version = "0.13.7" +name = "pyside6-addons" +version = "6.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, + { name = "pyside6-essentials" }, + { name = "shiboken6" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/d9/b62d5cddb3caa6e5145664bee5ed90223dee23ca887ed3ee479f2609e40a/pyqtgraph-0.13.7.tar.gz", hash = "sha256:64f84f1935c6996d0e09b1ee66fe478a7771e3ca6f3aaa05f00f6e068321d9e3", size = 2343380 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/34/5702b3b7cafe99be1d94b42f100e8cc5e6957b761fcb1cf5f72d492851da/pyqtgraph-0.13.7-py3-none-any.whl", hash = "sha256:7754edbefb6c367fa0dfb176e2d0610da3ada20aa7a5318516c74af5fb72bf7a", size = 1925473 }, + { url = "https://files.pythonhosted.org/packages/e7/e2/39b9e04335d7ac782b6459bf7abec90c36b8efaac5a88ef818e972c59387/PySide6_Addons-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:7be0708fa89715c282541fca47e2ba97c0c8d2886e0236ef994b2dd8f52aacdd", size = 316212438 }, + { url = "https://files.pythonhosted.org/packages/cf/6f/691d7039a6f7943522a770b713ecd85fa169688dfdd65ddd4db1699d01b6/PySide6_Addons-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:da7869b02e3599d26546fad582db4656060786bc5ec8ece5ec9ee8aa8b42371c", size = 166690468 }, + { url = "https://files.pythonhosted.org/packages/9d/08/a264db09ad35819643d910cd4c73a86f72f23b7092f8ebc7e51dcca53a86/PySide6_Addons-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:53fd08c8152b6ba8c435458afd189835ba905793a5077a2bb0b1b11222b375d4", size = 162466096 }, + { url = "https://files.pythonhosted.org/packages/84/be/a849402f7e73d137b5ae8b4370a49b0cf0e0c02f028b845782cb743e4995/PySide6_Addons-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:cd93a3a5e3886cd958f3a5acc7c061c24f10a394ce9f4ce657ac394544ca7ec2", size = 143150906 }, + { url = "https://files.pythonhosted.org/packages/2a/f1/1bb6b5859aff4e2b3f5ef789b9cee200811a9f469f04d9aa7425e816622b/PySide6_Addons-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:4f589631bdceb518080ae9c9fa288e64f092cd5bebe25adc8ad89e8eadd4db29", size = 26938762 }, +] + +[[package]] +name = "pyside6-essentials" +version = "6.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "shiboken6" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/59/714874db9ef3bbbbda654fd3223248969bea02ec1a5bfdd1c941c4e97749/PySide6_Essentials-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:ed43435a70e018e1c22efcaf34a9430b83cfcad716dba661b03de21c13322fab", size = 132957077 }, + { url = "https://files.pythonhosted.org/packages/59/6a/ea0db68d40a1c487fd255634896f4e37b6560e3ef1f57ca5139bf6509b1f/PySide6_Essentials-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e5da48883f006c6206ef85874db74ddebcdf69b0281bd4f1642b1c5ac1d54aea", size = 96416183 }, + { url = "https://files.pythonhosted.org/packages/5b/2f/4243630d1733522638c4967d36018c38719d8b84f5246bf3d4c010e0aa9d/PySide6_Essentials-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:e46a2801c9c6098025515fd0af6c594b9e9c951842f68b8f6f3da9858b9b26c2", size = 94171343 }, + { url = "https://files.pythonhosted.org/packages/0d/a9/a8e0209ba9116f2c2db990cfb79f2edbd5a3a428013be2df1f1cddd660a9/PySide6_Essentials-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:ad1ac94011492dba33051bc33db1c76a7d6f815a81c01422cb6220273b369145", size = 72435676 }, + { url = "https://files.pythonhosted.org/packages/d0/e4/23268c57e775a1a4d2843d288a9583a47f2e4b3977a9ae93cb9ded1a4ea5/PySide6_Essentials-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:35c2c2bb4a88db74d11e638cf917524ff35785883f10b439ead07960a5733aa4", size = 49483707 }, ] [[package]] @@ -1139,14 +1129,14 @@ wheels = [ [[package]] name = "pyyaml-env-tag" -version = "0.1" +version = "1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722 }, ] [[package]] @@ -1184,7 +1174,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/ab/38/ff60c8fc9e002d50d [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1192,9 +1182,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847 }, ] [[package]] @@ -1212,16 +1202,16 @@ wheels = [ [[package]] name = "rich-click" -version = "1.8.8" +version = "1.8.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/7a/4b78c5997f2a799a8c5c07f3b2145bbcda40115c4d35c76fbadd418a3c89/rich_click-1.8.8.tar.gz", hash = "sha256:547c618dea916620af05d4a6456da797fbde904c97901f44d2f32f89d85d6c84", size = 39066 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/a8/dcc0a8ec9e91d76ecad9413a84b6d3a3310c6111cfe012d75ed385c78d96/rich_click-1.8.9.tar.gz", hash = "sha256:fd98c0ab9ddc1cf9c0b7463f68daf28b4d0033a74214ceb02f761b3ff2af3136", size = 39378 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/69/963f0bf44a654f6465bdb66fb5a91051b0d7af9f742b5bd7202607165036/rich_click-1.8.8-py3-none-any.whl", hash = "sha256:205aabd5a98e64ab2c105dee9e368be27480ba004c7dfa2accd0ed44f9f1550e", size = 35747 }, + { url = "https://files.pythonhosted.org/packages/b6/c2/9fce4c8a9587c4e90500114d742fe8ef0fd92d7bad29d136bb9941add271/rich_click-1.8.9-py3-none-any.whl", hash = "sha256:c3fa81ed8a671a10de65a9e20abf642cfdac6fdb882db1ef465ee33919fbcfe2", size = 36082 }, ] [[package]] @@ -1245,8 +1235,7 @@ dependencies = [ { name = "openai" }, { name = "pandas" }, { name = "playwright" }, - { name = "pyqt6" }, - { name = "pyqtgraph" }, + { name = "pyside6" }, { name = "python-docx" }, { name = "pyzotero" }, { name = "ratelimit" }, @@ -1278,8 +1267,7 @@ requires-dist = [ { name = "openai", specifier = ">=1.79.0" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "playwright", specifier = ">=1.49.1" }, - { name = "pyqt6", specifier = ">=6.8.0" }, - { name = "pyqtgraph", specifier = ">=0.13.7" }, + { name = "pyside6", specifier = ">=6.9.1" }, { name = "python-docx", specifier = ">=1.1.2" }, { name = "pyzotero", specifier = ">=1.6.4" }, { name = "ratelimit", specifier = ">=2.2.1" }, @@ -1299,6 +1287,18 @@ version = "1.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750 } +[[package]] +name = "shiboken6" +version = "6.9.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/98/34d4d25b79055959b171420d47fcc10121aefcbb261c91d5491252830e31/shiboken6-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:40e92afc88da06b5100c56b761e59837ff282166e9531268f3d910b6128e621e", size = 406159 }, + { url = "https://files.pythonhosted.org/packages/5a/07/53b2532ecd42ff925feb06b7bb16917f5f99f9c3470f0815c256789d818b/shiboken6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efcdfa8655d34aaf8d7a0c7724def3440bd46db02f5ad3b1785db5f6ccb0a8ff", size = 206756 }, + { url = "https://files.pythonhosted.org/packages/5e/b0/75b86ee3f7b044e6a87fbe7abefd1948ca4ae5fcde8321f4986a1d9eaa5e/shiboken6-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:efcf75d48a29ae072d0bf54b3cd5a59ae91bb6b3ab7459e17c769355486c2e0b", size = 203233 }, + { url = "https://files.pythonhosted.org/packages/30/56/00af281275aab4c79e22e0ea65feede0a5c6da3b84e86b21a4a0071e0744/shiboken6-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:209ccf02c135bd70321143dcbc5023ae0c056aa4850a845955dd2f9b2ff280a9", size = 1153587 }, + { url = "https://files.pythonhosted.org/packages/de/ce/6ccd382fbe1a96926c5514afa6f2c42da3a9a8482e61f8dfc6068a9ca64f/shiboken6-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:2a39997ce275ced7853defc89d3a1f19a11c90991ac6eef3435a69bb0b7ff1de", size = 1831623 }, +] + [[package]] name = "six" version = "1.17.0" @@ -1328,11 +1328,11 @@ wheels = [ [[package]] name = "tomlkit" -version = "0.13.2" +version = "0.13.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901 }, ] [[package]] @@ -1349,23 +1349,23 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.13.2" +version = "4.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, ] [[package]] name = "typing-inspection" -version = "0.4.0" +version = "0.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, ] [[package]] From 86849b67f558ff1505bbfeb3026a4b4eb6bd14d5 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 18 Jun 2025 13:32:59 +0200 Subject: [PATCH 17/42] rework semester class using chatgpt - fixes offset bug, implements generate_missing function --- src/backend/semester.py | 286 +++++++++++++++++++++++++++------------- 1 file changed, 191 insertions(+), 95 deletions(-) diff --git a/src/backend/semester.py b/src/backend/semester.py index 392e593..0f24a9e 100644 --- a/src/backend/semester.py +++ b/src/backend/semester.py @@ -1,5 +1,22 @@ -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 @@ -11,136 +28,215 @@ 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 "" - 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 " → SoSe of year YY + "WiSe /" → Winter term starting in YY + "WiSe " → 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)) From 3d164898bfaf56175f763eeefac007f9d9aedf8e Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Fri, 20 Jun 2025 09:09:28 +0200 Subject: [PATCH 18/42] rework graph to include semester generation, fix bug where y-values were extracted wrong --- src/ui/widgets/graph.py | 54 +++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/ui/widgets/graph.py b/src/ui/widgets/graph.py index 013916b..4907652 100644 --- a/src/ui/widgets/graph.py +++ b/src/ui/widgets/graph.py @@ -10,6 +10,7 @@ from PySide6.QtGui import QPainter, QPen, QColor from PySide6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis, QCategoryAxis from src import LOG_DIR +from src.backend.semester import Semester log = loguru.logger log.remove() @@ -37,7 +38,7 @@ class DataQtGraph(QtWidgets.QWidget): def __init__( self, title: str, - data: dict, + data: dict[str, Union[list[str], dict[str, list[int]]]], generateMissing: bool, y_label: str, x_rotation: int = 90, @@ -53,16 +54,46 @@ class DataQtGraph(QtWidgets.QWidget): lst = [] if generateMissing: + s_start = Semester.from_string(data["x"][0]) + s_end = Semester.from_string(data["x"][-1]) + # generate all semesters from start to end + missing_semesters = Semester.generate_missing(s_start, s_end) x_data = data["x"] y_data = data["y"] if not isinstance(y_data, list): for key in y_data: - data = {"x": x_data, "y": y_data[key]} - data = self.generateMissingSemesters(data) + data = {"x": [], "y": []} data["y-label"] = key + for semester in missing_semesters: + if semester not in x_data: + data["x"].append(semester) + data["y"].append(0) + data["x"].append(semester) + # get the index of the semester in x_data + if semester in x_data: + index = x_data.index(semester) + print("index:", index) + print(key, y_data[key]) + data["y"].append(y_data[key][index]) lst.append(data) + # for key in y_data: + # data = {"x": x_data, "y": y_data[key]} + # data = self.generateMissingSemesters(data) + # data["y-label"] = key else: - data = self.generateMissingSemesters(data) + # data = self.generateMissingSemesters(data) + data = {"x": [], "y": []} + for semester in missing_semesters: + # if semester not in x_data, set y to 0 + if semester not in x_data: + data["x"].append(semester) + data["y"].append(0) + data["x"].append(semester) + # get the index of the semester in x_data + if semester in x_data: + index = x_data.index(semester) + data["y"].append(y_data[index]) + data["y-label"] = y_label lst.append(data) else: @@ -76,9 +107,6 @@ class DataQtGraph(QtWidgets.QWidget): else: lst.append(data) x_data = lst[0]["x"] # - xdict = dict(enumerate(x_data)) - - print("xdict:", xdict) self.chart.createDefaultAxes() for entry in lst: @@ -88,6 +116,16 @@ class DataQtGraph(QtWidgets.QWidget): # entryseries.append(entry["x"].index(x_val), y_val) entryseries.setName(entry["y-label"] if "y-label" in entry else y_label) + entryseries.setPen( + QPen( + QColor( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + ), + 2, + ) + ) self.chart.addSeries(entryseries) @@ -106,7 +144,7 @@ class DataQtGraph(QtWidgets.QWidget): # str() # ) self.chart.legend().setVisible(True) - self.chart.legend().setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom) + self.chart.legend().setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) # set legend labels self.chart.setAxisY(QValueAxis(self.chart), entryseries) From c06ff40fd675dc46cd19085dfcd4c334794a7530 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 24 Jun 2025 13:46:08 +0200 Subject: [PATCH 19/42] fix issue with database path falling back to faulty path update logic in welcome wizard, add checks for qapplication instance --- config/config.py | 6 +- src/__init__.py | 5 + src/backend/admin_console.py | 24 +- src/backend/database.py | 41 +- src/ui/dialogs/login.py | 4 +- src/ui/userInterface.py | 8 +- src/ui/widgets/welcome_wizard.py | 110 ++- .../widgets/widget_sources/welcome_wizard.ui | 4 +- .../widget_sources/welcome_wizard_ui.py | 671 ++++++++++-------- 9 files changed, 515 insertions(+), 358 deletions(-) diff --git a/config/config.py b/config/config.py index 5f1324e..0eb5564 100644 --- a/config/config.py +++ b/config/config.py @@ -1,4 +1,4 @@ -from typing import Optional, Any +from typing import Optional, Any, Union from dataclasses import dataclass from omegaconf import OmegaConf, DictConfig import os @@ -31,8 +31,8 @@ class Zotero: @dataclass class Database: name: str - path: str | Path - temp: str | Path + path: Union[str, Path, None] + temp: Union[str, Path, None] def getattr(self, name: str): return getattr(self, name) diff --git a/src/__init__.py b/src/__init__.py index 1fcb8ef..daeaf7a 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -19,6 +19,11 @@ if not os.path.exists(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) diff --git a/src/backend/admin_console.py b/src/backend/admin_console.py index c65bfa8..69f6006 100644 --- a/src/backend/admin_console.py +++ b/src/backend/admin_console.py @@ -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.critical("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,7 +57,7 @@ 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"): + def create_user(self, username: str, password: str, role: str = "user") -> bool: """Create a new user in the database. Args: @@ -53,9 +66,10 @@ class AdminCommands: role (str, optional): the role of the user to be created. Defaults to "user". """ hashed_password, salt = self.create_password(password) - self.db.createUser( + 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. diff --git a/src/backend/database.py b/src/backend/database.py index 2baecee..bce9241 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -12,7 +12,7 @@ from typing import Any, List, Optional, Tuple, Union import loguru -from src import LOG_DIR, settings +from src import LOG_DIR, settings, DATABASE_DIR from src.backend.db import ( CREATE_ELSA_FILES_TABLE, CREATE_ELSA_MEDIA_TABLE, @@ -44,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: Path = None): + def __init__(self, db_path: Union[Path, None] = None): """ Default constructor for the database class @@ -57,15 +56,32 @@ class Database: db_path (str, optional): Optional Path for testing / specific purposes. Defaults to None. """ if db_path is None: - self.db_path = Path(self.database.path.expanduser(), self.database.name) + 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())) - log.debug(self.db_path) 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.expanduser() + 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): @@ -509,7 +525,7 @@ class Database: str: The filename of the recreated file """ blob = self.getBlob(filename, app_id) - tempdir = self.database.temp.expanduser() + tempdir = settings.database.temp.expanduser() if not tempdir.exists(): tempdir.mkdir(parents=True, exist_ok=True) file = tempfile.NamedTemporaryFile( @@ -1280,6 +1296,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 @@ -1467,7 +1490,7 @@ class Database: "SELECT fileblob FROM elsa_files WHERE filename=?", (filename,), one=True )[0] # log.debug(blob) - tempdir = self.database.temp.expanduser() + tempdir = settings.database.temp.expanduser() if not tempdir.exists(): tempdir.mkdir(parents=True, exist_ok=True) diff --git a/src/ui/dialogs/login.py b/src/ui/dialogs/login.py index b7b820b..cc8fa34 100644 --- a/src/ui/dialogs/login.py +++ b/src/ui/dialogs/login.py @@ -5,7 +5,6 @@ import loguru from PySide6 import QtCore, QtWidgets from src import LOG_DIR, Icon -from src.backend.admin_console import AdminCommands from src.backend.database import Database from .dialog_sources.login_ui import Ui_Dialog @@ -51,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) @@ -75,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 diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index b6b8fc1..81015d4 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -66,7 +66,7 @@ log.add( rotation="1 day", retention="1 month", ) - +log.critical("UI started") valid_input = (0, 0, 0, 0, 0, 0) @@ -1812,7 +1812,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) diff --git a/src/ui/widgets/welcome_wizard.py b/src/ui/widgets/welcome_wizard.py index f72e6e3..571ce01 100644 --- a/src/ui/widgets/welcome_wizard.py +++ b/src/ui/widgets/welcome_wizard.py @@ -1,14 +1,25 @@ +from typing import Any from .widget_sources.welcome_wizard_ui import Ui_Wizard from PySide6 import QtWidgets, QtCore, QtGui -from src import settings -from src.backend import AdminCommands +from src import settings, LOG_DIR +from src.backend import Database +import sys from appdirs import AppDirs +from pathlib import Path +import loguru appdirs = AppDirs("SemesterApparatsManager", "SAM") +log = loguru.logger +log.remove() +log.add(sys.stdout, level="INFO") +log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days") + + class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): def __init__(self, parent=None): super().__init__(parent) + log.info("Initializing WelcomeWizard") self.setupUi(self) self.btn_database.clicked.connect(self.open_database_settings) self.btn_temp.clicked.connect(self.open_temp_settings) @@ -16,49 +27,102 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): self.btn_create.clicked.connect(self.create_sam_user) # mail password field self.settings_mail_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) + self.settings_zotero_api_key.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) + self.settings_openai_api_key.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) + self.sam_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) + # allow user to toggle password visibility self.settings_mail_password.setContextMenuPolicy( QtCore.Qt.ContextMenuPolicy.CustomContextMenu ) + self.settings_zotero_api_key.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.CustomContextMenu + ) + self.settings_openai_api_key.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.CustomContextMenu + ) + self.sam_password.setContextMenuPolicy( + QtCore.Qt.ContextMenuPolicy.CustomContextMenu + ) self.settings_mail_password.customContextMenuRequested.connect( - self.toggle_password_visibility + lambda pos: self.toggle_password_visibility( + pos, self.settings_mail_password + ) + ) + self.settings_zotero_api_key.customContextMenuRequested.connect( + lambda pos: self.toggle_password_visibility( + pos, self.settings_zotero_api_key + ) + ) + self.settings_openai_api_key.customContextMenuRequested.connect( + lambda pos: self.toggle_password_visibility( + pos, self.settings_openai_api_key + ) + ) + self.sam_password.customContextMenuRequested.connect( + lambda pos: self.toggle_password_visibility(pos, self.sam_password) ) # if button for next page is clicked, run function to store settings - self.button(QtWidgets.QWizard.WizardButton.FinishButton).clicked.connect( + self.button(QtWidgets.QWizard.WizardButton.NextButton).clicked.connect( self.store_settings ) self.settings_mail_use_user_name.toggled.connect(self.set_check_text) # set initial values for checkbox, database self.set_check_text(self.settings_mail_use_user_name.isChecked()) self.settings_database.setText( - str(settings.database.path) or str(appdirs.user_data_dir) + str(settings.database.path) + if settings.database.path is not None + else str(appdirs.user_data_dir) ) self.settings_temp.setText( - str(settings.database.temp) or str(appdirs.user_cache_dir) + str(settings.database.temp) + if settings.database.temp is not None + else str(appdirs.user_cache_dir) ) + self.settings_database_name.setText("semesterapparate.db") def test_login_data(self): - sam_users = AdminCommands().list_users() + from src.backend import AdminCommands + + log.info("Testing login data for SAM user") + db_path = ( + self.settings_database.text() + "/" + self.settings_database_name.text() + ) + db_path = Path(db_path).expanduser() + db = Database(db_path=db_path) + db.overwritePath(db_path) + db.initializeDatabase() + sam_users = AdminCommands(db_path=db_path).list_users() sam_users = [user[2] for user in sam_users] - if not sam_users: - QtWidgets.QMessageBox.critical( - self, - "Error", - "No SAM users found. Please create a SAM user first.", - ) - return + username = self.sam_username.text() if username in sam_users: QtWidgets.QMessageBox.information( self, - "Error", + "Information", f"User '{username}' exists in SAM.", ) + else: + QtWidgets.QMessageBox.information( + self, + "Information", + f"User '{username}' does not exist in SAM.", + ) def create_sam_user(self): """Create a SAM user in the database.""" - admin = AdminCommands() + from src.backend import AdminCommands + + db_path = ( + self.settings_database.text() + "/" + self.settings_database_name.text() + ) + db_path = Path(db_path).expanduser() + db = Database(db_path=db_path) + db.overwritePath(db_path) + db.initializeDatabase() + admin = AdminCommands(db_path=db_path) + admin.create_admin() if not admin.create_user(self.sam_username.text(), self.sam_password.text()): QtWidgets.QMessageBox.critical( self, @@ -79,17 +143,12 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): else: self.settings_mail_use_user_name.setText("Nutzername wird nicht verwendet") - def toggle_password_visibility(self, pos): + def toggle_password_visibility(self, pos: Any, field): """Toggle the visibility of the password field.""" - if ( - self.settings_mail_password.echoMode() - == QtWidgets.QLineEdit.EchoMode.Password - ): - self.settings_mail_password.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) + if field.echoMode() == QtWidgets.QLineEdit.EchoMode.Password: + field.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) else: - self.settings_mail_password.setEchoMode( - QtWidgets.QLineEdit.EchoMode.Password - ) + field.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) def store_settings(self): # database page @@ -135,7 +194,6 @@ class WelcomeWizard(QtWidgets.QWizard, Ui_Wizard): settings.set_openai_attr("model", openai_model) # save settings to file print("Saving settings...") - print(settings) settings.save() def open_database_settings(self): diff --git a/src/ui/widgets/widget_sources/welcome_wizard.ui b/src/ui/widgets/widget_sources/welcome_wizard.ui index 0f1b382..e774473 100644 --- a/src/ui/widgets/widget_sources/welcome_wizard.ui +++ b/src/ui/widgets/widget_sources/welcome_wizard.ui @@ -23,7 +23,7 @@ Qt::PlainText - 5 + 1 @@ -78,7 +78,7 @@ li.checked::marker { content: "\2612"; } Datenbank - Hier werden die Einstellungen für die Datenbank und temporären Dateien festgelegt. Über den [...] Knopf können die Datenbank und der Speicherort für die temporären Daten festgelegt werden + Hier werden die Einstellungen für die Datenbank und temporären Dateien festgelegt. Über den [...] Knopf können die Speicherordner ausgewählt werden. 1 diff --git a/src/ui/widgets/widget_sources/welcome_wizard_ui.py b/src/ui/widgets/widget_sources/welcome_wizard_ui.py index eaf5851..1d87792 100644 --- a/src/ui/widgets/widget_sources/welcome_wizard_ui.py +++ b/src/ui/widgets/widget_sources/welcome_wizard_ui.py @@ -1,187 +1,245 @@ -# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\welcome_wizard.ui' -# -# 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. +# -*- coding: utf-8 -*- +################################################################################ +## Form generated from reading UI file 'welcome_wizard.ui' +## +## Created by: Qt User Interface Compiler version 6.9.1 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ -from PySide6 import QtCore, QtGui, QtWidgets - +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QFormLayout, + QGridLayout, QHBoxLayout, QLabel, QLineEdit, + QPushButton, QSizePolicy, QSpacerItem, QTextEdit, + QToolButton, QVBoxLayout, QWidget, QWizard, + QWizardPage) class Ui_Wizard(object): def setupUi(self, Wizard): - Wizard.setObjectName("Wizard") + if not Wizard.objectName(): + Wizard.setObjectName(u"Wizard") Wizard.resize(564, 425) Wizard.setSizeGripEnabled(False) - Wizard.setWizardStyle(QtWidgets.QWizard.WizardStyle.ClassicStyle) - Wizard.setSubTitleFormat(QtCore.Qt.TextFormat.PlainText) - Wizard.setCurrentId(5) - self.wizardPage1 = QtWidgets.QWizardPage() - self.wizardPage1.setSubTitle("") - self.wizardPage1.setObjectName("wizardPage1") - self.verticalLayout = QtWidgets.QVBoxLayout(self.wizardPage1) - self.verticalLayout.setObjectName("verticalLayout") - self.textEdit = QtWidgets.QTextEdit(parent=self.wizardPage1) + Wizard.setWizardStyle(QWizard.ClassicStyle) + Wizard.setSubTitleFormat(Qt.PlainText) + Wizard.setCurrentId(1) + self.wizardPage1 = QWizardPage() + self.wizardPage1.setObjectName(u"wizardPage1") + self.verticalLayout = QVBoxLayout(self.wizardPage1) + self.verticalLayout.setObjectName(u"verticalLayout") + self.textEdit = QTextEdit(self.wizardPage1) + self.textEdit.setObjectName(u"textEdit") self.textEdit.setEnabled(False) - self.textEdit.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + self.textEdit.setFocusPolicy(Qt.NoFocus) self.textEdit.setLineWidth(0) - self.textEdit.setAutoFormatting(QtWidgets.QTextEdit.AutoFormattingFlag.AutoAll) + self.textEdit.setAutoFormatting(QTextEdit.AutoAll) self.textEdit.setReadOnly(True) - self.textEdit.setObjectName("textEdit") + self.verticalLayout.addWidget(self.textEdit) - Wizard.addPage(self.wizardPage1) - self.wizardPage2 = QtWidgets.QWizardPage() - self.wizardPage2.setObjectName("wizardPage2") - self.gridLayout = QtWidgets.QGridLayout(self.wizardPage2) - self.gridLayout.setObjectName("gridLayout") - self.settings_temp = QtWidgets.QLineEdit(parent=self.wizardPage2) - self.settings_temp.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + + Wizard.setPage(0, self.wizardPage1) + self.wizardPage2 = QWizardPage() + self.wizardPage2.setObjectName(u"wizardPage2") + self.gridLayout = QGridLayout(self.wizardPage2) + self.gridLayout.setObjectName(u"gridLayout") + self.settings_temp = QLineEdit(self.wizardPage2) + self.settings_temp.setObjectName(u"settings_temp") + self.settings_temp.setFocusPolicy(Qt.NoFocus) self.settings_temp.setReadOnly(True) - self.settings_temp.setObjectName("settings_temp") + self.gridLayout.addWidget(self.settings_temp, 2, 1, 1, 1) - self.label = QtWidgets.QLabel(parent=self.wizardPage2) - self.label.setObjectName("label") + + self.label = QLabel(self.wizardPage2) + self.label.setObjectName(u"label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) - self.settings_database = QtWidgets.QLineEdit(parent=self.wizardPage2) - self.settings_database.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + + self.settings_database = QLineEdit(self.wizardPage2) + self.settings_database.setObjectName(u"settings_database") + self.settings_database.setFocusPolicy(Qt.NoFocus) self.settings_database.setReadOnly(True) - self.settings_database.setPlaceholderText("") - self.settings_database.setObjectName("settings_database") + self.gridLayout.addWidget(self.settings_database, 0, 1, 1, 1) - self.label_2 = QtWidgets.QLabel(parent=self.wizardPage2) - self.label_2.setObjectName("label_2") + + self.label_2 = QLabel(self.wizardPage2) + self.label_2.setObjectName(u"label_2") + self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) - self.btn_database = QtWidgets.QToolButton(parent=self.wizardPage2) - self.btn_database.setObjectName("btn_database") + + self.btn_database = QToolButton(self.wizardPage2) + self.btn_database.setObjectName(u"btn_database") + self.gridLayout.addWidget(self.btn_database, 0, 2, 1, 1) - self.btn_temp = QtWidgets.QToolButton(parent=self.wizardPage2) - self.btn_temp.setObjectName("btn_temp") + + self.btn_temp = QToolButton(self.wizardPage2) + self.btn_temp.setObjectName(u"btn_temp") + self.gridLayout.addWidget(self.btn_temp, 2, 2, 1, 1) - self.label_16 = QtWidgets.QLabel(parent=self.wizardPage2) - self.label_16.setObjectName("label_16") + + self.label_16 = QLabel(self.wizardPage2) + self.label_16.setObjectName(u"label_16") + self.gridLayout.addWidget(self.label_16, 1, 0, 1, 1) - self.settings_database_name = QtWidgets.QLineEdit(parent=self.wizardPage2) - self.settings_database_name.setObjectName("settings_database_name") + + self.settings_database_name = QLineEdit(self.wizardPage2) + self.settings_database_name.setObjectName(u"settings_database_name") + self.gridLayout.addWidget(self.settings_database_name, 1, 1, 1, 1) - Wizard.addPage(self.wizardPage2) - self.wizardPage3 = QtWidgets.QWizardPage() - self.wizardPage3.setObjectName("wizardPage3") - self.formLayout = QtWidgets.QFormLayout(self.wizardPage3) - self.formLayout.setObjectName("formLayout") - self.label_3 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_3.setObjectName("label_3") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_3) - self.settings_mail_smtp_server = QtWidgets.QLineEdit(parent=self.wizardPage3) + + Wizard.setPage(1, self.wizardPage2) + self.wizardPage3 = QWizardPage() + self.wizardPage3.setObjectName(u"wizardPage3") + self.formLayout = QFormLayout(self.wizardPage3) + self.formLayout.setObjectName(u"formLayout") + self.label_3 = QLabel(self.wizardPage3) + self.label_3.setObjectName(u"label_3") + + self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_3) + + self.settings_mail_smtp_server = QLineEdit(self.wizardPage3) + self.settings_mail_smtp_server.setObjectName(u"settings_mail_smtp_server") self.settings_mail_smtp_server.setClearButtonEnabled(True) - self.settings_mail_smtp_server.setObjectName("settings_mail_smtp_server") - self.formLayout.setWidget( - 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_smtp_server - ) - self.settings_mail_smtp_port = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.settings_mail_smtp_port.setInputMethodHints( - QtCore.Qt.InputMethodHint.ImhDigitsOnly - ) + + self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.settings_mail_smtp_server) + + self.settings_mail_smtp_port = QLineEdit(self.wizardPage3) + self.settings_mail_smtp_port.setObjectName(u"settings_mail_smtp_port") + self.settings_mail_smtp_port.setInputMethodHints(Qt.ImhDigitsOnly) self.settings_mail_smtp_port.setClearButtonEnabled(True) - self.settings_mail_smtp_port.setObjectName("settings_mail_smtp_port") - self.formLayout.setWidget( - 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_smtp_port - ) - self.settings_mail_user_name = QtWidgets.QLineEdit(parent=self.wizardPage3) + + self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.settings_mail_smtp_port) + + self.settings_mail_user_name = QLineEdit(self.wizardPage3) + self.settings_mail_user_name.setObjectName(u"settings_mail_user_name") self.settings_mail_user_name.setClearButtonEnabled(True) - self.settings_mail_user_name.setObjectName("settings_mail_user_name") - self.formLayout.setWidget( - 3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_user_name - ) - self.settings_mail_password = QtWidgets.QLineEdit(parent=self.wizardPage3) - self.settings_mail_password.setStatusTip("") - self.settings_mail_password.setWhatsThis("") - self.settings_mail_password.setObjectName("settings_mail_password") - self.formLayout.setWidget( - 4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_password - ) - self.label_4 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_4.setObjectName("label_4") - self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_4) - self.label_5 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_5.setObjectName("label_5") - self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_5) - self.label_6 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_6.setObjectName("label_6") - self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_6) - self.settings_mail_use_user_name = QtWidgets.QCheckBox(parent=self.wizardPage3) - self.settings_mail_use_user_name.setObjectName("settings_mail_use_user_name") - self.formLayout.setWidget(8, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_use_user_name) - self.settings_mail_printer = QtWidgets.QLineEdit(parent=self.wizardPage3) + + self.formLayout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.settings_mail_user_name) + + self.settings_mail_password = QLineEdit(self.wizardPage3) + self.settings_mail_password.setObjectName(u"settings_mail_password") + + self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.settings_mail_password) + + self.label_4 = QLabel(self.wizardPage3) + self.label_4.setObjectName(u"label_4") + + self.formLayout.setWidget(3, QFormLayout.ItemRole.LabelRole, self.label_4) + + self.label_5 = QLabel(self.wizardPage3) + self.label_5.setObjectName(u"label_5") + + self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_5) + + self.label_6 = QLabel(self.wizardPage3) + self.label_6.setObjectName(u"label_6") + + self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label_6) + + self.settings_mail_use_user_name = QCheckBox(self.wizardPage3) + self.settings_mail_use_user_name.setObjectName(u"settings_mail_use_user_name") + + self.formLayout.setWidget(8, QFormLayout.ItemRole.FieldRole, self.settings_mail_use_user_name) + + self.settings_mail_printer = QLineEdit(self.wizardPage3) + self.settings_mail_printer.setObjectName(u"settings_mail_printer") self.settings_mail_printer.setClearButtonEnabled(True) - self.settings_mail_printer.setObjectName("settings_mail_printer") - self.formLayout.setWidget( - 7, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_printer - ) - self.label_8 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_8.setObjectName("label_8") - self.formLayout.setWidget(7, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_8) - self.label_9 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_9.setObjectName("label_9") - self.formLayout.setWidget(8, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_9) - self.settings_mail_signature = QtWidgets.QTextEdit(parent=self.wizardPage3) - self.settings_mail_signature.setObjectName("settings_mail_signature") - self.formLayout.setWidget( - 5, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_signature - ) - self.label_10 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_10.setObjectName("label_10") - self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_10) - self.label_7 = QtWidgets.QLabel(parent=self.wizardPage3) - self.label_7.setObjectName("label_7") - self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_7) - self.settings_mail_address = QtWidgets.QLineEdit(parent=self.wizardPage3) + + self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.settings_mail_printer) + + self.label_8 = QLabel(self.wizardPage3) + self.label_8.setObjectName(u"label_8") + + self.formLayout.setWidget(7, QFormLayout.ItemRole.LabelRole, self.label_8) + + self.label_9 = QLabel(self.wizardPage3) + self.label_9.setObjectName(u"label_9") + + self.formLayout.setWidget(8, QFormLayout.ItemRole.LabelRole, self.label_9) + + self.settings_mail_signature = QTextEdit(self.wizardPage3) + self.settings_mail_signature.setObjectName(u"settings_mail_signature") + + self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.settings_mail_signature) + + self.label_10 = QLabel(self.wizardPage3) + self.label_10.setObjectName(u"label_10") + + self.formLayout.setWidget(5, QFormLayout.ItemRole.LabelRole, self.label_10) + + self.label_7 = QLabel(self.wizardPage3) + self.label_7.setObjectName(u"label_7") + + self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_7) + + self.settings_mail_address = QLineEdit(self.wizardPage3) + self.settings_mail_address.setObjectName(u"settings_mail_address") self.settings_mail_address.setClearButtonEnabled(True) - self.settings_mail_address.setObjectName("settings_mail_address") - self.formLayout.setWidget( - 2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_mail_address - ) - Wizard.addPage(self.wizardPage3) - self.wizardPage4 = QtWidgets.QWizardPage() - self.wizardPage4.setObjectName("wizardPage4") - self.formLayout_2 = QtWidgets.QFormLayout(self.wizardPage4) - self.formLayout_2.setObjectName("formLayout_2") - self.label_11 = QtWidgets.QLabel(parent=self.wizardPage4) - self.label_11.setObjectName("label_11") - self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_11) - self.settings_zotero_api_key = QtWidgets.QLineEdit(parent=self.wizardPage4) - self.settings_zotero_api_key.setObjectName("settings_zotero_api_key") - self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_zotero_api_key) - self.settings_zotero_library_id = QtWidgets.QLineEdit(parent=self.wizardPage4) - self.settings_zotero_library_id.setObjectName("settings_zotero_library_id") - self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_zotero_library_id) - self.label_12 = QtWidgets.QLabel(parent=self.wizardPage4) - self.label_12.setObjectName("label_12") - self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_12) - self.label_13 = QtWidgets.QLabel(parent=self.wizardPage4) - self.label_13.setObjectName("label_13") - self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_13) - self.settings_zotero_library_type = QtWidgets.QLineEdit(parent=self.wizardPage4) - self.settings_zotero_library_type.setObjectName("settings_zotero_library_type") - self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_zotero_library_type) - Wizard.addPage(self.wizardPage4) - self.wizardPage5 = QtWidgets.QWizardPage() - self.wizardPage5.setObjectName("wizardPage5") - self.formLayout_3 = QtWidgets.QFormLayout(self.wizardPage5) - self.formLayout_3.setObjectName("formLayout_3") - self.settings_openai_api_key = QtWidgets.QLineEdit(parent=self.wizardPage5) - self.settings_openai_api_key.setObjectName("settings_openai_api_key") - self.formLayout_3.setWidget( - 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_openai_api_key - ) - self.label_14 = QtWidgets.QLabel(parent=self.wizardPage5) - self.label_14.setObjectName("label_14") - self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_14) - self.label_15 = QtWidgets.QLabel(parent=self.wizardPage5) - self.label_15.setObjectName("label_15") - self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_15) - self.settings_openai_model = QtWidgets.QComboBox(parent=self.wizardPage5) - self.settings_openai_model.setObjectName("settings_openai_model") + + self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.settings_mail_address) + + Wizard.setPage(2, self.wizardPage3) + self.wizardPage4 = QWizardPage() + self.wizardPage4.setObjectName(u"wizardPage4") + self.formLayout_2 = QFormLayout(self.wizardPage4) + self.formLayout_2.setObjectName(u"formLayout_2") + self.label_11 = QLabel(self.wizardPage4) + self.label_11.setObjectName(u"label_11") + + self.formLayout_2.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_11) + + self.settings_zotero_api_key = QLineEdit(self.wizardPage4) + self.settings_zotero_api_key.setObjectName(u"settings_zotero_api_key") + + self.formLayout_2.setWidget(0, QFormLayout.ItemRole.FieldRole, self.settings_zotero_api_key) + + self.settings_zotero_library_id = QLineEdit(self.wizardPage4) + self.settings_zotero_library_id.setObjectName(u"settings_zotero_library_id") + + self.formLayout_2.setWidget(1, QFormLayout.ItemRole.FieldRole, self.settings_zotero_library_id) + + self.label_12 = QLabel(self.wizardPage4) + self.label_12.setObjectName(u"label_12") + + self.formLayout_2.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_12) + + self.label_13 = QLabel(self.wizardPage4) + self.label_13.setObjectName(u"label_13") + + self.formLayout_2.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_13) + + self.settings_zotero_library_type = QLineEdit(self.wizardPage4) + self.settings_zotero_library_type.setObjectName(u"settings_zotero_library_type") + + self.formLayout_2.setWidget(2, QFormLayout.ItemRole.FieldRole, self.settings_zotero_library_type) + + Wizard.setPage(3, self.wizardPage4) + self.wizardPage5 = QWizardPage() + self.wizardPage5.setObjectName(u"wizardPage5") + self.formLayout_3 = QFormLayout(self.wizardPage5) + self.formLayout_3.setObjectName(u"formLayout_3") + self.settings_openai_api_key = QLineEdit(self.wizardPage5) + self.settings_openai_api_key.setObjectName(u"settings_openai_api_key") + + self.formLayout_3.setWidget(0, QFormLayout.ItemRole.FieldRole, self.settings_openai_api_key) + + self.label_14 = QLabel(self.wizardPage5) + self.label_14.setObjectName(u"label_14") + + self.formLayout_3.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_14) + + self.label_15 = QLabel(self.wizardPage5) + self.label_15.setObjectName(u"label_15") + + self.formLayout_3.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_15) + + self.settings_openai_model = QComboBox(self.wizardPage5) self.settings_openai_model.addItem("") self.settings_openai_model.addItem("") self.settings_openai_model.addItem("") @@ -189,163 +247,156 @@ class Ui_Wizard(object): self.settings_openai_model.addItem("") self.settings_openai_model.addItem("") self.settings_openai_model.addItem("") - self.formLayout_3.setWidget( - 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.settings_openai_model - ) - Wizard.addPage(self.wizardPage5) - self.wizardPage6 = QtWidgets.QWizardPage() - self.wizardPage6.setObjectName("wizardPage6") - self.formLayout_4 = QtWidgets.QFormLayout(self.wizardPage6) - self.formLayout_4.setObjectName("formLayout_4") - self.label_17 = QtWidgets.QLabel(parent=self.wizardPage6) - self.label_17.setObjectName("label_17") - self.formLayout_4.setWidget( - 0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_17 - ) - self.sam_username = QtWidgets.QLineEdit(parent=self.wizardPage6) - self.sam_username.setObjectName("sam_username") - self.formLayout_4.setWidget( - 0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.sam_username - ) - self.label_18 = QtWidgets.QLabel(parent=self.wizardPage6) - self.label_18.setObjectName("label_18") - self.formLayout_4.setWidget( - 1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_18 - ) - self.sam_password = QtWidgets.QLineEdit(parent=self.wizardPage6) - self.sam_password.setObjectName("sam_password") - self.formLayout_4.setWidget( - 1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.sam_password - ) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.btn_test = QtWidgets.QPushButton(parent=self.wizardPage6) - self.btn_test.setObjectName("btn_test") + self.settings_openai_model.setObjectName(u"settings_openai_model") + + self.formLayout_3.setWidget(1, QFormLayout.ItemRole.FieldRole, self.settings_openai_model) + + Wizard.setPage(9, self.wizardPage5) + self.wizardPage6 = QWizardPage() + self.wizardPage6.setObjectName(u"wizardPage6") + self.formLayout_4 = QFormLayout(self.wizardPage6) + self.formLayout_4.setObjectName(u"formLayout_4") + self.label_17 = QLabel(self.wizardPage6) + self.label_17.setObjectName(u"label_17") + + self.formLayout_4.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_17) + + self.sam_username = QLineEdit(self.wizardPage6) + self.sam_username.setObjectName(u"sam_username") + + self.formLayout_4.setWidget(0, QFormLayout.ItemRole.FieldRole, self.sam_username) + + self.label_18 = QLabel(self.wizardPage6) + self.label_18.setObjectName(u"label_18") + + self.formLayout_4.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_18) + + self.sam_password = QLineEdit(self.wizardPage6) + self.sam_password.setObjectName(u"sam_password") + + self.formLayout_4.setWidget(1, QFormLayout.ItemRole.FieldRole, self.sam_password) + + self.horizontalLayout = QHBoxLayout() + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.btn_test = QPushButton(self.wizardPage6) + self.btn_test.setObjectName(u"btn_test") + self.horizontalLayout.addWidget(self.btn_test) - self.btn_create = QtWidgets.QPushButton(parent=self.wizardPage6) - self.btn_create.setDefault(True) + + self.btn_create = QPushButton(self.wizardPage6) + self.btn_create.setObjectName(u"btn_create") self.btn_create.setFlat(False) - self.btn_create.setObjectName("btn_create") + self.horizontalLayout.addWidget(self.btn_create) - self.formLayout_4.setLayout( - 2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.horizontalLayout - ) - spacerItem = QtWidgets.QSpacerItem( - 20, - 40, - QtWidgets.QSizePolicy.Policy.Minimum, - QtWidgets.QSizePolicy.Policy.Expanding, - ) - self.formLayout_4.setItem( - 3, QtWidgets.QFormLayout.ItemRole.FieldRole, spacerItem - ) + + + self.formLayout_4.setLayout(2, QFormLayout.ItemRole.FieldRole, self.horizontalLayout) + + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.formLayout_4.setItem(3, QFormLayout.ItemRole.FieldRole, self.verticalSpacer) + Wizard.addPage(self.wizardPage6) + QWidget.setTabOrder(self.settings_database, self.btn_database) + QWidget.setTabOrder(self.btn_database, self.settings_database_name) + QWidget.setTabOrder(self.settings_database_name, self.settings_temp) + QWidget.setTabOrder(self.settings_temp, self.btn_temp) + QWidget.setTabOrder(self.btn_temp, self.settings_mail_smtp_server) + QWidget.setTabOrder(self.settings_mail_smtp_server, self.settings_mail_smtp_port) + QWidget.setTabOrder(self.settings_mail_smtp_port, self.settings_mail_address) + QWidget.setTabOrder(self.settings_mail_address, self.settings_mail_user_name) + QWidget.setTabOrder(self.settings_mail_user_name, self.settings_mail_password) + QWidget.setTabOrder(self.settings_mail_password, self.settings_mail_signature) + QWidget.setTabOrder(self.settings_mail_signature, self.settings_mail_printer) + QWidget.setTabOrder(self.settings_mail_printer, self.settings_mail_use_user_name) + QWidget.setTabOrder(self.settings_mail_use_user_name, self.settings_zotero_api_key) + QWidget.setTabOrder(self.settings_zotero_api_key, self.settings_zotero_library_id) + QWidget.setTabOrder(self.settings_zotero_library_id, self.settings_zotero_library_type) + QWidget.setTabOrder(self.settings_zotero_library_type, self.settings_openai_api_key) + QWidget.setTabOrder(self.settings_openai_api_key, self.settings_openai_model) self.retranslateUi(Wizard) - QtCore.QMetaObject.connectSlotsByName(Wizard) - Wizard.setTabOrder(self.settings_database, self.btn_database) - Wizard.setTabOrder(self.btn_database, self.settings_database_name) - Wizard.setTabOrder(self.settings_database_name, self.settings_temp) - Wizard.setTabOrder(self.settings_temp, self.btn_temp) - Wizard.setTabOrder(self.btn_temp, self.settings_mail_smtp_server) - Wizard.setTabOrder(self.settings_mail_smtp_server, self.settings_mail_smtp_port) - Wizard.setTabOrder(self.settings_mail_smtp_port, self.settings_mail_address) - Wizard.setTabOrder(self.settings_mail_address, self.settings_mail_user_name) - Wizard.setTabOrder(self.settings_mail_user_name, self.settings_mail_password) - Wizard.setTabOrder(self.settings_mail_password, self.settings_mail_signature) - Wizard.setTabOrder(self.settings_mail_signature, self.settings_mail_printer) - Wizard.setTabOrder(self.settings_mail_printer, self.settings_mail_use_user_name) - Wizard.setTabOrder( - self.settings_mail_use_user_name, self.settings_zotero_api_key - ) - Wizard.setTabOrder( - self.settings_zotero_api_key, self.settings_zotero_library_id - ) - Wizard.setTabOrder( - self.settings_zotero_library_id, self.settings_zotero_library_type - ) - Wizard.setTabOrder( - self.settings_zotero_library_type, self.settings_openai_api_key - ) - Wizard.setTabOrder(self.settings_openai_api_key, self.settings_openai_model) + + self.btn_create.setDefault(True) + + + QMetaObject.connectSlotsByName(Wizard) + # setupUi def retranslateUi(self, Wizard): - _translate = QtCore.QCoreApplication.translate - Wizard.setWindowTitle(_translate("Wizard", "Wizard")) - self.wizardPage1.setTitle(_translate("Wizard", "Willkommen")) - self.textEdit.setHtml( - _translate( - "Wizard", - '\n' - '\n" - '

Es wurde erkannt, dass der SemesterApparatsManager (SAM) zum ersten Mal gestartet wurde. In den Folgenden Seiten werden die grundlegenden Einstellungen festgelegt, anschließend wird SAM geöffnet. Folgende Einstellungen werden über diesen Wizard festgelegt:

\n' - '


\n' - '

- Datenbank

\n' - '

- eMail

\n' - '

- Zotero Integration

\n' - '

- KI Integration

\n' - '

Anschließend kann ein Nutzeraccount für SAM erstellt werden

', - ) - ) - self.wizardPage2.setTitle(_translate("Wizard", "Datenbank")) - self.wizardPage2.setSubTitle( - _translate( - "Wizard", - "Hier werden die Einstellungen für die Datenbank und temporären Dateien festgelegt. Über den [...] Knopf können die Datenbank und der Speicherort für die temporären Daten festgelegt werden", - ) - ) - self.settings_temp.setPlaceholderText(_translate("Wizard", "C:\\Users\\[Nutzer]\\AppData\\Local\\SAM\\SemesterApparatsManager\\Cache")) - self.label.setText(_translate("Wizard", "Datenbankpfad")) - self.label_2.setText(_translate("Wizard", "Temporäre Daten")) - self.btn_database.setText(_translate("Wizard", "...")) - self.btn_temp.setText(_translate("Wizard", "...")) - self.label_16.setText(_translate("Wizard", "Datenbankname")) - self.settings_database_name.setPlaceholderText( - _translate("Wizard", "semesterapparate.db") - ) - self.wizardPage3.setTitle(_translate("Wizard", "Mail")) - self.wizardPage3.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die Mailverbindung eingegeben")) - self.label_3.setText(_translate("Wizard", "SMTP Server")) - self.settings_mail_password.setToolTip( - _translate("Wizard", "Rechtsklick, um passwort anzuzeigen") - ) - self.label_4.setText(_translate("Wizard", "Nutzername")) - self.label_5.setText(_translate("Wizard", "SMTP Port")) - self.label_6.setText(_translate("Wizard", "Passwort")) - self.settings_mail_use_user_name.setText(_translate("Wizard", "CheckBox")) - self.label_8.setText(_translate("Wizard", "Druckermail")) - self.label_9.setText(_translate("Wizard", "Nutzername\n" -"für SMTP")) - self.label_10.setText(_translate("Wizard", "Signatur")) - self.label_7.setText(_translate("Wizard", "Mail Adresse")) - self.wizardPage4.setTitle(_translate("Wizard", "Zotero")) - self.wizardPage4.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für Zotero verwaltet")) - self.label_11.setText(_translate("Wizard", "API-Key")) - self.label_12.setText(_translate("Wizard", "ID")) - self.label_13.setText(_translate("Wizard", "Anwendungstyp")) - self.settings_zotero_library_type.setText(_translate("Wizard", "user")) - self.wizardPage5.setTitle(_translate("Wizard", "KI-Integration")) - self.wizardPage5.setSubTitle(_translate("Wizard", "Hier werden die Einstellungen für die KI Integration eingestellt.")) - self.label_14.setText(_translate("Wizard", "API Key")) - self.label_15.setText(_translate("Wizard", "ChatGPT Modell")) - self.settings_openai_model.setCurrentText(_translate("Wizard", "gpt3.5-turbo")) - self.settings_openai_model.setItemText(0, _translate("Wizard", "gpt3.5-turbo")) - self.settings_openai_model.setItemText(1, _translate("Wizard", "gpt-4")) - self.settings_openai_model.setItemText(2, _translate("Wizard", "gpt-4o")) - self.settings_openai_model.setItemText(3, _translate("Wizard", "gpt-4o-mini")) - self.settings_openai_model.setItemText(4, _translate("Wizard", "gpt-4.1")) - self.settings_openai_model.setItemText(5, _translate("Wizard", "gpt-4.1-mini")) - self.settings_openai_model.setItemText(6, _translate("Wizard", "gpt-4.1-nano")) - self.wizardPage6.setTitle(_translate("Wizard", "SAM Nutzer")) - self.wizardPage6.setSubTitle( - _translate("Wizard", "Hier kann ein Nutzer für SAM erstellt werden") - ) - self.label_17.setText(_translate("Wizard", "Nutzername")) - self.label_18.setText(_translate("Wizard", "Passwort")) - self.btn_test.setText(_translate("Wizard", "Prüfen")) - self.btn_create.setText(_translate("Wizard", "Anlegen")) + Wizard.setWindowTitle(QCoreApplication.translate("Wizard", u"Wizard", None)) + self.wizardPage1.setTitle(QCoreApplication.translate("Wizard", u"Willkommen", None)) + self.wizardPage1.setSubTitle("") + self.textEdit.setHtml(QCoreApplication.translate("Wizard", u"\n" +"\n" +"

Es wurde erkannt, dass der SemesterApparatsManager (SAM) zum ersten Mal gestartet wurde. In den Folgenden Seiten werden die grundlegenden Einstellungen festgelegt, anschlie\u00dfend wird SAM ge\u00f6ffnet. Folgende Einstellungen werden \u00fcber diesen Wizard festgelegt:

\n" +"


\n" +"

- Datenbank

\n" +"

- eMail

\n" +"

- Zotero Integration

\n" +"

- KI Integration

\n" +"

Anschlie\u00dfend kann ein Nutzeraccount f\u00fcr SAM erstellt werden

", None)) + self.wizardPage2.setTitle(QCoreApplication.translate("Wizard", u"Datenbank", None)) + self.wizardPage2.setSubTitle(QCoreApplication.translate("Wizard", u"Hier werden die Einstellungen f\u00fcr die Datenbank und tempor\u00e4ren Dateien festgelegt. \u00dcber den [...] Knopf k\u00f6nnen die Speicherordner ausgew\u00e4hlt werden.", None)) + self.settings_temp.setPlaceholderText(QCoreApplication.translate("Wizard", u"C:\\Users\\[Nutzer]\\AppData\\Local\\SAM\\SemesterApparatsManager\\Cache", None)) + self.label.setText(QCoreApplication.translate("Wizard", u"Datenbankpfad", None)) + self.settings_database.setPlaceholderText("") + self.label_2.setText(QCoreApplication.translate("Wizard", u"Tempor\u00e4re Daten", None)) + self.btn_database.setText(QCoreApplication.translate("Wizard", u"...", None)) + self.btn_temp.setText(QCoreApplication.translate("Wizard", u"...", None)) + self.label_16.setText(QCoreApplication.translate("Wizard", u"Datenbankname", None)) + self.settings_database_name.setPlaceholderText(QCoreApplication.translate("Wizard", u"semesterapparate.db", None)) + self.wizardPage3.setTitle(QCoreApplication.translate("Wizard", u"Mail", None)) + self.wizardPage3.setSubTitle(QCoreApplication.translate("Wizard", u"Hier werden die Einstellungen f\u00fcr die Mailverbindung eingegeben", None)) + self.label_3.setText(QCoreApplication.translate("Wizard", u"SMTP Server", None)) +#if QT_CONFIG(tooltip) + self.settings_mail_password.setToolTip(QCoreApplication.translate("Wizard", u"Rechtsklick, um passwort anzuzeigen", None)) +#endif // QT_CONFIG(tooltip) +#if QT_CONFIG(statustip) + self.settings_mail_password.setStatusTip("") +#endif // QT_CONFIG(statustip) +#if QT_CONFIG(whatsthis) + self.settings_mail_password.setWhatsThis("") +#endif // QT_CONFIG(whatsthis) + self.label_4.setText(QCoreApplication.translate("Wizard", u"Nutzername", None)) + self.label_5.setText(QCoreApplication.translate("Wizard", u"SMTP Port", None)) + self.label_6.setText(QCoreApplication.translate("Wizard", u"Passwort", None)) + self.settings_mail_use_user_name.setText(QCoreApplication.translate("Wizard", u"CheckBox", None)) + self.label_8.setText(QCoreApplication.translate("Wizard", u"Druckermail", None)) + self.label_9.setText(QCoreApplication.translate("Wizard", u"Nutzername\n" +"f\u00fcr SMTP", None)) + self.label_10.setText(QCoreApplication.translate("Wizard", u"Signatur", None)) + self.label_7.setText(QCoreApplication.translate("Wizard", u"Mail Adresse", None)) + self.wizardPage4.setTitle(QCoreApplication.translate("Wizard", u"Zotero", None)) + self.wizardPage4.setSubTitle(QCoreApplication.translate("Wizard", u"Hier werden die Einstellungen f\u00fcr Zotero verwaltet", None)) + self.label_11.setText(QCoreApplication.translate("Wizard", u"API-Key", None)) + self.label_12.setText(QCoreApplication.translate("Wizard", u"ID", None)) + self.label_13.setText(QCoreApplication.translate("Wizard", u"Anwendungstyp", None)) + self.settings_zotero_library_type.setText(QCoreApplication.translate("Wizard", u"user", None)) + self.wizardPage5.setTitle(QCoreApplication.translate("Wizard", u"KI-Integration", None)) + self.wizardPage5.setSubTitle(QCoreApplication.translate("Wizard", u"Hier werden die Einstellungen f\u00fcr die KI Integration eingestellt.", None)) + self.label_14.setText(QCoreApplication.translate("Wizard", u"API Key", None)) + self.label_15.setText(QCoreApplication.translate("Wizard", u"ChatGPT Modell", None)) + self.settings_openai_model.setItemText(0, QCoreApplication.translate("Wizard", u"gpt3.5-turbo", None)) + self.settings_openai_model.setItemText(1, QCoreApplication.translate("Wizard", u"gpt-4", None)) + self.settings_openai_model.setItemText(2, QCoreApplication.translate("Wizard", u"gpt-4o", None)) + self.settings_openai_model.setItemText(3, QCoreApplication.translate("Wizard", u"gpt-4o-mini", None)) + self.settings_openai_model.setItemText(4, QCoreApplication.translate("Wizard", u"gpt-4.1", None)) + self.settings_openai_model.setItemText(5, QCoreApplication.translate("Wizard", u"gpt-4.1-mini", None)) + self.settings_openai_model.setItemText(6, QCoreApplication.translate("Wizard", u"gpt-4.1-nano", None)) + + self.settings_openai_model.setCurrentText(QCoreApplication.translate("Wizard", u"gpt3.5-turbo", None)) + self.wizardPage6.setTitle(QCoreApplication.translate("Wizard", u"SAM Nutzer", None)) + self.wizardPage6.setSubTitle(QCoreApplication.translate("Wizard", u"Hier kann ein Nutzer f\u00fcr SAM erstellt werden", None)) + self.label_17.setText(QCoreApplication.translate("Wizard", u"Nutzername", None)) + self.label_18.setText(QCoreApplication.translate("Wizard", u"Passwort", None)) + self.btn_test.setText(QCoreApplication.translate("Wizard", u"Pr\u00fcfen", None)) + self.btn_create.setText(QCoreApplication.translate("Wizard", u"Anlegen", None)) + # retranslateUi + From 08b23f01f8fdd126cd9322bffb5d8357050a3b90 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Tue, 24 Jun 2025 15:48:03 +0200 Subject: [PATCH 20/42] update main file to create application and check for wizard need --- main.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 328ce8b..4066d3d 100644 --- a/main.py +++ b/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() \ No newline at end of file From 612020e495238907362d00b642cc671e4d761df6 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 25 Jun 2025 14:37:16 +0200 Subject: [PATCH 21/42] chore: change logo --- icons/logo.ico | Bin 270398 -> 270398 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/logo.ico b/icons/logo.ico index 4dfec92884a2c07de86f6bb8c48860378222215f..89ad115e629f2c46da61ee1be423d236a4ae3fde 100644 GIT binary patch literal 270398 zcmY(s2a_bpk)?^f>8eT|oO*E1Npe=4^Xlqqh9*Ea=m0}xFe|$vD{>i|Mx$B)B4S*^~GtnYGT9{T?qmS&+=U4>5bjQ7h>E-yo^aTNNKd*S()KZNe^I2>>vQ(V(N_b{DF z;+D_1L$NRnmEwYJ9m5l+G-rHoUm0+LnT>p?c7^_x5@pi@$WkKzFu3fKli`T4LQbXq0r=cRYJPh z3iqCU3J;%u3Ptv5b-{lWHMc@ zea-V+gnGg;IJb7|nBQJ~X`>)OMn$5M?+HpK*(#?=gCt+_lANF?gTnkt^$byOeuzlx%&(d*g)fulzFYIom zxo$C1_>8d(ui`BgzjNpIo#1vGOy0f)HgAK=?Mxhl&CWbz^Ua9q1Aa4=sfBd57Babd zIADCbiU0kB68nR@_*oKPwFlXJgJ&@1`SkdEIc#sI!>v18;R}D~i`(%x@9=vO&%3F3 zetYpX6EdkHb|4>ixSo0fMoUeuk8|GIXaBvhjV(CX%Z3B+zqeZmyWET2ZSDy+A)Uew zr0Q`?roOKm`(=gKqW86{#Lyw|GZP~+K%`&u#z&DxabIE&A( z$a88n)}c{jJQXLLTb=JrLq0QRoQ%PMV`a+u^6I{fzwzjbV}jSxnsL6yt}v!@GsZDv zm~mQUELCe@uW=e4y!aHZAASgV&eMCE&SJ+Hvzbga+A)8lTAqh`^(ZvJYf?F4oSlYt z;|d(!=a|>*I}F*v3HxIwlGD%w>$B;@Fqzy9)7itYT0IP-5&MGi;oxo<4IYQy@KI>@ z?lSi8hTh;|m`)D+J-ai_*tlfxehj?!TTz|+XdtO-o0oOYRNU&zX~m|U&k&q z+t;B|JB!D!@mj8&gdxWgADxc2>YDR-6poHwZ0t#89p=Z+!T&?<*>RXIAMyP|>?rqd z@hH?f;HrbIXnCda5wLy}Cnj}4xC zll#&L2M6WQ7_7sqFMkfx^@C6Zy8~>U_-}Pi*=O^tiiIPd=OO3BHGuaTHn*5R422Td z&kcD$jdrF{TX8>5xV{bctM~|gMW^k#@jW*EM!7KJ9Qs`Ilw&SKuXh-2K)E<#?AJmL zj3bIQCOwWa|Jt?1w#YyN_RN)_RPIA=n2; z?5o||-^pSZ`1`i62Vi9vpSlI!Z$~_Sfj_>DKiJ;#l41cT)H&H~;SI9-r-DYm22WcCE*Lm9Pb#zqq{>tb8F_#56I3>c4dp7lI(0^f_*@$ix{&-g5_!1Xb9J&W zV`p-79iG4cF?9RK@m^(e*jVg9miy^FEINMhLxs$Y^Y_^d!8@D+ZlRy<_9#r@7rri+Pp}8~Lx1=LoA3x6z6`6Q*I}`K73PcQ;1oYRe;(%Zr(rRB zf)B@bOrJ*Vce?O^)&6r+uG^K6R=g-ni{yQV4ri?`(6A(F5iMn;lr>4rw?9-M=!n# z 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

    #N-GojJ zz1L!K0v&WidpE-lAEeNO%S`(|7)TjHy?gn4cc4@I6&%5SlGZT&W9-U>9nHlpb_X`{Q&*^T$UweE@Y%=Z4k zIu72o8g_SA!xPA#HEheyzHjr^?02}E1@Jwz&!@a@1-bnx9w(grJC*bU>%dqN_}!Wo zVf*a*-gMF$;Q!~-hp8gBE(MRRoz-J4D~?###%tu~16+4{9Q3Z0GtLCNUw!jSOW>0# z+P7NGz)@R@9%A3wCun}1vkR|(YP+w$VXeJm%p%gqY+i2jI)?E{qvSX@nSDX8bn-Oz z4L4%3bWho50@l|+Pqd0_qdDLTY`bAj7kv}>Q9bNJ2U`%2uSB>$K3)6Q6bsZd4D-FX zR>ig0o-lsmeI~O#S#Z8X>~Rq9L-8-7UaxeX$OCf0jeYbIwUbdew2yP(<@^Z8adCEB z{9Ot)g0#8{nXU4|sgeDw@2q^%!&%8C@5}=4_ksZy(Cd{3ZVcb$=zOb}M*bXP<}SpH zdc1VN>gL}dX6!}FcpMJrYR(_MIi9}=ZOI&F3sYtP^xTAvr*qbY13de8KDl!aHbtz* zu4HG9FOVPL@t>Z{zy?#8o1aF$Kz~hIL_9|ld4UVNcT*39iF*P08{(wX<0JQQ5r@;^ z;|N2LZkG%EPrL%{C(wRM%|54tK}@61km)HUcJrQ`*QD0R#+C0U>wEHm=k(slN1Rzl z{Lyp#UirQ7(j@YTWaTz+BA%mOQJv#pDby5sV1H5iwDO}7=IBeMq%P{qa zbc9qV(4cTV^Kn!ZGxZIPriNPwU2EEy|O{#=Q1>Q z$FIW2J@$zCpBHW9xU@fBweIcLz}e2C4ZMfEVcZ6fyke_Qy$A-m*6Lfx8=8mUB#aPy z;na0ZpxL_zjBY*rnqjLY=WnFnI-`ArU49pB;LKxu5j-Tg0SiZu;Sbmg0{3wA^lNAb zJg{vSUSnq92|6SjJZo};xhQd#n<3HjWSE#j6VX8Vy?7~9(d1&i{uttZRxgS zRy`9fRBD$5F}4xhprd~)J5A76q;9Pu=Wp#=iPqstYlbUS$MuOZmtR4@uM8hhPQFwO z_a={?ewZD9>I-VdSNy9G+C~BRJfRSIKX^_6OgD~}t6z05F-Scl4?RT1e9eyJDlUST zt6qH8OD0#u-^)*EuZ%Qgw7*F+B?)ZbFWx@=I?dyYd*TQGEv#Y(fO;{+BUshT0AhQ_W{>nrX)H#HSsknZH*+PgQEpG_qeOG`)?h2pVx9Y=|U(gyZn|6c6f zhiyvN$;H`XYbo~t zz77PrW^n!k%qRMoPs}C;yU5+5!ZqpVJ*)qv{lOd(=PM1o-vu{QyV>QJm4m4-=p(L6 zyC^LDmiVtcAPbx|hwI^0?azG|S1|`1#KT$e(Z_ak{=&rnh+>Ck0*M8}RU`fwoE@+p zW-PVuq?p`NJ^jYH%c!xJS`~8w+V!a&U1XAe%@AXLem>fja7SZ+rEcCw$mT|&ek4(g?Ye7e0|Bruf1<4p8mC6 z{rGR}z>A;Qg_l3E>mU8nZa)2moxcB(?S1}-{O&h)_h)~w9d|#&_Fso*$p1h6I(Yui z?ZlIx*oxyXGOu)$9pcB>4RVgTqaC*X@B>@6|3w>MMsRw4mu)_O&-T3dG4XCSxx!jo zxQcxu_^yU7`i%X%@NpCDBi#f3(Zc?)_4EaZ|L6m^!tbraheoNnDyO4cIGx@gc~X`5 zQZwMtRkv_9@vq$TTJVx4@}n6o=q1&mX;a9##=f+!9^(4I)UsllB^&|U_sHRuX z2m8;Z*4JJD=|p;nF+uKCHo2uZHtEEfEOEt|=k^mz#PQVrya4s4UpYTL@oeS{ojr4$ zH)-bPk=wc9kL7_?M*gGENUj;+?B;Q{{miUu?`IUuvb=TFLg*+4*vk=OAFz5<>RCzS zS9|fKZ=DQ2=E5%tV;9#T6FnaBW;IhgMLvMqTAG;h2hZ&ASqxS!t{DBj=Te!s0}BAB zbAwq-okD+_+%|wO(#%RKxw&R{ed0us7f4GW4ctMzB0ujrISoz#uP2@U@1y0dnqFF= zX>g*G`2IOzk5lA(gg@{e(EI$p`~kT_7N0>iRJvQc(xk;#&R!$1LU0Mq{_AUB_>hJ2kdBG2b|7G);#aHlx2PrS`gBSYg z8ARYcM%fb*W)?6Kj4}IAh~^?%)zz~&o6PT_^;bWKc@=tn@(U$k{#CWi0Tb&rzfwM( z{E?cqelA?Sh3M3Q3)E8M&Y0P5v(VI>-h|FKextf+8TfmbEyb={SD*)g9*1U#8{qEE z>D|o?!CG6g?wB2V2>)k#EBwFbt$P&@BTGA zysq0q^nF`8;25J5cJj&RcK_R-Tkp1;Xn>B{YIOfcwp~F7diL-g{weUJCnpawV8#_z;r87)VlR4u`Y({#i(jhG)pI^Kji~t@&SslCJ z)yv@esUD~TBdiA-tfcO*tOgq-*Dk4Oghz$0DE$s`Ftj769A6ybJm(4jqvnqhhx0Vo zL#`O~GUvlR^{7td;l=(v+{+w%eh&ZE9voqNN#d(3Poz&6ETV=2YYKvy=P{S2-dQR( zEo}+mbs3!REP84I{6-$Htuq-fMN@;k!b9GzTq6W_9s*;?#Rg)Ean!RlZEG#6o+9%# zLHfaQa8B93G%H=ip>%8`iCSK^@{CW%ya9WJwV%(0FG#_5r();H=oU&7=b7!L+V@lg zB%=`k=RvwQ$xgihK0r>93@$#6&lI8#Pz+RW-_7fU$elDF^bFg_{^`}_kmIIdQ_kEG z_jhW$U_3e0b8)ct zTzb52u8A~Kw1-hV0QE^Ts5|n}1;`~22x)#FTsDnsmC1W_E0>@q(ww#4Yc6NNBaS6? zhGrs!lX!S-zj8&eQR#IT63-*~=z+sWDIjiDFz-@FA3}Sr%GuLf!p@Jfvgv63a3<^7 z?G1;i4*Q+i+C#p-+U645n%W0#;o@Oy?S{|D+)o2JZXJK0&*yC*@1D^_Uzo4evsd!} z)}VjGTrIV6DLS#`U=59JLw5YFFW~~U*oG4i@D*olJvhMPHT$ds?#=Mp)3%mg{zmp{ zjqJW=+v%B3UVe+7z_<;=%^TWx%TB-YnH_!Ua~s2cS8cjrtH;k;@5Zz6@-Hx-a~po~ zPwe`~KeJtz?%PVR!j9#eZ6~w8d%^Puc3rcfZO_}TV=vgsjpvyQ*lOLqJ8i>3dV=_o z^=Dtg=gqPGcOE3epp;_uK{fo0#MwU)HYV^mJkvxuSY` zF`irYA0bcklVhe6C#CTv4PRmP+Dj7@C%K%y8a1JIKlYhIgZ_PzXvqL^Wwm&5u+@06Z z=TN`S1CBqHdLvmgOVr%jQ{)4C^%Jkl=yL_hxinj#-6jG0ZrSLw2#?L=z66-V&o5Q} z&vk{5@5F!g{dqp^1rt9blen&#gAg@-LES=f0XSpS3>nyd8vUSTu8U?BGS%C_=CyY* zM>8V4W-j$wHvJMeb%u0N@(P$y;@)azAch~1hJ6f9UznIJKjcMQHo-Y8rYQsa+eJc+Bmz*-f47>|~Y|?T_ji^!+){^H=T!r(^$l+X{O9#Cot{ zX+kfae94yWx(Y_r!aUh*>|l+ZeCtQHc*AiUyY!eExD(u9h1C-yi@_YKs8bv8A@f>C z*wwYoR-q5Nf_~nHyHDu_-f-%LwcF3&8*bRDiJLaO^`eb2Gq`&HLpVSW`1vKU7 z6pcG`z_oSgq|`HmFo)~Z1TPqWQ8%l@YTyKyat4aQ;Pd&+;@{@e`_CsAOJMVPW%Osj zXf(GWTvpniA#%tZ<#_PDW%t5>Gr8}=`qV>mVS5?my6Kz|oii6Xa)jCK2)y2s`Xx>r z$>a=b_Q%D!&ct@4&nX^D0Q-;gI;yLMNlS0aMV{&CPHR41JqEBr#k4&104nLBgo~IZ zSIi@)2vhHh=abAs_V332a>*%_V@yqhE6+@eaK>l0#cNZOyQn|X!S7`A+6$tGdht`p z{T*HacKSVXbYTN2>^DoHpP=0&(*DRG7oSFcFD{|<4wV09lGkL(FW~>Q!#*RO`2(JF zDmjFsVMdMreKdK$H+7nW^~wH)JxYf^U9}2vUUQHvG~e%!^ZvZE=c98dTLIHY=z&+;x_hw~wh zkP9C#PGBytFD$RBkvzW!-F~nEKd%=DpAHeTBlHz~__GLDu=ZD#fK5%Whd)nFt9juP z@`Wbif4j~!b9ik%>#Vh7$Qt>5LlY11?)fXXkguO`bUHf0#FlJ0WSygX_&GejotLbO z7(90M6C1tpj`f{+pZxq=@VBeB`P2*6Gj!7F2DCS0O?^vFutr&>{f zb}0IR+px#hp6%?nSwQYQk68h@#q{guEgrQM8&6O>oTdh6kN2`|#8+l8>s52ZyJ5H4 z4tRf^eOu87Bi{0yh0ODm(nBb&U4o6WX9T`MJ)HmUHM_|*9+L~)u;Z_MZsYeqxBiPC z+B$lS9pHuSXame<4!CXf2wDR0jp+Y(?ZmI_Ib&@DTbY9f|DXpr3*J);+=uEa=Add@ zt(jQg1g6+PZ8TkYKpph}^9$wJcmZGK+tt4flSc-~xtu-@`F&wI+(CRt1^8BtnnSigazOu9_b=Dk=5MEs> z_NSWNN4$=)S272jM>8qXmCT|4s2HO&6b5t9Ye$H2vcC#u+kzEK$c4qn(+ukz_^|jM zUOxp5MeVcn)9?194eFv#okTxRd>hpo+70f^A$su>A^0xj@X7GjrMKg7aTK4id*SiI z17*AF$tR!%l}U8M4VnLLj)j$B+rX?>=;q*bhV{H({9%Uq*Jv&ZB;Iq2hPe|ajihPrOax;|iZ z+DQ-vp9>YEli#!oELZWL`CG1C0DJT?`zU{$N&YUbLIL+)eLBs31hCIc{Elp2JH}K; z<#_N3{6EEWFEJ`kue*eprJ665o{%_B;x@`pWRbIEEAN-TAtnfi&E&c$_YelET@|Gj z^!7@bdxD24{(xo=)B_F@|%U*HQ2AFLmyt17-jw*46So?7g}7`t#|uv=5ro^#ow{cwd(twvdK5TMf>BT&0?4M z4Dh%bxCP3?YspEv`nO^Khn>7(Hs`Xgbv0Z%Y`PQ8ZnV61KK!|LPu|3Ck6H(Mp#6vL zq62;%9N-SQ#SvRFIF44)X4`e{4c^l>TZTW=jN;yV@7Wj}fK|sIS{u4ROTimvwll*! zlU?OphjlN0Wb?^`7A;2akiN%E@Q(#O>>uf3pIH}p0kOOeKeT|_sk&|fvjALEdPUMr zD5l1)hLcl7&J`dQ2EpXK+zSuqCP++m)9=xKr($U_IG6zOUG+Wrxp-{q`>N*8;hrnT zy1|Xam&pY4OQ*&Z)|?>!Dx(I=!$-@fs81k$SvO}zeZXnd=jp_L&6$O$52EnBeArE_ z1dRy#5IM3p@(In9YKBqqBu1|>!g;J{9ftR+=LK5?+YnBreZS6pAH6bZ1%>&X9&E>p z-}T3^8|vF>_z1;V#a8(MH+VoUv+>#Bk!f&Sgu6epO>y#+vkRNc#@~rI%uF#gc$Bm2 z!}kTbMn2Ug*uRf_*NYvgH?Cd4vVo{@Px@jhsmd+X?|atU`<`rH_&$9?`A_-8O!|%D z-e=JtQT{yzAEx<8@%r4ncX6Ej@bi+v|5L;z0DlnwPdU44Z~4CvGl$yaUC8`@9@oPU zSHrP?u3fh9SU9=b)8NJr{73u`6NhF08Q5{Aa9esMsmwHFG3$_y&&c8b28qr26`fYD z{d3@SLGHVo*)P?2(rK5zh%`eq6O==2(2n6ubj!We1KB*MW~Foao$3Nt`{cLFNvKH{ZwsvOU*_l^`=2aQ| z)}A%Y5tP(eKl*%2*6qUn*@0OL9}XMrKlnM^{a3AXWIvp~eRlf2@2rb@ziIv=TX*OV zT%G6X=?$~PV+0M+A@+hDwN;lsv1u}tJ!;WU%od zeKBtwZESpikN1#5Y@3z=mls`uY~sIqUD?#sIYGY1r=`Oy7w_KT;1Ty_v(J$aq!4f8 z)YzJv^b)^ZV1ItFPR*_O^Et!VkY)q3@IkTx?ci45Q$9}^pYjMjr=#i1^QI|R;9Qf)dztK#h7dw?r z6@Yz3$Pi}gyxM+1z!MLcR@ zc>!{$#Ef1mX@P6HkeQizBV2EE!-)myXe?=VS-Y9C@Ey`X$Z*jg#MgL<`^uA?z3Ajv zx!@2EXBhlQ`J;#HDBL7~5B75HwTC^#8I0h=it$UcsZ%TIOBItdG|nP-t?xkhyWd)x zd#$at&laEwG=un;2Y#u26SL+l0F%37%fRwmx;9z^+5&Ce2dr!OhArQ4-}W<)H-7k{ zo%`fhws_sO7;v5?!1n+*AMCEKe7F{KcZ*&7WxARt&iNJ z4ertQbB}C**BC!^!*-s3%{K8`>n6_FFm^n2=85&gNt#VPP~EuLPX6Ev+j;5^dV=f~ zfS0g*?3^tfI%EyZZPl=ov~y&atvmmio&@y@HB&6F)Mn1aSJaY0{IT?4J&I-I*6QDgBdUIva3=M1rTOnAr_~&ra$9lw zUEJeT&ZBrR%JDMDpEHR2;sAxgS!<|`D$!0U1*b2a%?@+oST6V5!I(K)8Q71sW;6M> zZ~*NM(oQ!oIj45NX%197MOCwEzqaDPAO1*;zJ#B%T@0oh1v64^s<~nxIAAK=8a;oC z?1UJWN`9){nR#H?(h!s%h!nM127SO3{El=^U1&9=6K93BW&5l7?Bv44D`5iP`~EXd zUJ4v(%?J6ynuk=M6CMLTOFH7xB~uL5wG#&BWqwlFcMdg?KCd`VKF)Y5{6OjXYR*S9 z$inrqcu0dYO_(3AtG?Sbu)Z96n!!T!!|9iL>6xg%=LS#E9uVOVnh#7B|Bv}%=>qF_ znt=##E!^T!&O!f2y*p+#grDZZw{vklGvVZ_r;$xv5(n4OJg2x`KJ4Gw1Bm@=@2WIN z9sM!Bs{Rjfy$j%4l`hx-PjnUgI@iIyTnkSNUJB<#8ipz0o+;d`EX8{6mvoJM%z`QY z`@n6qhuBBF5Vy!nuH@n2oHNc>E_g#OaYQ(P_9jKpb1sB~DE?YS4ZD)T#md2vG!Hz7 zzW<^Qw0^7S*bMOQYWmsgAJjH3vX0fv?ZT^HwvpJrjD1*hS5j}YCz`%uE%T=>?8BN% z?Qr(#PubOb#%3}r(KfQzCb0L_lNZ4i`@k7C+wl6Mwr~{I4|8#eNicyY$TKreo(bu)UgQ<`7m`)4Xm6>#Lhi4}tl} z60}|`YS`z<9BPDoF&}M`ARG~C;CLOIr;pG5_QK_p?TeEqTwlNQ;=gmS8Da3!(9cqQ z=3cwNfu#8+?F+@B0_iq0SCPMPy%o<}Z{_GBSAda3;OJ>4KsCB-Kh-(A?4ly4mfl1J z&RCc{T6-q)$p6JX%wTs|7JkMBb|pTUpL|prVBeQhz_1KKZKoW zFP8Lj^;+5o;3vn7f^mk5TG7EJMxwo*179Hx%v-u*S=e3%*FczLju$)t{~+GoRO%1) zhEcO(MwM~n``4!9^m*&FmC!Ie(*NgziI^GVcGZqFV|B0%r(F3$3KWWLqLLDN0k9f=CEKJEHe}*3#LjO27jQzrm*IeHea1iC_srZ{@aKEYWf2J|Fm<~Tl z^?x>gCZs$E96+yINc}I35osBP2AI=Y^6XI(CJWhQ76!ZqB z63bKI7^EcQWAM%D?I>R_Kw~^Sy&tXi9()>n>z1`v+_A~gi4XHPPJe;fUk^E;=EMD5 z1Dz`mIfA%T0p6n*TldnxRqh|;t9DLEcg!p9gz^OXh!N!)oXbMuR2kY=C2$Xmz}yO` zRioUyGJL_jc6NHx%hoK+?0$Hl^9QYM-3gnu<|MkXXoA+#7eL>q6#Lh8D9abOh4+k4 zEdejT^6ro9#Ot4<;du+60$$GSfaVAD;G`9!d0U5eQp>_YuyJ(r=_Rh&_W+GP_I|BD z!hFzS^!9dJ=ZeF&Xz3ny{cdKjFM42Tc6Ii$JDA>E`v5zA!09@f8<>aQN9Xue8=~g# zS~bb%ZnoKQ6=yQ9S5k@=58p@GE#HWphy&5T^)xfWU)s%Y{uZor9PE@DocNX~;_uM8 zC}hu9S<^~p9+tqVZMT_&lh(ZclGP9oXZP=b2ff}}@hQ#pIV+`|P|2QP^vtTOmM~X= z9wYSNvY|sOyl!Z-t zRqLY#E!`?{ib~OxD}i^FKW`139OhrD2jS=pv0rgHb%y%P)5&eg&*9W0lQ)W^mr1-9 z?wm*LkHb|6(R0w*)Qqt>^O?-qD3;`Ketqge!b_AURr!KZ&LJ2!IAuh$ubffo;W@lG za5LG3_GV;c@!7E53}S8=j*yQ&p_iRwzT$Sy;Y@n^&wAe3e*T7BAOqY>v-sM5m9E@` z80d9kJoq}?STA}!IoN+TK2aEl!+mGAP#Bi#=M3sz7k*JOS$3_MrWs{%6oT;NvbbKt zNVKOk3qPaT$s~G|Dd7A8bParY^Z36rsq=a*0Ct<9xSc|ck1v^quboO=GZh|zb^%Mn zU>bf`y_XR9fJc}N_p6Ze5o1m*$UL@cColXP>G4RbF$?~E4n2S{wx3IF?MIt4fc8Wd zIBX{O*unn5AG4Xy%)};Ld``uJ66`;h9dSPT3t@BxL(}^#e>VD^;M;NCU;24oVK&&l zazpI^@`0I%f8gdChPfV^hg41EmED8=D`!%^=hxri_aofPD1JP@7~GG3PK5YV3O`Bx zz%n$*iovc*_<0R;YW4MD2@mb(sS z)-#Fi?R?;9fb>pWvvNF$0Q`RIRUQ{?;mQMW1-GIJJc9kKum!)X(v6hR{D;8X!i|+_mC%H%L)8VBA>n;Ggn3U%heZ{ zyGB2BHd+V8=!sRs&z?ySaQ?ugE!}jCTm-z4+`k7Mq294m%=z@$#Wy~-;S0_hYEl#5P`)=}` zFg-+Z7?f9gc}Pb@HLHi$%>Yw#(+3R*{{s^gHmVup9Q=axv^`EfP97!xJ?C0`SG2=T z`U9%}Q!ar~PeePXBBl_w*rwgpugRY3LbP+pa(FK}0WCiH36i^SuiAj!j9G_o1K(zl; zwLmUDAxbUalYR*Gf;8TO;3Cex1nggXqa*mxD6@Wk-hY^UB*ypoU_1Hv)v8MN{PL(K zhc9PW*BttBvxuRa4qmX)>mSl1`^r`vd6k(EY2dG97tSz!`m46(@&~r{#uwJPMsmU3m9v=H<4ajWUG(82L8yHMPtUtlM|d#>w9&(eym<_IGyR{lB$Mm!HyK zKVg0Q?pgoQS8VL$OSa?OW1BepifuiG-q+bzZ5z1#)rpnjh%lBJ+TY6;pFRJkAG{2-uf%s_2N&MQF_4NJVsXx3~(W{1l@3$>0M~&=jqqp zvlW}rAEh4Y96>X*|AcM5{bQTB_n9q)n>1(19-D!0sUZ(rwC5G;Jo-ahbNg$XIB|=8 zh9mSQ;0qD!Yq%y=#Jw`|sXTVhm4dAnfU!!$-w#JE44)>(HI}Bgb{spnHvKI>_eeWG z+?+Y>ZdT3j;h`8TO(*r^667WM*mfZ^jm11PKNYB1Zk}@NtE2<02iq)XZxivz@8|)b z8w`%9UU3fiPb#^xbU8Jb6X<5X*49fyL%h9>vcj{pY|4V>hl;fAR zKg>gJB3#_Xxk?ju?byHae`e�TjdHV2e)A4jz&6nj-c+<-xbe4$(JEfFZysP9_JE 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 F|1W6P<8uH2 literal 270398 zcmZs^hnpT%cJ7OP#y&o_ugCWB*key(#wIFfltW9coHG)LJcMBg!yu4QLP8nkoCA_T z5-6gC0w9r*P;9kw277$YbN_^O-``rbx<7a>JX=*?)vg`a`>wEeRo`~opXq=8`?K3_ zzwPeZe{|bF>-_GxaNBKv9@qJK+igFJL&?A29k}0a>>W^~es*(Te?Py@wS5D1p4a-> zwcUO6oBRFk+8!Ns|K=WD+tb^w@9FFJxz4j3ukO|}CBJVf|FzwER@ZM)uFAWv^Q@oy z*Oj09ltJR!tq%spHPt~qH^_6yir#5ko_T+Q7_V>5%4yygdIFpZZGI9M|`cgdW<3_tw z{aO1YEcrO6-?u){`0M`led;%Jrj*ZddS7;O_BUrpSS3vx1aXu+IO0p8_Iu6@=`kXH}|LwyOc-scYU|oUyM8FJ|`N( zTZ|ulQG2h`FO^BVyiLXUT;n;NfBQjJ(^;FHJe&15RuD!4M*LCFQ`ujcnoc4R0Xcv7^y5)I7X30ys zseb>bWaB%@l;2k#f39dh?YX%t<}2HMZKv`SeaNHtRqgP)ROZ#4J-uC5bPf5utlyXQ z+_m@BXP>*kbYLGj(mimf#F%FaIf%T8ZSN~{$eJQau3{I)3bT* z`RRwsr5}`+GR*TX6F-=j#D31AH!_2hhcIlAXEh3sn{ZkBMB*Q@*R$W=Jr z?}=9=f6n1L!T~qHu%3++a~u7QGHLtuT^i3~oES&O(Axw4im}Q$3Z5?JM$gbr%7Fi_ z7h}m-UD15r04MuYp7{Ej%MzHT4a`6Dt~`3ylghcObNYpRr_6D5eUoyiHkHa`b>E_o z&KuOF6m0|VrQ=F}`K>&XBbOJ|nd8rqxdh*d&r5uY*cg7=hgX$26+;ABo{{XiyIJzyOepK;<`i5MQ$nSH(n1V~U z35t1TE?qv6CC;TQ!KcpQTXZ?s{T|QjJoR~C1(}U!InoF6a&#`HP3UFaqui@-OjZ8i z&6Hur!#Q33xvJ;jBjog3@eMen57=y!b6in`k(n{4t$K5<3iTpbL!(a z_p;5=r)URraYglB(>R1~@9phJ26)bShj}XS;Qu_H0~~M>J#&KL;YjEk-IC%9(@4gV5-nh@FLf)%4Xmib_8wD{(~>t3vQ9q zYr@0ToqAq)zozTVL*fT6pbpC5Ud&U>mE)AY>G^AVp8n|#^;mq6n4?=Ilky4a0iYUr_kMjS;|U&y`gq`n`y7gq3t)3RiC4nEAEpRbGRht zEO5^C#CObl$Rg#CyzG)=UW@W#j$+QmcVJZQ4jE%CLx!r$g_}YzU^lw`sD9HIY?`a$ zOUjSuqc7i=D~-808~sh$)OST#zWP2qg=`5Q%&oUqyrb)JKk~aCW6XC3zh%2A%jZ*b z3U05!Gh9RW$j2gGbDRg~mp%tB!Txn&De=#JzveO$Wx6fF{1;oJB=j?cZuA@b{<#>{OeVTie5PJJvmQWqSf^DM*b)4X|J zcw2e=9=KE)j05A!oZWC866KL6`-X4FT#*7RQ5Shgj0^ZrJ0SDNd=+xR{Ha~p|Ex30 zq<&JAmvwW@`J*j~-?#?v^zuYmKYWI~Rb#AYvo9P;VOK|)JR4<5zT~&Kz2de?oWp0t zgJ>iDjIx3+OWPRZu6YsWN%&;wF>oIKndTk&g!|~*E@4wxcOH<9e2uh2T(d*%(413V zRUg2r(#`#Y?TU_9unpnL98<kJ9=$zqf#aXKiuU9fUY703^I29kuhQ=rOH zcsr`OgEASbTl<~L1s~>lwlBwnW9hYQFXdj9J;}Y&YkpnN zL>ohnxF48i48XVi8~NfKk&EO7j;`0>D3yPCC$>O!Sw2Pu-objcZdcwNnrn2u`hG?2 zx+0AG{436PeV60`9-waV>{Z>nx*Z>c*fZCoABodzv{&)ltCFM3aD);vzTBclze37;~4eV(gr63*Jcn@FRJ>oa%nv(tLf(Tomokv7_BN zui1y_>j3B3oTsbcPI;39c%I*IF~{@`(_Y}(H83yCxg0uX{@)#F*8Chm* z!Fnn0`*<@Sx=!A}W;{!MjGM=bl!tNf`7Y{(AF>^^xwIcXrpiYfIKQlTnV;)7`ARY9 z9Hh44e-nGdm%gd8$&)zpu_>O*I%C|U-QG{e2YX05u9^d4zQCL7Gs;w=y_pyf(&a*) zIS-i$*+HjQ*U8wcOvX|v@Gsr3HeZ$gcYdYMsq?bG%!6d*3hh9bfOX_mvWy&D7Z#B( z|E;nTf5@ZDFnN_|8~5T!J(033p95WEZd``+jLSwL4>|5W7T$0A5IEtUkEz-od=HLE z;Dt5@ZWt%HkL$EK`W*d1&tR8h<1rVMN4x!5jaSGJ>ptp-+DV-$BVan@i#ZMb?=q;m zLvBNc$rH8*<#CVsOTCeN1`iL`J#?SSEygy_{dZN{8LQ9_(btd_#y)>jXX;MQGa&Ph9(Ye#<2>hxF}bAQ^n+`(Kgadbc5#IIbY1mahYNH+apz^IFOIXKZ07{! zO+B5qH}&EtP(FDm!`rXAs6R21=dRzqZ!wqTB_Zb=V|)u))wo<${pl-sKNw@#2c(!o z#zy1ea}(pibsw*i{`!6W4*7}l6Ay4g%3rpZdG|3Z?U(9yj;8(ePrn@tn$Mgs?sMJe zz>gun)K8wAd(W@36Z==ecwy)GyemI#aXymWz+TgKFR1AG+8Wu~NGUcY2J>P$I>PsI`9oWz#nQP;qT zx2<@_Wk7B4@yT+P&)cOunLjxR{p0ZrVKQyH9G5INWs7H-pTu@@M)HKux%%aD0sb}q zZd>RY{iIyR84O&K{9V#DmjUF_uQOjU?lFGUhyGAm;sCCtEOQ>X4V$XEEviHH1qV|e z^-@n99b>|Z<52A*o=h9O9cqtbqLA6V=51H|s2kj8e`#}7(lb7nURKl$14r_cG*(x1o%S;Bi5)-ER&b+dz(4)VG0AekJ!LT#V4XU>FFMb%QpVAr z+ym34xaQn>f_rL%w~h7``Mhm9&m(ikT+)Wj8)Z{|^>grhJfmyyoaT%%q%JQTnN|N% zZYkGg9NnjT^p_NP2synZxxA=nE~=dug>CSCQ8EdiGiR5j_b)4MmC1QPzmY@w<~*P| zq)eCVVm_Hy67_q1KF8EgK5T`MH}H?{#)tQO>J#PUGmc4rmcG+J+9;XPGhmZ`Wjp+u z#;~f7)IooXw(^`Oeiw5=|J~Oq>dZQ*-)#=%<@uBwZ@0#t=aBgv7i=Qrki6MGWGK&R z8_!ZM{qr?Jjl0_b-mmBfW#FqvJ&ad1XJe~SPRKg_jJ~D}BO4@_2g=M;)Jxw|Cb$>a zi*m9)jxL9ct+?8+`D?m2&q0eO)7Dn8S5oI5skWK5VB%A*_~N7|(^$TlSYDL?WA z-%=0HQ&t>RhPZ?AOYGtFK^sEHMmgN0 zZW7PZKl;Tw!lgpb@!MrP^^$ORLGxHC%%8&+5dJRg?53DAdsM<^#hydXvH!8(T;?VJ zj3+qAIZB+-U#?^)RMN&a;{yA)T%=<&VB`M1#f41?e-;eUD`l$Qac5gH7;Tgup=N&nTerDay0gM|u zL;g7Uzl>fllkz9 zHst2&&fs&(nM`S(76J(p+ZFl3zHZ~*cedJB7kJq(gNrD$L1&%k9d|9TFYO?!-c zq-;;hKYgZLj{ZD(^sM(+bDm?1ETThsE_euRg-ntsW!3eU%HkSr%YJ$u|UO0UnC^30&lSmyR#xl-f`9h5OldZ!_(tKf3Pnf~}$c zL`;tMBS++k^N3}I55zgL2QDa+`V!~K0m+xi0~s6fXT4132w5*UlQtJ)nd9YU7wx2t zl#Oq|dEhH-IOdx1W)CX9=LPAWlADNL#&5WRbV(clKU@?~T-qjiQ{OWseiGAJFMDS$DL;GQh~xhd_w+IH z;=3b<^a0%rw{Qeg)pCt5U!~WJamzLLEX$8PLw$^^#)~n^gy{l{E8Bp>Za4xmhz3)-Y-{I}YanDQ7cwuZ{~cA;nJqw;3TJ_jEZ?Nom6 zxAM}reBRp#R)x3Z8F0sS@{{O8U=@6Y+|sV}e;m)Wn>G}<=bYqRNPC&rBBf4*PrMH4 z26&FLeJ=Ezzl%|fU$&L!D8t7T4$yPd=Ukh4y$(1NI|2Kr`W&4QYrZjdUY5!M@4;cw z#=w8|^VNJ3_p=?yT?P@bpL_NB;`W|JlFpT`zqX^h zo!iz~K0sHyAbG#=UN1YhsfSg*+f#g?^Y?mN<>p>W@)6$E^IQ8@9XY?WeL%qr;(_h_ zrd`oS=SQ`hb|k(zCO2{Yihf^&f0VfHd9gLN({?yU*VJz0mbLg?kIxtdcC7pA%0vGX z*Uk;nTNh=cFz?K(mrYxgF8a7ce$QKKA8}4uO8G4M%&*aAG4?(ye-$s3Vyo!C z<}K^>{uXVZOva0T1@9Dmq%mXMXq)#z`JIQ0v8c+6uGhS|tQSY;0i83iIk$PAyxjlc z@AN6-KIO%@Bdd9jXW$X;(KhaJ9(htO@zq220{g)W=OvSs+j}r)8JK5RI-R#Wf9(H0~XFL8@XFL6NcRTY=4^L+|!3mMhh#Sst z?I)h-D_-bbWhY#a9dKU!K^wrs`7J#no~X86*xJ*td;1E^bIx`8ieBXyHU*dm`=p3V zprf7B)c52-A1~$-9#CHH(^q85aa-Ur$BSbrUq;{TST(+v}4Sj>`ai5q6K9BDS5?_LsjC}>h0{<@a%9}c}RClJG?)p#dDV0@!&Se%p zK=uRAi9gx_{{8(?*TZ-=hs`!_Q)vZvnny7``H{T3JbzG<edK^pQF6anw0wIL>{1C|B)39yFdD-KJ3gLJooh9HY|xR}1VXzo%`G z_~&mJE%8qt&eK$M4wzC~u z)7j2!>}99l>E*{$n|fNsd*ThH$}Nf?$~VAfz(2Sk-y*RN2jC-zP7mGc*Wj>XJfq*} zs{W3B`s_G#yu$eC3G3jhW*9bpT7@>-ciau6~Q-Yk8=!t>iAWe ziT}hymHfKghm7X6Qh86@#-4l0mCG;sB(TOaGj=%H3t!^K#sVM-s9QCIoFdXC_k>#?$G&uRQVSjJ?1HX#>YeU(S{!g{ zV-G)`+1%4Ah5w3o#St63>s)aK@eukSE+Ao(r+x)XsfT?(g8D*S@uKX1^gnYHxWdlp zUf9+=6Za!8d4lK8t9-_t-;9srQgwi3>g33^{0%SChUBTtle|IwBybyc7IPi*!jU=g zIDpCk17W97kCNJQMc3V4!FJJmMVE_r89d&pJiH5c3;9=~PV|k(&Lz*_AaUy00|&?; z*HxEa({o&-jiuM~8rR9kZ|VS_uHV38k$nBhIVfoF=BlTqYId-le zi#{Mfb zai|(Q?Nx(Y=@0m&jTxJxtvpBaI!nr{YPX*8J~5WgA=SP(^5;55N6HQUV=UohrR1zqI;c|T7qq|qoMTD2+bR5S>1=1^ z*MG6PkG(bHVH-Jigbf`r&w7kpZ(UzrZ%z8VWL^3Uwr7WSuoc5w*#7Aa?Za6O?a-S| z?cfY?!rbQe(fpQnc!9WJSw}mzs!TRPC)otp2VFg#en)s+hCou21j{?2<`qkfLaJlIT50pF>w^W62cu7ho4 zjqAA{M_%U-$!FDhQATh9dM|h^%gg$l*T694$zDKjGe%&7eu6Q_w~lcgoS@5tBOIf; zM?Ub)9#Y;-^l{M`Gajkq!DiYMd7e6)y*AX(I58&vT+z?OF-Pw^ZBu(m&XLMrI>xzY z+qt9|2V^Xt;hg>>U%?H*fzAuqN5Xy9#Whl?j^{UiEOP0#h<=kFImEZd)<^Eo2{-2@ zmlx%SgOQ6niRqQg9`77FcZ>A?mhSfTTfJ@boCj^(xL0h*D|4*d$PLzY^iJzIW{-6m z@t!ptINn+edd*r6_?->x+sh_C+}yUjTHp3gYG@x!YhZ__H?{-Qo7)Gon%SX+&F#pt z7V;0;+Gk7KI0qbC+tI#xtD_xX*U3)4tr)_)-8}^ds133MNR{FP?qz&{YZtLq-c{UD ze37`OpXc7|=GaW!W4q+OB%UX4j0c=iU|3@Um+;#;Qpd!5Dc^9Oyg|Mg@8H!^{D;ks z9_L=nQE~ytxL$CIb475C&%b!tc|mp;-yXQEBguV#-TPL0-ro~0WGmz`Fp3d!%Y5+e&={%3ph$e8ndB>0Q=BNOcFit)(t^|8TTQhvrHWh}-w@Q=*# zJFpM;g&cx;u4A*rKGm=x{F?G0cP@*P$x7MUV4oP@`EA0LC+Sp`Q7Iq)#Ku0hW&SW5 z{rW35=#_cG{U+-S?nm#ja$&#A>-(+!$Q@Sqfj6!3{S&S6ka5<0&`2xq{abseU%5?w zqM^MvqJ|xKy|(S2P*3(i6FW4ku^pCwaAaXi`($xT=YY?bwYD#ox3QyZ%47?aD`wc) zPQBelK0+7y0$r>^vBk5SWFP4FnN4t#cuVmIu7Q2W|7Nvc*T6RS&xsGxcR+r@W!?w- zx!5{>?Px!)2tT~Redlopa8Z*xude`wz|Fexc zGN*}0-`BtzzWSPdOkPdCK|UB~wyw`Ls27CUt#s4M&%$Le5;TNCx3N z&Qs4Iv&cTNw2P8g{QR?9d)w)4!j|^$Rmj(`5Oz)}9=ChN{WfLti?Z|QTKiYGSldy1 zt%GpiWy~(?K6a0F7`xv(PWaf`zqZF}KRDOwKRC-851nZZ?w?^b22Hg_{a&-qeTLWz z_cyau&)2c{M=Oo1W$%x#ZTqG)vHfp0vO{y5*hlm3v%?FU*%!;&hyz;Nmn&M!CXjz1 zd*I{-;a^+;?kk0F5`5rx{+3?f;{`Z^`;Kw(K;rW}c7gJeO8tYh0k9ju?e`NggGh0o z^Xf87xd2?}vs^##K8I{}>2v0m`=#8>Z|1}K06Rihh69k3&P9LN$hhiuj7B> zuZFFP+=lGBj;CIpR|;E#^S~43C+I5ly}v-T`$|S zbcoHH{FFU5Vv={z&ec)=5=2Fy13x=1J-T)$JTkoUaL23iPd{( ziPe8#kY4U~iAEW!omyw!IS@+U`kpZJ*)<2WB=F z2Q;Piv!BFj#zHrtZi?nHgpQR;GNDM7Xa^E;|NEP&T9Vw zse+^If(qFR$qDHrfa_9Ufpf+wxRHC|djvlOX9O1zli;3gjw+iVd8DJc-z=mPtH^yjRI3Z-xL=G*ww?ZSjW*$*2S-P~#4lz0 zz9NkYN3K(THU6;~@a02~a}QYo^T-(a&P#{;eaVx{s$|o3j&u_G>Fj&5qjz+YuP^M2 z6V7hxWrtP_vN=pX6sZ2k|V_YVsDhpg?m zL)K;dht3PN9$#m*9$9Yn9$jelAD(LshRqfyD9ILRG{G=DK3~$xdEl#+ZM1gK%D$03aCCLMDlQ;K zkaj`vLKpcB_zdC%r4u^GPQX_H|6reUu7USRT7M+I==cwuGFDaB@Gpv319`ZfxG&{^ zO65uXd#qAx4dCB-K={WtU@l7W?=cDHO7p{h#w*%G0sb$^Hbefwm#;%=zVP`||AT+_ zVPad{l&twXP?Aybo8Of~p7EuT+vI_&bCsJI1~aMexu4%Vs*-oLqg4MUK6&0TTF5%G znDU)R_E=TLAccGMRjfymS8ee262AjW(N51?$VmTKj+9fqWFE@eJ}9>^QSoi`|NQb2Ai=8VmGmd(M|VDlCBe1S;#W-6Fif)bCwad0C9G$F-!iCM{pN5df+2)5;`z(jcy0u zuG>@}Z2#laUG8F%Ia4)piV)aTUaVBE3KK3D1d^V0pke_s3O&dbL=E1&;>kkV12d$Hi<>NlG4ocS`|yX=JPZSi^Qryhk=bE!*`*9jzH? z-C%kn`313t`04zn_JwSMFP65Jf6z+tLd6P~wY0BRXs?mh5s$7e^R)xk4^FJ>qWuM( zw2q*?hw>Y+5%3>Q3j5d$aKM?(;sas$RKWu$-jNR>4)7%GbG@R#yW9K9?`sdT`@uK5 zpXEQid3)3E=+H#!aMq#XmM!2@M-9>2i|?F`=|+vt0ki4zF_i3i7Lp_^Uj7iCaB zWo4Wd9nQO;d@IjmqQ18;Fb@x0CWcDi`9`CD=ei#Jl6sdq!7@j$*X=0AkHmG_Ro!2% zCr9Sd?E&G^pHaI#1}98jR$t?LB2hl$6#G6yZ}WU&+xxEHZUab8*wcPN$BZ3w|C08| z@te6pj?N0x$jxQ#pF5}bZ7O7Yo-1U{WmP)ky!8GF;ePXi$87kx(Kht8xz_EqcddN1 z@GjhUdVQBLzS}yFJ>c;@@L%q@$L9Y~`u=0#{v#`!aKt*#JYj=Y-LQu@{$G3Iz5myG zFFI>=p4%+kFSojnue4f^F0*?d77q+tZ1>0xsPo`FYw*BKt3PDAH5@$2nhh9ft$ICb zy}Pxs5kqU*s+Vfn_EGh0_v`iL7sw`w*LD0ZwetZ}9=?R1r@&g-;>u zbM2(^R0!wfC5C{G_w@(;#vUM5h!@}jj->P>h%qMa!Fu99`5}2DZ3Fl9nI~~du2;YZ zRp(lVxd8692a6a0v47SDzLngv|A+6FgL`o8SeDEo(~cG058W@Bm)xhGNAJdUa41_q z_u^Y-$Tru+4USLrKbQ!-CZ^#i&NI2KuiqXE^Y#?_7~NdxGw_i4<9Tdtun-(Td#ESl z3yuLj%brfhr_Q+t=5zhPf2)tahNfq4VIvgfQI_{X$7;J@?J2J3wb(mR*e)uYc2Y;^ z|G>V-Fr4Fp12V1&7Z8hg+ei0lbBwEahVfHfB=_?*ruYHgui_jXKz(k*!~McC$@6gT zx&VIHUtf=gY@@Bs>3r{C;%*hq4Q#@1^V8j6Y%> zrysQeE3Vjs8~=~p|IYuh0UQ3y`mO&@dtlT5wIOS7S{pro@1v`&w)4OWt04}ki7hZ} zp<)8_v`#SB>fS%o>JEO>>J6G?_4~eTtvU^|p`9Dsv`6l>jeoq`wvD*Uwv4SQ-=Ln7 z)(zgM6I;z1s$yla7lYP8R3H7x5o5-T?OT3&LmU?sh}^58sOm2+v$kUP%0>AHb3P zRX7*s;ct@1Z#1tN2lRaeTMG^l&l3Y={f~Db`DQp+&vzZc6EcS!`nlf&V_-h;9(;jL zMt0HbJRkZ!^fER;a6#())V+ye$B1MXd~=?^^SR_7ek0RvBP$=6r3{`c^}A^+IHuhs zd=PAI$5io5;-9**UdqV2s(4EM5BvuHoS*2A61e0DM`oOkwzVz4(R#;>nNSyUa6h*eupVvTAw8sZK&`*s%`PWcYZ4~ z?e0?Cl;R&O1~;Iiqiip~z%AG#fycyTU?$e(z$axSmeZdgFZOCaUy6Tkv$!n#02Zov zLOj5;UWeL18JuT5Jd@wT;ylZUGQpMjPyNWg_v)RmYadTL>3U-NWjgOPUicU0vG+Sp`pkOGKVw7R`qmzhzIWWO{nq-f z{m%NX$s{gV`zPzOT3oQ|Tjzy=Yp+>5*$%ZHS6o0oK;6feTb)N03wU_G)l;hd;B2cs zbcWUorz$o$(Q5a9!)o;#X^nb3U_HCEv{(AxVN0K`Y45#S+qS$`%QnAO)3%JOXWL${ zWxHh;>=6f$KG5F6{d(u%z|8yXW5o>*&(l6*?KS>*K})S6w6)KdwwI59O`!LZ!~xg` z@W5BZ4rC7yKRii{K#5qviS^otC{FM-2I1V}2f79aoYXOG1okVlM=5Y0`x3x@;vc-j z3*f!#9J_%%OKuxPEFohHX$R!-qT;vgS%?^+;su&#?0$4V$6LZf?gikOSVqQ4tShI! zg9ic&mMS>_g7au*NMpAi3`--|)<4$*Hb?`PKkiJ>yAx@wGRs`|y>r@3%^)OUL8m z>)7#i?cWg(pzF)s*FWese%V;<=TS@_d%x|3Pptc#Q})2xTlUa9|84iL`!DOi=9cwa zBkZqM0_QsRC$0L<`mOxV`smnA*E=gN&_mDl-ynWicF~%?vRk@;nQVb2*61Dcpzd0_y)2A+$LymN8eIBv9Jf+77*s)fK%%gFOctmjc{DoSYtQ?-@2c`UPbmL z5C;e@D8)Yb&pnDBLsUL&e^TmvaG#0!^!-ZW4z4)|=-MUCHQ0v>(Eo1#OJ<3Ar+kA0 zuod!&3Oe z3+xidXTeXjoBRF@=S3o4w2Scw8{PY;y!0*0h`1}Zy6kqnvqgL2e((ss12zQsCh=U@ z`S=52=ZAkEK0W?D@8OV$Nms}w^rQ3$-`1$uj9seuwe-$ey>tKdTfJ=KoQJeuca#kn zzSKI5*lO+Aw>Rc}xAEH%)7KuJPUEzXNBX{#*77@REx#ja{2|5s4!O?mFjGGMs_QmP zKK>Bd`2*Gp-@<<1)i)jUV874GTS_;q&kCjG*R2<6lB0!~qS5vMw;g>J8Bvq2d8`2fSf*`i-|beP6TMU7of^ ztvc94UF+D)Cu+$axZBpfSlc$eTG!qkUDvjbt8d#U)K{#qvF)1N$o5WaVh3lnP}2KG zb6Y4zAbVhbTg3@l>UT>!vY@rr6x(QjK|8krh$DWbxFNQ{mn&rxXy4H{!unUM+uGM_ z+PNLTUc^kfj_CX#zF>bcd;s>b7t&@pwK4V|;3q_^P&!|8<_Xy?%)9R& zc)z4`67hcb7xY}%2KWx$UH@YP@VD`N%}cUYg;mLItXEabFj!73fqC%k@n^|ktknst z;7j-7@1utPOpf5_STEKW{O{a=ZLrE(Bz&SWk#{fxP6Mm>C&2;KM;n}PJa5<{=`#l( zs0?8t@ycF@(wH7)Cbr$Lr(NKl{-AG_oQE_9#0kMNoKtE?`0oO{v=uBnhOq&_w`_37 zd2tVnCU)aGoF&fnKH!(f9-WBi1-HfeExJAKUD+wF(RmAwlYIR4 z)lKPo{T8Nst-NkMh41dmgmK}$hmJj#UbUX2C0DJ-k}KA2(Pb-NaM{`~x?mlbU$kzr z1zL~)$m%_{)@nbtz#2Y0&l){A$LbB8Wi`bCweNq^Y7UxaH3v?yn(_x~^c`old%tE) zx;$b%%3Ikh1MjsZPv2v2zg){Ujg&7irh#o4Q&)aLUF|EZZ|}<%*gd(4{DQ`|Pd)+o zKO{c*XpT5RcEQKufWz~&hPWtfflrqZCnQeL&OYNEgcWV&6KLyAEwjc!B zedBux$&>kPv?JRS`kcQBz~4~0jL)xhZI}Fe?M)Vx;ex32}s*1w=@=d>4uZ(zi`D&l;_eNM~QKPCNs?p^I^c~5KF z9)l)E)x|zoGQ?(2e90agIomo4_btZkwpODQ)05uE$1j(@?*Qhtme<}fuXE}Ca@qN1 z<36;u*!z<|w_Xb>6x;hx8}iP7TmN;~__Fn7+mkr&Bb&aj@ZW32HKi-oW94P*zVwQ9 zS#r&~Ds^8B-Y;4grSb)rtm8Z#=bpC?vn#FbtW(x%)(I<{d%-$QIcjx>&9yp@%(4a# zFVLFceBUovYsgG-fOtUb0<{KCvl>b@2TZn_eJ5D0-s7!aw?A0NE`96|gYUBiiVLiH zuBN^FviyOO#0Kiv+lmov8CP3-2oxKbq*$S11N(>*PHA8tDK>CumeOo-fNX#x3);GW zaAaW{`$Tbpj~8ig0c!^S4g&8Xv~e2%JHR;syMTD1#|soMlxPD4;#nakva7Ok4x}R7<>VB|I!Zt{pbADdeE{NmLE1r8v?`K^kE+~C~ z{qOqUZT&*#bq>}@i9KXH@D%nucp^nC5KIf_N?dd7xK0a<FX-UI7y z*#olm2WcIzpZxYdvh%@wU+MZjvgxt&1NYML%Y^-9m#ue!_ihU>TG#m(tbG1OE1PrC z+Ri>Nyq~ppGtXF?H!H05^wZW+9ME~nF=2a--S*2C_UqO?Z19^qAr`&?JOBrHd_d>yNyaaz zT2D~CAodTiM_Dl%;s2EHJJR~BI3R6;#D3sE=0DZ~@E@1;y8AEf?sY*guaWFC85u|(;1_7C7sr+x%ir1Y_|2`Jz30>|igDfV2K7u=Ir zPWhy6RVPuSqqH*A>n zefaoc>x2DXN`27vvg>=xpYOR`_Ppc%igf;E*YREFU$$=Z&RfU%XQkJbg!wizDy)_8 z-sa6y)_mGG)^f@z>oDz<-S^B|`*F?QcH7_n+8T7|Zll)iu*X(?YE5*#;jsDEq;MVtv~odtkSGg1w3n?w`?E-yvvd2ep5I^wE5M zpJYJ`JFHj$dj^hZPf^4LSUV&(fIq2RP#ApM95@PH@24&7GwS^sQCNz*#*{Y%ur#gCX#a{2|AQ z&pamHzy?@NoPep6b#zw#m5x z9INe#Pp)MmA8qqQok|{OP;LDWxt3WkO!UXyi}Bz?ajVqzqn4Ke(VhDx~$}F zQoAlle_s?gf`3x3{ek7cFY9#Gem~DAzCDJIP8Y6&6Cw@}ald%}{5H*{Y&+h$^Y3j* z-xI@QO&_10{pn}qLsnoDN*0c7=wTb@J!H?18f9HZuC#Vq%WFSor*OSPvAo!?Unab_ z)q6T++OOYUYk2J8b?giO?A>P%Pbcl)8MxxIJ@EFQY>3wK2gt`Kwg>Jr^^$M@eSIIe z@46WKUcUVNN-G!k+s{5{WzzF)XUM*v{*83~aceQ<8*4gMw*Jgh)?)Yv_RBUe*q{CQ z4*SPn-)lo&dez?8cF3ODc*F)R{lxBjZoOgvi>&!$3++C|0~>2^K%#bX)E7T2=?Iy-$PLN1o#8T--=iP>x9Gtd~HDM2I(tc7ohh^>@mOx z0OP6u^N4*=DH%vk;9DjYn>*@OPt~;OkQM#G76#ejLR8EY|_x*xwYZce14bB59|C0K6No`2| zPn-_lKK9}ex5r+9FNo)}w_CW@wbb<-Nnjp(j&p2#^7(#n?c+Euu0O>!-WlVaJbeT2 z8?E2r-&g4ONv#zhUEfc!y@zbl8^g6p^{%zkJNMV;f$+$KDkW?9je}9k1W3ca7>7YXyz$1N;HS1wNb;9KfCdxZq>#0dc@5i?Ih< zhd&?=_)>8JzEQxt2ks+i4H2Ip)(^w}$0s-;?8iD`>?yz=fD=5qZvZz04}kTI32=Uv zxtARP=er*u*#Y|%TH8Ix{vXAI__oP+`g>oz|A&qFL%e%jyucbV9&#S~zY2?B9=sL# zC?jKjzJDGpDV5sX+;J69`HRmy2ie)Okf}VPpZ_qEy>sL<-?tk-S7K#;d$};S$%8k8_n}^&Aa=DT3_C? zcz{ix@Pa-1>TK&e@@;E7Mr-)myVFT~_d3hZ@1(ds@xAgfhpdxg`o#O%%LZsCeP5=v zJl@YM*Z!S83(wkv>%X(1Z)*)-`}BItx9=z4zMp)1aNk?{zPI9fy_a3JUW)0Fi0y&< zu8S^N=LORF!ale!n^hs-zQWp0Jt_PCsBr(4HJk9YH5-4-+P!hi8b7hl{_fX(?Y6)A zrPXZR*hVg$WHUecz((vlY|n1}RPPEL)*itRZNQ?>t@(>vt?^^4tof76tl4juSra(m zvBlznMb_k@dDd8ag&Sy}Q2ht71*U18P)Tb9wFfEbU80)efZ78lSiOE@tzqvMt*mQ* zd!k1poA&5kw(^DF*t$R7DO*5sLd6K*9aF>JV=tlN1N*QACd($!d&hgHDn7usNE919 zIQu?3pp^Cid;kxmPr$oJyl?P%vA@{&63Hh3|6i}dH^5G4>-zpE>j!Xw;sJ?$_8I0H zLD~S0`N9TB{g2)U|7SMwo}#!w$1|JC9slg@h8M15=SoIX=jYLJRN&I(URdRr_zIkd zz3V!3Gf7KmD!fk^#JD|^)cH{8~Ix_zTcsw-OgbJ{?m?k{Fk&dZT7@>mihhbq2Iyw zMXl9c)O$#905L#xKgr*<F!*?CB-)%l%kE}av0~UN{t@TV}y(`q>ndR2>spWPb93WfZ zK5;-}?G?fwfCCyoG|TEgBwt{d-W^hWun|6i-Xpq4YX!9j%QqM_UOX_)YV{vw&3paU z`gHDSFZHf#vmdEt8=kwzw(6aNt*_i`Tl5X0cgNJxzM;CdL*F9WJyBes_~4!?^|e08 zJBN*I-;4%|3$TxX{e)UOxaT}@FaJ<~PvIOf0LcozKQ_P__Iv1>fB#FmEMtAhbHoAC_Kmn5 z7;?Q`U^8`Ism;r?o)10EH-@x-HTMWt;a_>cE4T{1FWjdcz%>&3S-OyKcRQarz9W9% z_l197AD}+4>GlNt5&k~y$aueV1?5wQ%Ei`kzLQVxe*;awsUz3w)84>mC-L{I;VJg` zvj-}04{k#rQzp;yn|nMDp0VdSUebC!iP)bfkLmLa91-8rgacT!yPz__zVFS|Z{q*# z!w2(c6vOj4zkD{<`*;ucqYa1k;{vfzt#~xkQ?}+0|-xJRl z?)%Ed@4HOcXWd?UpSWHRrLNlJ-)WKT{P|kDn|Iba%&l~N-)6>XYdP(tHJ^Oknn=&r z8~vr#94#Au#6i2K&vg5npLejo`0>A4Kuy~?QnA9(cgrWZOKSu*ZMVKhv~QBu4JV2ZCfBw-)AT)(>GkZuEWLL)N49|Q z4-X{ntBXxee5rK;@b5N&u>bW6eWyTNK#I6Pu{MAYAp4&^0f~R`j{ToL0P6#+ zC4zbIe^&P^rT;58mlr+&_9FO~KP(wWcY=HLF5k`vUmmXqV_-%{#})dq({_jVL@@i*6~*X?h$&9O{d@W~6@2lj+5;jpwm zKQC~eZTCJC7byCc{(80T&lm{9>KA`^vwBZZo};&`{e0?94xs+@^T9Req^kY-U|l$m z6gEJ<$LT)3zTP+oFBF{EZv=_7&oSWAA8PNOO5y_&+7i=UY{uujyxR z&3?#U8aL8-OD9!hJWz2D&ahCtF{3zHr}8G5yx_ zP6_*p=TEJ$=JM;y-c*`&%<8^;(CYkgztwnpjs0`;$L%LSZ)Eq>sA-Q3f6$g4*)5@w{kCsIoBl)%Tm7Q;5x*i^U~~<=Q*f7UeeGU*e{2ogH{o9S z1Bw~){Qet^!VjhY@d+})0r&*07hnT?sP@qtxJ z;)Apa!Z%>uko5x1zrIrv>jtrJ01h~@zJoYG>x8oVPYd@Ix>f=174NImHQ(pSI*?N3 z#twS_S2nZcJaGWN;v5|txaS&q9D4;G^LJto`<=uyiDAycNajhr@Y^xzWkvpcuF3~U z4k>Bd_k5FVhssLKI$l*@@<8a|h^L3mkKJF`7vxid4>$&9Q-8;A_S<8Jr>){#AU@#l z)PZ&SLp+XW`Hi2>HGao+?bDHNPcBFffCtFWG5(Haw2^20TRX!41^pd6@PCFq`ttEB zbmY6bXEcu|^={5dt>yE5?X&XrzggGaHq9Aq!^ga=eS34P%czaQ@^;1bwU#fucOZUW zn&j*AUM@UPj;{BZzV_`+`_cw3yJ!yz_q?AsKx_D=aXtKdkLL^by|h1%y?I@<&#&v! zu=Bet)V>_W2+D-}HuCY?&XmrdQE9EUj)%S9c6z0?pL)ugJiFCyFP~sN6?d%D|8@I| zf2v{s@bh0;!;VdC!kY26;ml#1{rTrM`QszP|7SM-!!K>Z;V*6S=ZgDn+G!03jJDhU zM^C$>>wN1y|G2dr{+=~|YNfUO{TgfT_}BR};)ADU2mBU)Kpf!lfQ8mr96)TaKJO1X z{^vUOYswC&q3@2u0qh~HasPC2z!b#?Cutv{Vgmz5Ta(^TS+}0@35M3OrO)1>cZ?Mq zeB~Z-KppKF5bnoltzcYDy;o4j*9s0yVJ~q5Un_(Mh!YYYIHLCnc(;fn_z&zWCLkUl zEp+)X}-?2Z>Rmt^S*xHV&+L}GE=eq8Aq+{wA0pZ;x|_RiH-J;wVt)V z{+F)S@3&K|$Mciyum1in`|JPxuhwx$naw-2#8zMV%-%eD#AbeZ#Abi`iB13HQ=9t5 zQ5(1K6U8oP+27vP({8)1pZ(}3Z&;J3WVg)t+*;|o!p)vuX|0}HV=bOrZLP!stz-){ z7Y8(bGB_aZ0DJ-79csY41LA^d znE2;?bBKJf2P3HP~I5L@8%Mqyt(z&=2a35W;K|BfU1+I(v}bbRmuICHGa-cGD?%yYP) zTIZ6t8q=JoPS5XZfNh>Fy_b1@crSS*xe3fBFQpyAF>Q(1dw`D-T###qV6rMt!2!u1 zl;?d^9`dE#ka*@diQkFs%m=pJzt@rbr7?QW(d8V$f5!gt?Xl(07w_M)U#BXTuj{Ay zn>E_MM{Mt$V*Kpe=Wkn8=>0H%A581`=ivnH)8Dmlu)X=l)Ar~qlV$6#QEX4|=8V)H zJ?-6TTY~#`(*L}R%O0Mpcl5ND*Os`x_U(0>d)n@oo_E|UwikF08=sh7>ion$F+Gp# zDW=y&@%+y0&(#`!8UDTY?zEjjZ104%(7v5k()+Eap0>u%?6;pZc*Xw5|88%MT0dvw zr@n75j+kwK|I=UDzt;SBdw%xwwxM#XEkFCQP5uVq!ul#}{rp;M`TS~YEgm2>|D8DC z=~d1FjUUGkP%J=e1C8|la6_#P)K|KD=zOdBu)aI|*b4E$QgOgS|K4D&q2hv}bL}3z zS9A~W6UY{*DO=!f?IEhscZ|M4__THG(cPZxThC@aCR^ae-`IPv+^Khp?zSx>?zFAq zfvws*xK)3%V8?{IzFxRnYX!R%Bix5Ap!nc^zD-te0N)`3`*6Ug`VI+y%Ybhff4W59 zER#O~{=Z&@ZO~f#h~R{_uJ_UXncxD>`A!-8dx8UEpCEQX#s`Q6RuU)F5&ZM*(ENMR z@xA@fmFUaFT&|NRe!;NoYA}zk7nTy2i7%cHjsSycI|MHzSNQj{+@`4J3!Vv%AQn)~ zF?pVxlUPig#=d&+k1bN*86JXjRA$;UxpzNfg0wgDC)V?K;y>$vOTc#GKXIPdk~_Fh zBIcJdy>t4TwAl3ickT6UE#mhTT8B9+d!D~t$vuzhi=$6zZuo}WN$ur1B^v-9IQdRr zJGi{REuQ+Q_U(<+-_3eU`*-#T%X{_x9mVpb*IC!=pcr2}#r4bh?glo#5_@;c9QXPT z#~Ytmw>c+m@EXPT^sNov$?d1LyuiNJ@$l_Ez8CgB*e9+>V((68?0m)bSj#J84S$w= zd&TvdN#8e~`K8@A^J{A-JO92HKC#=&*4U5!~2gag38zDKr4 zwg7$sYXb-M&5@7i>+cyUM#$eX!47~6KGzyye7{`Z9*{499{}!2$JX-w5v>pE9bCY=AvN$pL4DfA0CaKhpjFcQSWLfAaTsgxeot8H^DBPrY1a zvx6&PEba#nIPMGXNd2C?@xygF$5-K7kcaZYKHnoE=I6FLWeIoSiTtV0(>@PwBK{yQ zE3nKxu$b@h(Z=K=&U4KnIgYYDKhOLiagTES8+=6@B2U=+j`Kp_bB^9mdw|$I`X4(0 z+&jjl>(4sYb(F5J&|bZB`ujQjy=(a3v|@hv{a~JVzE4ZbMG-uGB*#q`;u?`!txdfECNg>&qDVtd&6?ALFHkN?JD>oV)O4O)HO^*!I<7_jC$ zfA^mCJaA9)cW||5x7V`RuNUihxo1!D{JH0}XHW0rX#KvW_UtvEcFLM*Ew9P+udLO~ zV|L$&FYQ-dw%d<>Im>?X(^2-jht}G*b)VSByUy4rNg%H z*e;v>&0d@S^&Wfk^Y?*VuiE4d4fWf9!y?1sZ5=;5YZr zw_gsNZ?_MeXLk&dA24i%{FJqNuV|&t^$p?&7yEiaJ*^kiRg9nx`w8_o3F_WI$!g0d zsH1g)2K`>L7TpKhpq@=_{3CU2?H{y0FkIgrQ*3a{XvG8M59}DP{e$9wor(?a(l^UV z2lNdxd;jY=~{lQop0RQYAK2JPQ_W#WS^XUEjTk)y$Q;+i-9O5T~&D7l-!IxvJ zz&mRQ>0c)gBsb)5xTUIWojZ2(`4aQw&+Gnoqp;%@ zD|EYGx}KOG98jV6@Xqkv9mV-r*TctW|1SIVPik-0am8g$%g#T&Nq-}EO&{AZ>wX(P z_5~ZL@9lLRy;0a!TyKoNxhMRi`?2wf=a&`l<+T&;+bf>Wcegw6eh&M0rhaMtSHynZ z!5jXhn7-EYlpOcc^}QVT*F2`*vjq2Dr0+W_mfvv!xUbauz4q(~_pRSNEj$0Da4(%d z@u>Fe9+N-umDL({!2bT;HTM7fWQN_>bdk-QxXV6%_ai&9>r0zC@olSlXG?3@vAz9n z^2@fV;v-vjY_}~uxyL4-*lANL4%jo>Hrkzip0YpxyPo#re|*(`e%Cg;y}=3lSgn$m)*mp+8ufhII%ps9@B7uW`A^l*p22(Voe_8I z`(t{aK#7>Z_VIOO12h!=`3{+UfH%bfS}WkaVq$}bv}b^ClYS=5f4W%TC0x=}>w_)y z{t)YgVF$1`Amaky-rp0_nn3Ooioazb8Pa=%r?pn--yGyyV~PQsA^FnY?G2hD2sKx(%)HbV%zae%ublDdKUca z*~EV8U~tcQUI&}JBj+}Lfm7bgp-rj#vHjsH+Uwr|;CGQHeE`Z#%!h6dK5(5++^@ht zN3MZ$U$fKE*YABjFZ90q{lY$F@=Z_{pH`aR1EMp*6a`OSoxf< ztoiV*j{o*AX>X&j-}Z&|!vA_}B`#?B`?nMuSY6El+9%Ldso4`Nwa0L!{buN5`}v@- z0e;nAJfH**+%a^C)p~5T)qQfU)l>dj`u1qehxFaCVTgsP4H5{bx4)q;j4ZG?)1Krx%$OmiN@)r~veB~~C=hZuG%a~g71@!j{v~OtVc-a6G zd8e?6VuO0WSbx*-qq%|qkM(!V4lm$um~9l*k^Bmzc;A8K>X&M z1PAc0aE0C*;^+xqK)OHo0kaRtu};j)v4+kQPJ|<{$Tg4q6aOoIqo1+;!BgqCzS0ih z2u8s%wt$x*Y{N0>Ta@Z={zgy69;m}Pq0swb*TVr31K>GqfB&s^c%Axt2T?A*N44LA zu6JJ0wJekJ^Y5u;OyDw{pgyC!liz$lPhkW2`?un~&6}isdt(*b8?C>M zt@rZENAumS59II5ztUa4>+0c3y|NeD1ZOF#|()adQ z%OjpItgn`xzbbTo&lT4qu8)7OJv%)XYdvq#dB=VSt>u-^*P5Md{W9tNHZ!z8U+?GK zH}$C9H|a}j`Q|Ze_WGxGcfXDH5C1XOe*CZVtZms^TQp;@eYxkLeY5|dZJ7VA4e0WQ zbuAlVt5$w!pMH7OMoph3bt z^O}|R%Lb?Io)%x(FX}C{zy3)NyY0_!7xsT?zijzCD}VDdYd8H1Yw^l9#s1e@JMjPV zJK})gf!6xn;`w#fR6am6aX_=D!~;($F8JhXYk@tWbf;o}zZ|mAel8o}XZ`2e&y{}B zZ;stA9{Bfxi}c-b`2)XQXAPfTufItkpFkW?m-R#T56CB|seQ#Y!~=YLsIK-6)EYcd z-ya%d_4_<$ExQi3hkD)`&!~nqm$qnt@24D|x;yPpW?ytM=9XyaWv-5*wy&C(~zJNdDHhAb=FahTB8II{+ zB=*zRzApQNzth3rEg@fNq72GP+njTr=a_n!Z`!-xqHEa~%Ann}3tz&^5r(;rEdYk$ z4%YTB==>7rd}l+y*~6dv_^B5?55E0K-Xifnb-tGe&U0^%^MGuDpQRqR`jri(}&t0#_0Py+TYTJeS5O?+xs{7^i6%~ z{&v#);5^sy@bw+{qxH_7&dVkqv7Yl!+c4q%p^e(Vvq9h0d+UbY$G3(vt66_+v!6e+L2AWY|L}BtXJpJHfGXB+xhWv+kNnq?cIIS z7O&oHV^+?zVbexgjqbm-zxwCL?5}@3!+u@!L;F?3llISbEA8hEDuw$ByYs%S_V>T| zt={>qE$siB{pC-Z`g(o&oDH&{1$Ng^tqVN5${Ibh-Womqmg0np6^aAuXfI*SVX=Rpj@tr?4{ASg?ICYigF)l0 zk>Z0*dp%=ayL7b|2GqBC&(yGY^sSL?BWi1nu%2xkTgSG^9@sfaJfL?7KA0i>KZ|%^ zL%lE1*!_URiUoYOl)q2FdSKW9tOI-x`+6^kSOBsAZwmkagyMjtlj4Dl2gJ8U`0g;@ z8jC#viJ6P?X&uM--_q@lA@nxq!VLP{@uS~d!w%p&wu1ZnV5>;3qrs|vrwzbf8uWC0 z+lF#^KGyE9%0J*f`LjLfc;_ClE*xf=V3|B%m3HJ>fAT_<$-bSi*(t;0isaQfHU<2V zxK2OeqQ18Y2cqv2@8qF;?0MeFgAYR2hn)}pecfK~Q4zQIz4_AT)#rMLXR~bn%>(rg zp8lQ=F@3#raa8y}_73rUy`v{S{%mDeTmL5ecZLi1itUYl&pPrQ4*B{jK|{2c2s^+}HEZ^{1${ z!`f@_&3xT|)4-$us#@co`&FFEeSgO1bBz7np1t8Z9y2#~fcIy=r-l#S@%7f|2Y*iw ze#R5upw%VT$KUr^{h4~9*L8nh;`F!mVB$`2yG{?b-L4U}Cv|xH>-yrI_x0r)7qol% zK~-kY*Vu_$boSh*diVX$^wK*Q^a>iD7trsInZ8uX#e>x1;R-cxvqE>>e_X9Qf1#G$ zzg4rI#QI5KKkXge+is?=zp1x=_xoo0U6TjZs{hE4^OT@3%-(Pr9pP8Xr{<6h{w**Zsqf5_dUV1OwZ+f3tltr0 zfachM7UfUszVgj#S-DxQs<)~gJVB>n%pM(gQ2i$#Rj)Dohy}(Ev0n-6v5uO77)`)LoE?^v-I}eEbsBMAc@J1hN95-S)ip?W@`L#aY)9vT~ z%4`6~0Ir$GuZu8iyAknu#%8b!yx!Q3@gL&sM*r>G{=Arnh;jkjk;b3PHP`@uXMYBB zijDig`Ed<7gTEWEGoJl3?E7Qvdtc&@+`qzc@7|xNjl`OrD9>-iKDNPj!T2}cW6awI zIHwPJJsjS*wkHwQlDW1K4<327iZ|z!RLQ=q(m=ntJh=8r>btjCgm?Yfo_L7 z{^FP6>(4Vg_gtvsh5mc;`)F@2u|IfsKd$R}S@3*C8$MOt?w`T^-!uT+R~>}Q-%pGm zVtsi0z4YYmp(l@7Tcym;D?zhY3hs;H1qz@24xj%W^*l8CTfU_)k9j+A`YGsJ-qty6!=g<`)xT)Insphf)?JpW`9s@uZ|j%!Aeeu!|95H!x8E`A zBei&Bvu?SoSik@M{b2uI-O(yb8PxHd&*W{S-niy6b-%Baz3~ebFb_0!9&r+Q&zlcN zGXIn^XP;6Qn!t1(OP_TNP2k}O``|vwF#sodqH_Xn^WJ`QUR2kKC)9q#esX~wdVqSs z{Z(56{_n>Q_&6RgFVJq_GwdbXtKJij5)T}uUdWyzW|8)!$EYV9;Z;4cYX#^Ax;LyK zC%^{OFGIJ;ypiGo8eH06Ye#e<7NDni37jCiie6eyO^}@guQOxl4Qm57McM$@0zTN` zIzXfe@YQnz;@Y6&f7b-;<3kPbC^$zC^bPp`suBA!?#=sM=6P!YT>txlV_zDdtC%zX ziRaDj#qqxVxMP6e>%q^}{u=!BKfhWJP8^#@{Wr!|gnwV7+u?oo|GwXd)hMoa&L3gd zxb+G4!Lfy7jK%BS=Z0P#&jEH1Qll*}_Cx#+Cl_AR zi2n%tjqw7X88`v1-gWsic}dLg`5M^*cs=6(kmD2U8}sq89WeHTO&~7tUe|Ak?LUKm z{Q|ANcjkU|v{bL{s@K-FV>EgZI=Cg&tiXK+xX(emmq~7)LH#bDjT&$_jF#a7Tq?e zVB$Ho>9a>SHa|#y|AX43Tv3ODOS-@R9^KruO4nT9i5&kv-FP>#K6CrZ;286_{uFwK zQkfHwj;1ITdz889OO=r8B+Wic?teo03r+_7XU_ruoQyfolLH(F|A){49BRZp*e4c9 zAsJ1##~)U&(R|#?A>I(YeL$BSbF<{$Jn79^(x?bb+~}0sq18TMKM0fc3zhJ8nG?y~05Y00(f0 zc%QufOJ5IW;{UM&;6K&_I^K_U!NESj6MRQq(75^))Ar3^k-z<@rWVEdfinkJ#$9YH zj7#5k{2%bh{YZcNt1maM{F>Nj8|TK5wR*<5@%pnrm-oil4;bb%e+Ik8s$Un|gsbl{ z*7=SB*Sz1)htF!X0l}{Ly^WlNaUbXWjtAo0Abu=7&ui@W%>{VYX3*ziYpmaKPVZTp zKZco_#0KEqxq;{GyS5+uf7kg!JumQj#l-w*E_EYE`=!(`1_1s)FJC# zHSIWCzi-kLf8Rpa-}sO^WKO3S`2+G1aL@d&zN;>Qe|iV;{n_ijVHO}U%-(-iJ-fm( z_?_j2>PGH)C4)U0a4Cx z>^uI4_Y22;fsqC<=Hvsm!M^byVr1}TjK#5Wg!KsX_WiaQAr1#Ges6?9$JF4NINqNV z=J7kG=lR%fME>7+jeNd)Y<~u;zc}B=4j9Lgzm8&cW7)pl*I*lXKKS~^@8|Cte_y}P zF+;#LuMh9xbphYlB0nF$K7P!3fsc8>ANefT>w|WuF}`>G&W{=Mo=5Tnb$id(`pFtz z9t(5y!MNvZnbZFoUBx%(t1lDZe?h)=3BUgtGdMojU96+)25I4<`Knp)9I^gc`gEvy zF=IDpskwb_qvdfuFPgdcdg#mb9=&W%Ch*5jDBJ7~HqQ z=eMHnSB@_~PJa=aLgoY{tU^mn&tTG8=7Mp}-SC~tpZyDciR=wL`geX8|Ej!I?}Z%& zd5ggP!WWb??{vU_mU98}faIxgfcW}kA7cJs1A;v`tiE$!Qm=Whs@LpS)nf*F!)a%T z1CFZG=>5S4v~746e@{)IW-~T`T%ZCQU^~Exas#+R?1Srt-Nx)!Z|a4;umk1>yE`8k z3@_mML+mW|tby)>*-tQp`GbSd4PXya1};?1z#*DHELr>Kv#XR^;JfST6QXDE!qaF0 zr~!Vw!}G+<10;vqpX+}S_B~siT;Kq;fP>EeBflSTf0#JHc7QsddjpLBZ<+lO;XnIo zACT++f5m@BH8$t@QH*Tthxpr=@@e$<#=Wr|;orVHwgXX|9AbA~8+iAyBM3judtw{l zT>sYtR*idXgy&?2+qNZO*jSB^^*kY`;By1thhLAeY^=wx^LLK%?Y>~!iP%=eIFEe3 zy0OtZ>#y<6w zFY)=o2DrXQ9`NbGG9BL3pm|H?qTkt0e$U?|v3@RiH@BAw{<9uq<_5Yw*Yxt2IKIdK zqvdf9;A0(c(Z)|TVDB&Z`+wB{IQ`I@Ltl%j0!;|yWH|TZ2{ie(4cj%j2PyKG~AJq?TuQ#~wxBQPvUU-rE)luEsW`XXxd8A4b zH)!|zH+AvMd-@~uH$FP~g;vfyuC%PBny~1Qj=k|mo&V@Pow@L`&b?2M-cDu5y-DEy#DA&w*nfomVAcE%vgu*US#S={^bGUNPBXt8 z{Db{~e|m$Hr~QimRNH{YI3QsnK6~a_b(wKaU8kQ@_o?szlTYbU@c%F|K&KIV)qcoM z;(({o2VnzT7bpk!Wl!k7lJ(etC)A>JgPM~cG&5I7&9LL(o$M&vuU_N!qaoaZ4Z!~o z+d|Lr7WJmLs1LkAuOaLz8?=s{B~P%oj9upR80FQ^)3WK6Xn^Pm+|-kKg8lR%{NE>V z{-4qdWF6qA@c#de^P~UA{{M4ue+j(X_xps{|Eljt1B4F9y+GeT?|xvo0b+pfr~$YK z$hG&l_7-C_X!yXP{jG5y_&zWn#{hoJuk{H&w$UcUkNXlv5yaNfIp}4d&D_{@5ix#b9~qCZ8u{35MmbaYFvlY$QSs#`!#{ncg(tL*@FdSltIG4{iK?8Nuf@UEc8_52O{tPg2d=?ABuwB|!4toT&@n6sCH4@@1sTkZOe)r~jgs7JHOS~uaCUfuV)E}nW* zAHMj$_B`>r>I&!(BolT!kk43xJQF3 z&uRWl=5!9DCn*srU<4&o|xZ`?s++jUD`mj2UJjkvw z@DH9JLKob+?kVv9Br(87@__YfUb5&6gC2N)dceN%9}e8V`TlRI_kYbbaDUu(9$@b8YvO>fZ3Ex}JtycdFGZUF7~8Q8 zFwWw2|H5a*pW~lv z{KdHUcL+Y5$K#*v=X~4GdA7ds@9*GaKYtbbVA?f0f3HwOX#DID``fR-NNt|i1fGw& zU-TJ~-}lc9ew`TqE3~=x`JSzBeZFV!T%lg-9O^Ro|JuABw$*!BF2nV`v!e{%>nM#~ zxLPF(j}pJ157^IK%Dhc>Cw^wa$@8~}>WA|T} zyM>>phG#!t&8eX`ubSMx3SV!ZUjYuviSx_AeDU^g(d{|ECr{o=%uJl0jla)bPcJUG zPX=H89%pXeDtc$JNu{$k;-A^)otvg+ceT^#LCbXZnfLV8p?CED={I$L|67_l`hZGn zpV89oujt&TU+MJQZ|V3eFX{MeZ)*9I@NdQV{5JT-2cJ@Y!70s|c2Ohi*!SJ*sIq#V zRCU2cWoN&jR&6KhnrmFk@2KWIhbevjSrx-&BvH@nvl{G^hh+HR`!m<`Z`tur#QFcF zh7veC@S~V?Rt7b)ObZ_Bm-NUshHlZ2zz{i^6P9v}b=m&e!cj$iOFn5Gl zpcgSg@2l6Rm{a%!J(08Y)^=ulfcv2F1LvRYajs7vFxvl5_wjhRM)-HHkK=wH^M9`Y zyY^?^@0&&&5cB=E3FiNdeb0cm2EcWINS9+DA8B-h-;Oa2zG584mxCW zmu&kQ?G79nxX++Amc|ag40ybB>Ue2jz8~}a`mcOXY4ixVKc;-tOwAluq#ZM|G(NAj zrjDJXkKX)Q=MP`dtH(ap>SZTYQoK$xsZktx>q8xf_uKc@d7ZfMmUbR~RrSNr&^`Qw zno|?(Ppm&|z)PAw=56A9V(WH?RFwOHYAWATSGfNs*C*lgTk4kP=}H)~T@}0pw|@X` z@4>2VYFUe2r~&h}#P#6*fvOFZ>1eK~zbFrU(fR;2zAo z-`BoB@O@z1J%E1BSKEQh&hd?V{JgOrYJb5FfO#JC`IZ>f`#-*e2ly6SVE=U$llG|` z#{t0>H2P%wd1K8wd*|iGmbn6BDSpoPjpu-0KEpM-tMNaz!C#2^jc>o+uk+`H*YiH( zH?{$uf9dBUKOEVB_`Y%L_xL$q;qu*&_bvCsuGQn7qZH|RzarMRmiOJg)p~l}a7|gXOl9+SwSezpIsT7zN9{WNm);PPVS7qfBX|LM!)>>_tbaU1#oln{QI52ezp)<0QTC=QJ$sd1bfVrQAL*Ys{E$R9taFx87b0?mOuZ zH+50_oM|d}oW6vu-@xnf-IxQGxgOlFX6MK1ZsRgn zH|iRHBj*2?fdAs1>;>IUJ(D^{KH8@u@+jlqa}aWjfBucR3(-%I2c%3t2KLE6euMus z>VcVXfe)rkR_p9}y02il?#WxJyO~RL7dXDFXtnMI=S|Dj;mh&yCHCzc@ZJo38}Ikz ztW0IJcGnDi9C;_FTKe9 zM{U*A`E&Hvvjyb)iBa57eJ{qjv2N@;_76EfxVPr-s^$l-?-Gw)#x^+icW&<*fam@A zzU@Mo{qwx%0c6wrNBrMd|Br2eukmeT?6=1_bUiKbZeTF>&oLhTydU%XW9KDu8{Zo~o7mmY8~1zvB zn!Rj+$`?Kh-ibSj>(fIGpEw`9yDvWjoZH{Ke&?RtLO8vG$LPafioaj+o=WJMts{^3 zyuCWl+1%&(n$GbfK0ok!(yzPrl| zO)&e^Lc-5oyc{NTSw|My{VsexJOKJRhB5@s591-Le0x zx)c9U?cx7`+ow;FyuWJS-_S1q4UQx7`z1Ts75MBgaGIV!@)ffSFVGiBy@Xy!&o|Bu zbrtZB9mrg8HsC*P>Jg=(19IIzm6JjqP)Lu^eI4`kyIXsxX^$alo-$jvr!UgYoLkcu z=#I=qoX2!$_A)TOTzBRy3%BpcS{5D;&-r=I9T`h?JJ;LNxn?ZU?eL?wWiICSJl&o) zTg`Ijt8F>^Mnsz~e&0Pw`T{+_IlrjS z7rcXS#{iD=Z5P6o*I4)C_&(PCng8?o#++ZkIdy34fzQ{+vWNk4b%n=&XsrLoK0oI2 zLmYswj&W#TYrh}!@=**B*@uAnp!;dG7uW#5KiCnlY`p)O`Q65?>xQ-yzU|KrHYBzY zfeVN@#~8OU&g0j{kB2K9g6Hs=zw?RlZ;Z$9vkeHozcCMRT<_<8dkq>MbNG($jeFkj zdfzwX^4^>M{aM%V>Ctl>0QSEE`xg(C>d2O%TK?E<)y>~Z?1}D_zq?FgeAl8gjdg1I zc^voPX5#C!IIi7i665E<>t(H=Cl`%P_3oe1@4)RH{ug@jy%#sae2sH_bUPK~_vInZ z2lK`~`EKC$i1&-o?B(M}3-I^(XyI}192=EGtZ&>WgY^_*{S;#Uh7c|x+`h6?&v>9wb*^qr z1cHlUZnlQ$AfF<_Fog<8~>r#w-NhS$Wy<2J}2P+M{fTJPkj}uA+G;*=UqHYBlgqQ z&Di%}wE@K0k83?L&Cf4A6>IQ#54$1{E!U+atbrPhKW}Z?5Bc=3e?@YWA+>F?T1_ z^33sZJMFP|$h(=rvGyYkfZH2Pjz17izxDuJKDe*J-`7yPuO`l~-u0)z?Uh-pOMi}Q z_~!GieK+n4pGFJ2`Rfqt=aS#&tocM4t3FoB3j6!_m9qRTejdEzEOc&z7O0?lkcJPg z*0^Eisx3~^lreRhIDDYykE+x3LFt-5GDE|eb3LH2K`+1jsxEx^j$V56j82_DuZ2tY zDKnKB`R$%j_g2T$z2!kwr5@3=>XRCleL~X*zN-b3KT%G`Zgf57^*f{C>rJdbUxn*G z4n0Ll^b4mmBP5GCpvlYz>br)yA?vnd_N+1P-T>BpG~==eX< z{UwK9phL>0FEDT54CR*8D>*G+_poE=ch@x6_4oA9y`8IdckhY1z3&v=(tA=kH}zq5 zVc)5`rQdYj(tn0-=6X{&)3`mA!)yEVnm*%obDwd#Jz;{HrcKrZ+0)gwXr3Ns-&p_g z+c?Z7_k7~<&qaq?VbZwW`2Q7pV;4H{{mEcG4g8zWi?DwQ8{pXA8Xxog@%5_i2X5b3 zw@vV4!3JOlzA?{-7UgT^{f4~%1hqfg0Ql-Fjq$&+>bk%26Zk!F7y0$5=4Zbhbb;Y{ zzYbf#?Kn<|>v?ewU~GpR03RRQ3**+G=~yGS5ytv&vH#!e0x|Y~)$7IFetfmhciqk> z&iUiG-&g1K-h*w8o^ymq+fQC^jDzX8XZKs`^w#kh_r`nFtLxV=Z|^j7HjkI-{LVUU zSUpLj=dV%O{DaD048Oee99$l_htJEgzh4UXOni@aC-8c~*W>TW?W^(iRh-IQ@cEolw7O-_8u#Cm>wqSB80`=or9#i1ocggm^#f6W9v&H%I!VEMlHK za;PHo5(O*RTWTACz4iQ4+kh-`faDn`Lj5moit$hHPdJB^1OJ&*Jz445MJg(;P;ODR zdMD)Rw!7Prvos|h=%L#>)##4yBXmpm(Ym4QNHyWy$n6_@a6cUS7rT$)_81=H`EJ9w z4#RGY(mhFI)iiyg?$1R-RSf=_6Ou3i?0YWpcziy+NbW&O9M7C0=9f>Jzfx~KS4$s% zGW~sNx&;2c^TXJ8U!OVtP{RY$#&|eK(fJV5e+8#^**?F~|NH8G-{Akjf7ts+?0V$| z;{H>v{pT?M7yZ9EJ#b`=kMV53Z(kny-zZKup5wSZ#Qu$OK(urF*Vwk%M?+fFf`FmkrGI7DXzZCZd41^1#m_sp4|6!Uox{AnGPJh!aQK7J z=v%*6MQra{p5uJ``zo;SQ-Nl$eAlnKJ^Orf+y(gi0&rh|R@d`3GSr zG;qip>d<+quDOmJ`gc9)11?hEK~IO-g4xUfPlbapmv7B}D!BJGd(#i}B>tNQQ_ri1 z->Zh-tK@zqH9hlw71Z_0*e6nomZ5Y9_}|9PKxPYPvll#@Iior356WgIsCA2}@SB-y zKVxUXh0qsKfDI^I{wiD`u@}D&|G#AOf0YFO6Y&4Z9QOgc?q@rYJ#~+Y2G3GyWu5Z# zi`A=diuxsIDkHyCJrlEa+g)f9=|8&Rj&8cOO`&dnq(L`4+Mp(#>va9YHM;iUT5b>2 zjh*Xxyk1QnsnZQz2I{t6Lv>fbk>GzE{(q7l0RQbujQ^GN9`2;?2<&qb`5uX$OTbAc z7MQST1^BPhrG2SjKP`&;ndM>phx{JQ2c3^Ge}rCtbUd~J_W7~zkNtk+^YQ(S{@=Sk zemH|R1^)j>W=whZ*H8St8}~t{kB|49o`7}d`(V&I{WuPYZ9wRsb*{ks0v{M+_$U`J zuKjwy);NrB`+kV)!MOQ8`}{bL_s%=#4?chPt{i-|Z(n^LO#60x-=FDY-|zfCj^&Mc zAIJTH%g27$2K;1>z^|i*=Nf+C`q-1{8D8I`?>DC(;Xc}ron`zt;=Xk2Wi{;kGdcdhsE&Tz zs=X2JBQ76b59UMt9-r?TeyrE^4vj*``S`kwE&R>n@AA+9W~~1d&Yb!^`F#?+UjN1K zDiPdg&IR|>loLy*sibn8(zON0>TA@{Ku~sG7yR~5aS*;j%MsusqX-36+%ILRO zx7;=y+-KQA1ZvYI_V?DtA$uczk#m!R8iAjc2bKk|3- zp2m28FWkOk{axhv)bvW2EnWh*?_Hr`&M5ky>@8@P!M$~hY3r#Mu0f+rT_cNnX3i>j zRd$!W%dFtSA#+qzRF;l1%2x_eo_|@kH?d&b}Ib zc_2fV(e^pU_l_^;{GQ=&tlR%v%j-RUmxCRMVtwP^&jo)Ew!`i({J;1AT-^n5%CrC2 z|4Hue`G4L45OaUN2JXHQzs9n8zgQO#VtpR7rpGoTuYAswkSp(-jsk{Zp@$G2! zz`5)8S!ncgAESQ8j9t&uckMoxIN$yF=K8YW_cDn`3yIC^o@3q~8hy{;iE8&zKTbKA zH>X#QuMhsdQM(gn?|}P!^!Wu~KW{6w{4JOGEHrS9K0gKA_gQvPy%)Ws9`vkqpM8e? zw(FFhGfXwvRVqr#R((;Gvb#P^O)fzV)ukFt?yr?CVwaZ z6MfaR?SRl{S3)mg0`mfTuJ}@k;6CMX_V=!Gtj~Af{DTI7^&$58#(iT=&v8K29(?|8 zbbQqF%E<3a;q{BbeUax2+xEflXKbaOfsP?%J^EyDpRkI4UY`0UCaZ1xj>G}|)h9hmt=c`J>#n;~zrW^gHEGdPH?+yp4Xtu?E!S(?W$XHm z`D*fLDfq7f|Mj}P|4?|uF>0FHh=2UQ@t-`IoFB{^|B2w=x0Coz$;1N_m#iY*tkGBC z{cCVY^Jn^Jhq*kyHXdtp1OMOX^J6;^#RIP2drxM>xd_FyN%=%BB`=XMDZC9)IDaz{8RbxvFRGX8dfhCp7d8CyF zWcJedA=MgRl?FGTq#+qyHM%fCW6Jt#Y+b&_4Jy|7;nkWqXPlPI9HCWn2dU2^{nWS3 zL`^Ce?%?Ba`{elpsOeRc=T}qDt0dO9&oA5gSK=Q0KD>TWsOd3} zWDDGZcLZ-h7r?(iW&Qhn_J``V{6lqjzC$gu*St638D3QJ$~RQR9;0G7&k}61^MC^E zaXvYjcb_HCIK>PQYk-ar_ec15|9>{K!3u^g(}1CqRa#Z2^124)m6XuikfWs3Or>RH zt9_@=YTy20^-4%l=iZ6B^WK(lgm-OhhH;xTrocn4_`Z?n>whNw-6WIgX4Rk}`J>&$u-u-fMod4Sx-`YLr z`sV1|4-nf2`*DAl*beyT#lAf}j{kQIVEwNj4{?9c?SX0M_V9Vm@vm^c2IpU)-Ti_6 zS>IzfK4UnJ&z5DLpEy4Yy>8*UkJ0OXuR-MYgQ(@zcMu`y6xHn=XUo0msDRW*>>V)0uR<=Zm?5Zsp z&fdyLQk2uHn+D|-sUo*XL+C9p=+a69bNXmPL%D|G>qqAF)@V4uX#?^!v?NtS={p@V zpjgw!HfX`@aT-yds%fK(HN0}R<_~yXlXB1{^f{x76#o6m=k(xx3-pKIrRew9_0|Ku z#{_*%0XoAl2XGbI!qs2E^WpR1`BSLr=RNzg>W=)UhCcrfY8?NYSpRQf=5X~sdY7o@ z8T)0}hEi(z#=iH12VNiEARFvwZ(?U4Gf2|b&<7B-$`{ld-`*XY+nxWDe&~Js5KFgD zAE=S5&#G+YJ1Sg-4saD540=HKP~eikOx~=bjkN+Pki}BBXLU_kSu%E;gb@x?rV{l1U)d)xOrPVm*Q_5R;0udzS>CBzL;{Qp~wyB{#-`+pz@aI7EW-*r5Hj{EgO%^sWJec9iU z=er00`?LHm0;gZ1w{{h2`{M)A>&|7SKKh*{XXw3ug*YG0j&pmoJZX!l+xzbb-=B{i z$S1aRpH9{i_vyW>9DGtG{M_J!==AaTbSFcm+(RwyzE6)llBkN| z@O1ormi6kPG1Ws=SXe`Szgh*|T4_*fFO4kD)Tq3^8eWv5nv5PAS&^Y>!)r8u>L@MX zOd3f|5FJ2GURTYYP@zR*Hfv(aDOI$mpC<7o_3W`$H{Z(Mo!_}AAYZl+I{_r|A)HX|7LHn_lN!spAY8uM7u@2U%>l<&FvRs zCkpWS`OF;5-5TZiSs32)yatV# zph07&sSa;lSj+=PE`9b9SiT90r z@bCQpXXnIl-ZB1d1LEGi-{Rf#yQ28LGSmiQ8xV2>Y=y6W%vWPJ#=kM_nBMV#&u?)b z$Nhdh)bQ-b8(-sjJ7L~NqyGua=N(G1nid0P1kPc~xyJaPSbrIS0REW*!I!w)@q z&Lz?98UF>Yq<}V8uNP}sV~~S-t^|B&3-{MSDn&`tOAP@<*N2W!PF>KTJeG`2cR zHJQCNvNA(!7L3%BtLJKT#WTw6a89Y6PpWzI!MgUEhjioZy_sjbLS;{WpiE-@MDU(r z%&-3pjKW8P{bcxwBJ+Dk{=x3h{||rfdj8+|op{GUl;a0}&$x%B@0Or^UkihYtz?}NU8a8^i#!s1{s=*^PaKv~G z7(7Z@1!YP|OIKEDsnYZFl$4gDM<4B}X3g8^p8FnV7V#sxzIjhw2mY^boucc({|(^( zM&rMGz3%EgOihXZn>XVBA@JV?{HMV=q%`8+Ho&t9B3@!Y^PX4ey~DNC{L`4*3(mo= zbAYcL({tOnH-Bdi-`6PSk8HsA^q?8%whLFB_oD3y`M-I89ybs0qhAy7?;Zeh0CHc) z@-fF3$KoQ=4Lz`j1>@FO2@4SwMDh~s}~JYi3sZ5{T^aliLwd0(#gX1TuSnVVlS zXZQ1Cjq~=F&QQ(VEyS=V(dWKQ&5StT{@%5F>v_O?R?w5vpTpme^ZZQW{fs5=66+He z6PwrJ-{aZ3WyJYq=yQEa$@5F``<}fKPGf%W{+wd^dR)iL+ZxT=%4FVFCfr{7dSZQJ z2w$3rhNmwYp5Bjrpf2;@(ZeH7Xy~+EdT#qE9oqexcJF#wk1boT5yM8Qs%o4@k6EkS zo{1{$^00>GRcSy$wI)^K&wIAi@SJ`cQj(zw)w!BFY=GvDtksmdY>g;MR!LHOjVw*n z{IPXfw_u7k%^9i{)0sy$;52i7w&|uOX}ad7j(R9#Jbo2!?P>P;iw$57a0WSk z61hpbWBskaFo)zn@%jHy{n7u#*GC#2>-DYOvu-!U`aAxK-b7;kZRYpDKD>SoagY6d z4t4y@wfKDOUh;|$l#IVmSon&PnBUWD(h9Y!uGLMQ9?}gh?o`ge0)Ir% zNe4K9(_LC{5Qt9eLw!oKK=*(hJV0^8~-7eCy)1i=k?#=(~a{V@#~TA_ibz4O2Xqm z@p$0qluk9Ba{{qxgI4bzh}E?D;!fiTT6sJn(MK zUN$zs^Y+7>P5ixey9Ma%s+p5*-QED={Yw0OIsV@J@hUiu_se!gxHp$yyqy`F=ypoT z?~94`3(>&lQV-1D#5@aZi0fD>;HYo#_w2D)#_l})eYYhS^w1>cQj9#L;mpF>{nU%v zzw@N_J^PZT&3sl_<&%}vKVR92b(%J1v$FbSDZ6`5O{yNFA*J=2Tve+f{W@qyd6uRP zqs~93Uc-t~)sWp!gHpO`2zFuIz(TE@J66kQj@0zwC7M*#Ta&9(RhT(mH(v9wT0WY? z{H%S{;_%D#CpZVs#MfsLpZKIQGsHVTUAu2^tWRCfwfw;8TeJTh*x&sx@E%c3&l(=* z_(e}~w!rOgVP7coeLQc_aYRPYFn!4H;5{Y5t!6Giud*da)pO7|U7L2dZXMi$-kLi! zV)_A7S59clTg>>xuBA`dqH5}*`S7p#%P(@?3%7IOW;{CX>{st81pgS;~YPZ^L_pKH2X8j-93}fdCQOZ z|DRvru~XFd`8Rz{P4gRk{V#vyn7@}Se1>=HWRgFo!#U-z`#9{-83g7BlH*sC-E*qiN2VViKZuO{bl~m^yP#3fye*B+);G;2f(~_JdX9@ z_M(~|-2Suw#QY(4gg*`DnJu1=mcjZ(^P9=U`bpT7q~+}Jpr)BXzLkx>s%-8N{{2sB z?GjG>TwbyCrhH-lO)fcs7_a@z!*-Gszx@gqX*l@U*e13-o+{NZ;>3Jgm?>`BgO(NIkBw+*kQhy6`!Lrffw_tD29W&MW?iqUIfhkJrIZ^{_ znaww9oa%@N%Ik)P`aofEnffQEsYRPd&<1wa^)0&Vx(E71_-~g{u$FT#(|4O}o*!y9!!cLeUh}|Q+ z+ZIH9`@U}+JI{}feLp#Uhy#j(zyApvVEp_1Wb6|cI}Y&qiQ4y%^t61B-?RS5yk}Ft&%p0z!>1ePIp*|2&DgQNxjpym!5yvn0R7pwYB=y$`cvr5 z*~ctxaPK-^i4XmF)~J5@sYB%hFI#zGf=y`{AVDCAdJ#tcO2G?39B@E$kST4=nL&z z^%p%e_p~aC25ZvHt;$L%R#uNkHMX)rHHCxOO+Q%W{W@v@dH=A|bj=-JhwtyB$ql*q z{P9{iWh8xnMH*FIr3r(FYtFcFS~#*nh5fs#c;r-7Kk*K^_nS(CPqV%z3*VpJi2XwR zb{&4+efc#9{}bF(&*KE_6YGa~f46ykbNcvv^gwyU2_eU~4Wzb_Mc-4(dU(w>%pw5y zY3L<9|FD7?NkbOxQPspJR5g8zhRk?YrGuxbNn#V-I`J0W*l@FYP0Uc;qDi`@)ve6d z@1Z59)@#sK^cnd4Jm!TKuBPs18-U%;BVP;npLH7jANZ&L*Z5B`{?P&_!n^m&dR7^o z)+w|7W9rgkvfAALg4d^K0Rdb7q)s7nJwfA(=wITl} z{%@5?4KRZoARqrg)8{SW+mH<0~*3iwaP_ovT35)(|v|9^O- zhP>TWdzx#9ISl{;}%tySP_w1PGi;gvTWsRQ|zwUo>{K5Cb z3q*Meb2yl<;k@Pwwk+iL#5UifWwy@gG92F}?BOMDe~5nP;N}L+0rxfYw}l)($8~&a zMy}^&(H~w2$CpRUpJjgo-lHC!M(q1Ob2dw}G9w`m|m+%#595=X84i89lx1Ao|?RdTi1$?OA$R$5#EMV++65wlN3r-%~YX z-Ztf?6so9qM@_A*Q3L%uW2zcd+UG%yE$*k8^#vMUUZ6Q+hiKKzahf!wp1GYB8dR8~ z>7zzy{IEfqK60oA3>m4~6-QOJ0j^^Oa|fBnm9duny7Vk$!b{}C&sD^>!C4O_cM4wXVYk;s0s-pejv3ookY zh^_4WDc9)byEI|tP7PhaJb~F8R5P92X23+69Nk_`vmVj!?!H|`eD{qn z9@OH4&uPeJ^!{*#g{$ZTfCtDUhbZ9WxJJwWaf1E-6l(w00R9I5$=HNmY4qSTt1zkk z1|@V@rS1>RRNFg;s@>hA)aITE^gWDJM#^YaQy(m;f)6OpSC_uj{O|3gYnt~6{{MRL z-=tkS8i0IifTg;{{@?gd8X5e5Yy5xvqPbx=SjPXr{}JjF#y>WGF!gB{JUd)d_S?h?+4usI5+lf8-Btb`~-#^8~mA=+W0kY zjsF-F3VX#Cqg#Qk@E zU(n^^+vC1`Z2K08#Z^V{pR{_!aqx^Odi zKLLmDy?JQ$nYriuKI+pW*QZC!{GK(tt`moO`do9+kr#(v{9nL5`91iLxP1HkM(o?? zQ?n{z?nV)PXvTdJyRr)5^9!xrA-2s0E5^R-*XiWS$>hrY(W>@k21j4?`u)+b^jYw( zI*mIAFSJwXwVSkh^+BCEaZcw>Jg;;6PwK#$qgqLwe>F2IkFI}P&u{udC(zIzSn`%O zFkk+uk&kQ6_<0&XZ8P{UpfBe^O(>^+Ur?`!74>RJ>Y%X&=zr=9wS3`hEuKD3V{5Sm zH5HmVe6S`DAD}TK&2tS`{oE&2yO!8!?R!dp9Ij)HXN=>QnIDirzTz1pb%*{V((oLH z-vjgJ_$v3n?+5)JaXy;;68Gw04~wyfh5TMZ&a;`?2C=?*{nRzg^dy!@z~}d)e%f>X zo9aKC+-u@?W!Fv6PSUTxN?UmNCgw_Tyu_m0e?qo zH#P4F{vXw~_xINItrFCvZMtsgU~O?uyc z|9e-d_md@sIN(sg|Hnsa`5SkCo^`s^=)w5+@P1bs^LFDs!n*P98b3OI@E_TLNY@_x zJoxuD=HmkX!MuHb-~;fVzD68=}o7g|*{`{EjK^z}AuJ>cn-rdN*J7zb|ef{n2 z;KRe?)b4DLus^l|emukoXR5Gc<}8eLbh^a&#=Pr$-kb3)b|Mmsz9X)#k z?Azy?3xOy7{BVI@dA3SVt{Tg{y)`OXaDaaNv&8w0J2Ub9dGv(mE+x;m|0lms3-LX9 zJ~ch}=wzbbDT33kgS)e4&wcpbnNey!pS->dU+)w7`d|Hhz&-vg)bAT{pG_SvE5<#! zav$R6zQm>d;H7%;ci(I2hw3`zWwozl{uA?M=FNXj=MJ3Kd(WTN>xW*@!3~GCVa7o% zA9IkoyRYiSLmyxdKGNwe?`r$}1A21$Dm^`E8L_|)O&c{&qepL4X8%GZKio#6ONupu z{C^7gACuKXgEG5o+QiYCK549`*Oh3&&~i;4S&yw*reWhIt9I4~)vh{6yv*-lCG*D~ zzkrtZB7UE^c|HD03C#9G;_5uTI48QPU&GFQ+Fzs^wd=&yInBdo+yvPGWzb z4(`**@y+We1>5%?xPL?4X1=6HCZ1Nukw=v3^ec9d?~=ufAP5;|6Q*3um-W}{1p`tCsb3HDB*Wp6mx&r19Jk7 z?~-RS0}$+|Qfo_@;`%@PfTns^*kRrO2%7$z9#G4sL-ojmYt^ImM$UTmZn;K}w3w#{ zTMX8H&FZ)|sAKyQC8gx4OYeU0|E+ahvo5;+fnK`)!T!3wZJL_2&(%$xOLTMBD&5{| zApHMuHA@+<);Yi8KYe;5{=t0MQOGsfx3L8?SFO<}$Lol3>C@xy+gz44Yd>HEjDOn! z<2<$jA?~NAC)fzEVSgWUfWg<>@0(wOQ^89M3TM0UhJz!>);dd-|6zAI{TzJ1ewfE&1!>g{qyugLzLcpvfb*UxI#* z-dxYxjArpf{GMm;*&l=*x!8eB*NE4CtSWl!8utGc?G8J!8~OYa$N9n6gL`6GpP)@8 zrY*qV`)}O-&Hg@**w*{#GU4;n*An9sFDFpD??;^9hd94Cv4!Vw^qh-!e$XMc%Uh`- zBiHG~GspDdu~XE1kAd%ldSd!+EoA@9CbWmA_I;#R&wi-W2j14PCtuUHIs3GWKBQwy z*6Ep<&*%{R+~h%XnB(!JGJ5B!fBQBXoSUc7MRjO>D>XdzQ7sr1u6q;R*6;`2J?u7i>m_T&<|nlM z$WB#H9H~Ft)>0j^=d0n#PtgE*4#-7%fZ%ND19IKgC*?Qz_Y9C!*8wLpr)=gybxrE7 zE^TkZ-`}r0Z%V)pj8NwXA5+f<)~I{4$KeE)sNMZzbYIgdwYWD|Et+*z%XS@gGqVD& zYtdQP-QOeNze(Fv@Slwas8~05sn8w9fB)fXo;+4o z76A9Y#(v+|ANVfbqhp^R{QW7<+w;E<{+^iL=NoGG-c4w|^VgvU%475~Sbmp1)h(-9nXWv4!9?;Cl@ro7jk~`MbFtB zu%DS5VBh(@`TSxqU*f;%;P36zjQcC(vS`xXlS7r5K4xv6(TSt% z&UyVqz4828I`-6gJ-h5hJv;BT4y>mBzh<8{&0ME#E05^;z3;=}P1eHk+mzWgRejpD zR6}N_#uQX&d_lIR6!g^O`gF9d1KBaQSY_kqYW(8|Rl^+5vd7M%Ye3rw9<$Ap5jW?q zB4!4w#psV~$>$rGwOt4Ps}I8M6YE#=ST#L*RlC_O82WP~&2BNAXB6u*-)EC`Oi|8b z8<<4C)1O$pFFbso`LC(V%rj~~<(S%`b?-2Gryd>kjJnhyvFW(_)$_Zt{x%Eu#r=0HBsIcG7>d^i+-E>Dw z-Fj21THiHN4>wUTTElYzsJ1t{>Tk(4WVg zZP)J1L;P^k^@yl%&$D*iqw_twJkQki)q3Qw@$X*|-&^nCT?Ou6{+c}Bxc}q9Vx4?y zfEF%Ct2Lh-e?B(cGxs<-p8E)|=l)#xe1wBm&ptngc;A{m@72q)Mr6yE^xgjnT`KV_ z^LEVVnahuldA&mXUm@|V>sFpMlk3_&*vN&~%iHK2pS@Y+wdw2K=NiSg$?*Ao9P7h7 z_gefeIsV(~Jo1Q|WiL_p+^NjUctS61IiPoU9@i^R9@Ng6+q8VhHf?1_?b*X0>f&3U z>h)9Sb#luo?VNX3JBefVZFp6uwq4M^C-!LT%Jtg3cB^)6J+7^5c58g~IA(WlRC0%& zN@&|wgXvkBmh581uJ~g-w6ZW8KdP?n?h10oWo-&8eQF>K_#?M=-am&_f((0!* zeBl-iT?7Y69*~5d;o2^@>iWv-bPxQ0^M33Fyys5c+O(-|y}z~EJvu>^BVJbxKQAA@ zUkLsS&HaJ@$p2Hvn01EU|6`%n9_9eS1tuE*aPNJxlT?`aJ5^=;L3#aeP?tw<)!i+c z>89%+(oNT9sQGOp)uF`)Mvo0FD2L&;WL% z21pOUZ9VIBci$muf&XuvHANkYX0!Xt`TqfSlH&92`@N$a>}Rm2JZBd8U;BhUeZB#F zlG79WM>#+4DcV>pnw7U8{dzJCSY zA?(k?-y8p~JzB3D@_qV|%X# z69>_^bok%H%-xzJe^({7{Ay$0ntkW_Xn4xOefe*6yM>{aPaMSWBa7ch+B$0akAJKL zaG3BIIX<=3?(Eg;KJT0!nf8J@vkT*qQM=TtVzJuhPbB7_qP|t*l|%1s&4_WDy<(ll zJhoaR7pzsowAIRPn5&9$E0kR`oY@+;>gLL8bpL>6x}^>HZ`xe9wQZ?R3H9oqfnTqA zNwur-`)GiR@co6<{yk&Sy;+X;Qow%__;>yvW&mX#-@~6_Zos-;`~RR#1plEf zNe#eUzGswueS$eBj`!V@Pp*IIXr4YlTA(+#m*{DFbjHkQzQnxU%J+UZG$PiC9=Lbj`di#vi=J=Hb8@y) zzuV&3n$)eBSL>Oz*1CFEUJ^Ne61-m0N^)CppS%=4o4@%aPWK7N)p@`Mwe3s2xO|2V zuG_8Gb{x|iTMy~^#m{QPpfy^_e)yA{&$3hNeZBeO>pHgmxSm@0oSt5EKzlZw(3!(8 z>&$LAt~Jl<*|ksW_;dTU>#3);=c#QvbM%zfF502`%n2%_?l*McXmxJgO8p;tP(xBv zHKM#sWyG}E-Mgw=Vu6Y#Z^tLnzX0!^2S1j-0&P4TUna469<%s7Yk2UK%G;2tJ%X3sb#M|O3GZVJMLPhN3);NzzrV~ z$GlD-u(dzfH`@T>fA3FC1^SWxU`5jcm5p5 z^go~DF)$zH0l&um#&&$Hn>W7wo(S{Cy=_BTz`vh2e(n1m1H^W~K0NSxjn|;{qsABe zdtZHG8xYq5BThf64a6K^%>9LWAF=*V9LFNoEynr&SLF5I!)sh&CUNL(LeJybr627s zqV83zIrHbJavnV?>~_pp;8-7S-?(2$tiR}GxISuo;NN~fiu=*-6Yr& zF2Da0u8YvC&pf01i{_|v=@{MKv9t0jhv>Oyj=+4ReAo3~3B55A($ z&%LSF51)p=+@;MtzwfE%b^hcldgJU%I`|ZIzj-UQ{mHF5d*Y~`ed@7b+6 zqnB#xfbE)Cwnup>b;{1H&;u>*L&M&Rygx^^`K8pS3fU{UT$L+cQw18EoS@_=?JBsCB^U*Pr+=RnV7T0q!fw@gx0S)TifKe!zVr_i28w!2L=g&u7-~I{E;J zA5!4;`{8SQ<7<0kcYDmY&p)mcJyea73QgK z2sU8&P>q>BOGBnD(9AW@Y0#V}l|FEmk}9Stp>&vT=ysQG$i9KyJIz%-VW2Y0hpH7k z+r4o1zx!R1x@9d>!+JDI)N{NGEFT^)Z|S@60dO09r&R2a@o%lIIe--We=_(_o_Z2} z@ly4tA37!XE|nEsr;37Wl#_am3i|&+g}tv)a@QO6P^&w1=bcTNb95IuL0jE@OK|L5wx|D(RobZ^InDta$MAP^u>K|;NbdhcDRff@oNijV||BGG%t7#oa@F<@hZ zam6LJlQ>x?agt54yV*V2P2!SdbI$Jb>^aXLa6Rw)GaBr(KYU&@qtSdv40GM}y01GJ z4p6v8f&WnfrUL*6koezmfWZHh=myP7Xjedbw}SGwE1ddY27cafZzqG;-*SO8;(!cd zfNe)l>*x1dz=-kO$@{^I;mY_k=0^?3uD*|LusHiszWoT}#QlbOUSs&Q`#zl>!@b1{ zmM7SKo2fDU`}F_p_sj-Z{BOsu?Q}os3mD$b4%qd4xOdm)^}fe;YuEF=zi)oWc5qny z|9h}+^*@UXtf&7k#QeX5uWK4SHn;pca{S*B`~Tvle7$+DLWlNlRKt$b= z?^O0qa(sIF9`fQ;!##Pv?-+OAaBsE!4BI8gUihl(=xfo(D>2-op_|K|oE(l}-fDXJ z)bVZR)K|;TBF?wImEqpza8qp#7abqd+RK2eo4_n?jK#P3`#3Padgtf-E$HucKCkJk z%e5dqUWdPURm<0`mZvsfXKuWz=DI%RCN*ha_bGk!;#>OSwTJrYop*KX^s~A!bVgUP z5BG1stq8G`^{fHX#E^4s$Q|+w! zR4rwPl;x>a05fT!%a+4m&s0oSyRykK3+Zd*FiT{0y)5(n=yT*9{d*N(psxQidxc*6 ze^m7f+Iz(MUZ3yP75j1vcWQZIuQFP8D6OPH&7B?U?t{0)%uzY|qa9t%>e=4Q zu7SPuzs{(0@6*a@L#G@LKtbz4MHjc}sffuMlmC55>3frS--~@ukJSL-+!6mL_Pnk|#rqUexK{Dy z1xhRkR9eAUG9vp)-PVj(wpZCbz)zu*7YAz{s8+~*bV2|@s=_M-+;?a+z<9Mc3O=OP7gL9 z+wppc@h#4`8lT~w`I@Th|K>RQ#aHq5%;DI{LpMK%SscsjEw(p(8OPN*$9EczifL}x z46gO>t)8DoKI=L91+~4Oy4^XhkGJP1irPtCe}I|v-JdCW=l9|34x#-Nqopa)I)3+} zPX5b%RlKoPQ{n@)Dzi{irY_g9DwJ7E9y`}+9Oi@Lo1kk0Ksu2)~UsfQ2X`Q5#% zOJ~n9e|uWDp1-LN-g}_e?q1W4YghE#m1o(_cS2igpFwN;W%X1(RDbQq>Z|`$&1J)2 zK1VC&hiY|VrBYi?Gy9JHqZgh+Jf2A}BHOSJk0*~^l@pvpB@^Txfd-C~YIvx*mzr+;Dhsb&GwGs4FS22&Us{bAK=)SJS zo!2$H?i9OohBUX3x_N%LlFBz~Exx^?VU4niGgZ~Vd{Re~iYxN9d3(3E?%boMO6v`~r=g5lj!z>;FeDNI(a~i+|$) zO)&i9|7XUxX+hd1Enx;Q+~I%3|AYVZ5#OKQ2N!{}#^F!L&luiKqr+pj zR`)&g1-mqUPkv9#I)Z!eUM$P$t#1eB9q#GXgLlKcTb)9U&0 z^pm5AC8KwvK`}sGzYpI(_z8O)p4RNu=swO{OiWv%XYbzBtq&6A0IeJ{1T86Zt`jUE!AE>Y3BMo7m3jFA>QM#Y+BvLVNEi98S%Wrt_L#In2Ji zomyD3Su095DZ6>QLenc0nNg^O+&opaRI^W@UqkzMsiD4F%`Hu;@7jX)(g6)$ysn-D zS5@DCRwdoUHZ6w~Q`D)+k+U?B`uvKjAWaK|cROJbzIUO)&FA~-0hWi`@7P?gVcCw0 z@v#Ypd&}!B?(oG1c5Ba@EwI)CzlGg|%6W_XRSZ(P2A zd0_E>j(+-LA${#N+A^?Rh1)MEbHHN#d-#3J_1~nQPfvdkO&s5_wc<- z!3Jc&?Z`g)GqiPn$4*@8dDrMkU7r+MUK!0WzKEL-ZEy?Q9 z-krL^4EmjqzOPrl|FL$Qy`UMiD_v8M2ru%0Va0gZ;8OJ^QyBuc75N+p$4ikGY-d7ypC! z2(8^0z&tyIt-j}r^{wZbM=d1Jc+6nmxDA=i<$H+rli5p}M9(0R`dSn=HfVf|>Y6HTSl^~KjWsGM z&eN_vgBm<~PCa|+{c%dx4=cZOzp~cuR8qw*#pJAGPw+fVPa37=Ss|J-FI40GLllr0 zPTxOM@p;wC?7r;!m^ttaa`FFJ`+n$72KK=E|Azl07ysYF|KF4c4qyTt;0$UM^FkUl zYG$TpVMqMqinTbgM8RpLT9I48-yg4->}5*G7>5ShW6D|mxRO>psnCVgBc}%Hv02z6 z`hTP58U7RL0j6o}5+D96&;T*~uakf31})Crrcm_%9scqAF77A(Pc!Tf+;z8x|1a)$ z5XYB*S#NL7VsXQ|`T0Lsj}II0$M?K^S{L(=*Z`LYu%qV>92XaWKf|Zh-T(Bq&DnVU zwAlyi5!if?{ru~PbtBqeUOV8c2U=ag)dBfCEN-wE!nArPw@anlX)%2cH?f^ECUVaKEeb*bL*4o0BVBmyjtYox$4;Bh9N2hv!%o+@z)|FiPbfY-QEy#%T0gye zTMu7;Nv94D>)hdey8Gfey?ggXy?^hD9^Sd48|Tl#=NZ!0x}(}%{frJ4FawbKmaeV) zg-*8pi21!2lo;Qs8A}QjRk2@%*uWfOl=S^0u|EHX%%iqX^Y6)d{#7-%|E4gyj^nq;w#48{ezp;v6;oZ3&wFE7b4bo8eG)(5)OlXX6oXpuf+ohf%x4=WeR;YU%U}_E@CPtyvA#WT`M+V-9j@ni0UuZ#VAt~E-`>;h^Wg9By|>j0 zeKiQX_2J(eYuLX5jX|3W;=L@c`ImR#HoZq*|6ZL2kI>)ke+hpN7lU{|jXXaMpOQ(P zFJl*eo*I9;`FnOang2IkzAXGt(Yar-Cx^W_&oW~V?(?1o@7RK@^Z51=AMgA;ajW6o z=1onrCjA8Z6=2>&Z7j{|SLCs#GnI&LL@e=QgyZdetXT5;=p7#_wC^KD4}7FJ_D2Qg z_UMUefttB;u{NCEqidhMrI&v6k*0|+LKLhh`6yd zPcL15PCvSLM=zc@sv|oGbmPhqJ$&u5-hKHzod3&u>+%_$-#(_S>rrdqo;YsdcM+B$Sd+m1cW?AB%G050MCH)&<&1}#e{ z*36~T=&4N5LQk;Lt1=ar7pma6iCVdQ6552Ll+k%gMQ{(&_WTh1)8nKUkwe|jL;PmD z7?Sb-i9Ph$Hd_ur{m<}EU())2%Q6P&Ab@yO5f*jX&naa9TR8ZdJoNP~=Fb58 znY*a*fqjR6e7=WcyR`C8{S2=DKcgu@j8A<(3(T9ZGOT|W@0s8|gM2juyjz{xP8yg` zw{!G!=GuNjoNxQG*pG>ih=QvU!F+HyxDOrpkXqJT zoqO{|-TdVzy7BW*b@UEe&cvWk%^0m|OM_I~OI`25-|6t@|D+k2;hGferU4h^#*i^>lBijtHR21RaO?W^LvfeZf6i+jmg8Z9}T;Iij*HXOvibKucnor<*fDQ-dZe zu7bWw$w~#8EeM>Z>2u~NwUYSZsByiphs6Dcf7kcN@28LWf5-np|IDP^_5bJ&gY{JM|1|3U9&&(8 ztN-`2PZ9tBi#OU`9p5njhquYw!H%1^dq^JeE;#16`n>sMj<;6t^~;7sA3iM)aF4MS zhJVAf;nmgs!L(uD>UVZ+XCvITyuW8TfDhZ&Z*Z?0u`?F?JG?tz@4atz3TKa8y}|xI z`@4A$Y|Ov%Z}{5q&vUiLO^4Zqf(Bpeo;Q?Ao^SYf*x!XVm&N&Z zsO?*CF+xr&(TVnsMasX(!CT+bwau`F2h!^F@?HtA-nUQzom2U{#F;>`5&r3a$b)uqh7UPl}^9#toCi$ zt>^X~(z{nL>b)E1_3^DMy0(2-d-M0HBYcl`B%aopn%jCAyLT6!!+ZPh>%`h;6u*SM zl2c2xG`&ydL+IQe|4b=xcv6V-Ex$MHr_-Y}?Of~W)x64#9`m^+^zqB=5bKv+XSU%w zdH%?3ej%~G&F1IunrzeJL4zyXc+A8{iAUft!VfT?Z}UpgaD^k`T7)y7zNF=Z=Hzv0 zZVt1sRlS;@P@+{SIVx|cQASRNP9EE@`l?cOwyjZfLn(VS*d>I{!0=F?_6}}Sv*rC= z9XfIx{u5{O_N{1ZRH&q)guQqBhygBY`$TbS;ceXV&wIS}y-wS@5vZ(NZwKpGmzo9UI^@+KvBDT$hUfPp00V2L6rz z=i#K#4@iLnyu6jVW%COPUvpmm)a;%dHCK;4IR!gFjc@!y`7z@+ZKX$ZVv{v1F;vUa z{MnB@lbylyssE2?fI9rgQ~zTIXfb;L-~jl+0h}CJ4gQ-nFQvobe`PiKf1fx0H~gnt z{O`j*e$e6nL8r^hosWOFb_D;_-H78&lh5YztT*R4`i4pH>$MeLTVVM2_3yvLR`~qC z5BuilEf+BS+p$`otN-&ld(P+k-F@u9S6su{44(77uf-L1y{|@mzx5r=me}>|{l3Ol z*=yX~Aoa&Tq3LV80si#?*TAm*g_^*x?sux~@H0x-g`PdwPalNaLyVse_6_$boK*7s zyc2(?!gIv6%y-#bn&-G3Vt8iBQ_1;KjxbZsZ6X|(L~3BBS7kNoBx+)b#K;No-4dt? z$MZdi1M{);t77(i#7xdZMGR2G>U~ck+ul^j<_C&|6B1E>P}74`C`JS*s)b`?w{#*e1jGbJf2tnQ)SHPm5yNF zcs!1`3+}C-Z!=9+6Y%k!tOuHoPC**>!*o64j{aCNd~U1hr9{#*i^k`#>U)D3-52EF zcv$`=&GhhFwA|xSbW)~j*R&`hHC5ZTZDPM*o;sN;PELwaPfx45yPGsT+@~#@n$_M? zrJCwOZSC8jgGYw6c3ray^2ra%O0;$RHtj!hLKj|mmEE_;)G~-pQ_l(Iw(eI{)ov{- z?$(0DQcYdN+<&OQrp}+CSz$56L5Ea%8cos*_vlzc(j0JbHWfzM7!n?<>RUcl`f1 zkPAAyn1VnhJA-`aQw{&*dS*!ytcv@Z+M@__JDa`%DSNz<5_HS<&U}H-4i~BulKX6US)caBgiTmOBxR@V*lgtcX8l2UFQ^dGO&F6iB zE+jf`#G{G$?j-W{lwt3&+&vzO#-QdU5}Ux_SC-T|NDt+L`5Dx?&BQ6wQifHnxy{TRu6A z2b<<0XYt^lQ^|9)iPOuk{1L5!|5O99ehsraR@?j5=X+=Ki=Q=34)2_v;okcBhWm_j z%rHyXQ$S$uOZI&5bsy1ZC$O79@(Qkd$%jEAYF}3wLIUU zqsNZ%+P&(-_xBARQ6+P}jr9H29DH3B)PrN{4rp0Ut7e3*)|4f4=<|grrHMK3n&vzl4??Q&nJMdlb#sAj#cld_`-~s=sG=g}I{n9aO9H6@FVy$w z|37K`Ux)uVbO6x-U(6nmWqIrhEzu<6iD_^E=As`IkU>ARYCriu^FMsHo&n;K5&!SQ z{}%H9U%b%)o-AL-Kaa%!PEQ9c`eyPCgN8ZhkHN0hS;Y1?c>R5a z!utq;`?;L||6umOgfu-352RZ$mGv6F(yy-mOwA9Prk6i@P0xS$hFbc!Y4RL)K#m%# zu&gZYxbvbe{rWQ<|LF%h`STy@Upe%;UVrg3?ceph5~5l)XF<6_$O-a>*FVNxt zj>0;?ee)e=5^uX$U>W(qqUPIL)%lXbD|g8+AXHC`n&O=SATM})6!QS%;3JO@&}jO9 zPtHLD+~J=Y;D98@0Uoo|dVocmw2B@;H1?bsFvEW^+z5;R)97>i^8fUHW>~QU*88^l z{}=aKiI=TTXZ`taeeNI3CX5{CvmMU8KHp*iZ%ppB1IFVq%sbpa^xpSj+xcZ|fDflm z|HF3wSkE7uVAyx|fm_S-zZ+lp?)!QQ-?anZz3-lXbRF+?U2lND`77T0f4yI-U%f%^ z=g_lC0{M2Jw(U)2|3ic>-g%DsvTW+ZPiSH2Qgt2QuhT#NP^W(NiH?5pp)UOP zbM@bOsHNEl;e#L3Z2z|vxZ>9ehC*rUbmNqP*f%h<$G3h17zF}Z$9t_{=O7xt>BwOpqT?a(H8_^q~6 zptnc6d$%e-BUzi!729ob(P^~1%5t>6y++j)c`7T;((b`6+Sc2tb?a(Wv!+JHRrOl4 zv0we?Usczc_nF`Okl6$@zmMHlMBN@e85pa1o(g*7S5(gKzZ_zr6mk@sFU|$~ne=Xq zn_yb2rs0wW?sKsL=KpQ(&+>npbIxK8#AY)R_-+{XZ3l?g2AKb+{Tro7#|Kq(!@_)mB_5fTr9&f|{|BCwG zm*C$p?f7?wNqo6!@w!7CV1C%y0LukNa&W_{uNGifG<>_fek4{fTw4ra*mkkIU6X6u z@0gu%IRLlD`S->4E)KA|A;;AkNAA14-hP+YI;@Xe$8n8#Z@ZrNZ}8S9{%C)X{fyuL z=7S3T?V}=n@#ZG{|MS%S-*G+uOgMh&#Qq*)|7`O9^no|Du=JRwXYAGFlwF#VzDF~2 z_G?bzQOzkisks$!%d0MEe(lo=sK2bmP1uJP^kmjv)1r0PnLWAA_xQS&fwg5FFM#)J zTH5x!0$ZOa*1L-4=2?YR?p8r{n>xC?R9sW7x@`>_dag}PU8@urFin%E&CvIr9H$bv zs^>nwqo;rR5x)Liodx?tAFykF^UG+#T+*x=59B}R$6CDjGleX_r&$4I3d#ylOm(o@ zsF6QQt@&p62ij4{y#I#hb?fyXYtQMc3SL#9X-mqK-uARA(c;OWzL!k?ok^^3eTo#~ z+hk(&JZkkd)b(sG-)@bs?|S*@ag>AmN433Y;p>m&__^@++&-=c4+>Zxy4^n z8v*ShVJ@E%jo5Y;1?IcO$(rY*LVirOE=%~h{rTDZGx6`&DNyU$x5w=(%t8F zX<%cGjt^~9H8s!fF0?V@V%Q(J6`i40J#+E6+Uv{MKbEV`byeEj)2vqJdxi&hXnTK` z($OQXt7P7`vP9j7u4>ce52@Y1jUMX*`giZE_RJ@$JotuYqm^JihJs`7Q2!zRA->N7 z|C!V&Y)6Q3v-8Pk%m&up{tsni2MqK1W&^;##Q<*qAK16sB>cbOKMDJ9ygxfu|F?ah zt2%F^(SQ%Z1_Za*J`$L0Ma)({Ac#QMBGpBpZGtMyx)9( zme~OMf!mLr(XZZYcmDq?xH+c3VKKgo?ZJ-Q)xqOO@jsaI>U3DFZNKlus=E)qeK_{{ zd>{7h*82a3Qy+eP{@eE&%N<2YUb z!gjAcNZYN+>C|d+sn-=8!X_NioU)^uU4C41s?KO`%^5AIKL=<0qL#p;Tg(jFLXLmU zIR%y-QCLxr5{jx+R8y|LeY+H&nXQ5~?8F+Z)H3ExAA9UE1uk2z!_S`A?Vo>NSAP7C zuK)B?o%!fP)$JojjJfJ?zhXYQ^TJHz@yLB$zxTd!o7y#YVH~r9{VG2Eq4LhauQ>|G=rDb~BlNGRWu}vtXEAeZe4Pd~ zxJ-Y~JD*FeZ}Gly_|V^bG^^uceRk@=-?KSJ-yD-ytAL)K)kul;sV8QuDGSwdPn9d_F%tvEnh!O`3-7oUe&-laDY>q_7* z9?`n`N}W1BjMhLJHU1JcROPcnc|c7K<@ES#)rsva%1%*JWv;5SqqJi~r8+mR*R~7q zseryu?*6w_eePpbpLkDsJK*JR!YA;3$UgoPX0E8^Q^$4Khlga?&pG#x#5`y;QunC1 zjXv4Se;dKSckbWu|H)@u>`(4*{C~rLJaL~7|Hl1uyMV!eWalmC=R?i!n;mGQ4%l`} zA>;v}t+y4?{<>BW2ZU|7t(f&MXer#!DNABBX4*VGHD#gO0r2=-+XEIu4KR`a*EEe^ z4E~t|o)}i_dH@dpX&be?d>@))@Ldf5`1=g7?>*q3$9>@ci#OJSp)!3#Om28FtXmx5 z{5^FvUmk9L+WB+*y4CxA*mw6`EMWWkEFUo3eH+soE*%#LpKSOv-237Mvj>jnXBhXr z)~>~|>)Y+09(wQleE)ZEeS1XhdfphoYXjWxSPX#O`IF`U9~9`P4>qaw0Qkq}n`WQ+ z|EzuBe=i(g`c8)brBx^7pSM>tJ^g$K!GFYhjSlbNbZY$SjT#@lNn_)-$S-l5rlfAy z)bxH$Lt|@N_CCJTdx;f>n8`h$c}3hZk20TGuSNNN%$2NX_N+xYm4#~Eyg>z3Wm**z z#V(Cj?L4qv^OsFW7cNxKzj;S@{|^1Vzxz;6zyFpRhp*C$yQo?7;Q-HIC-0KqvnLOq zzwoN&PH)iEN$A2&@YA?}$!P1;DRm%4@f{xReeoIHe)|X7dw_acXq+am$W_tio2of& zex5q!A;UfQ!F`&|?a|a#K#?044l2(rVeakv>FeuR7A^o z`7M1yb5o`eFU`;tZZk{cb^F3j4Q{N{g~J0nwy$3;O_eGyOxNb_7Oknu)}cfFYFk^a zmc~le)|b;GV3#hM#2dOd!u{!|7f_-dTiWOg)@aM6SJZgv`zk+1{#bPB3p{V$cEM_dB-D{uT29Rs+X*tLA-k@776 z=e}_OtoD~heq;KdhW}(@0Q3EZf71a^-HeYXRt&6vjyT{|g}Zad)d50U`E0NSq3w71 zjP7C+&~@l|4erxjMKm5`2Vp96Nb}GDVh(uTYH|SZKOYXD%>XRPlHc+?O$sg4q=-s5 z!1Mq;8?>~1FLQoA{L}LvfbZ&Z|9j5=_Z>e?{eK<)A6~xse|)&%-TbGIlW(!W*Om*6 z;PSgZ+V?Yi-sivF><_r}@&CNO+RObhj9T9B^Zm{qa9zW`z21j=!@kePIGzzc-G0|_ zZMPN&I6Gp!KC4;2#qWQIS7?8~+YQd^zQX_i^}}NQ^!_HTJ@5kfH|!g)|81w?lQDw- z%v}$(tmdQ^7VOjPG_ak}tqH5w6H~Qn^ok~p4u)$R!fmL{hqq{4eLG!n2+O#-LpGrPB zag1iur!E|9P$L-p#BS<^jEdH6Q_;2yjuRiv{D5h)XCD0l{f$rP?}Gnh z#QcVTY*;Qej4ERNI^v*m;{B4F^f@`kbEx1marme9Z+n09d_8~>{F}zR)Bgqg$<&Av zHiCU>0EYh*_<+kBpVhRyW13%fiJb67>VbE#1NeJRDEJS-_lLJqManR|`rhPFbb!zR_fKLMAU(h(=zuT>Jbnec0I2~^qX#$}{4cB6NB+>F-<_r3nw>iBkjo*TKAFRt*}7T?d!uK4h8*KqG+F^j`L zk4J2Q-Man-@8{wGi~m0;(&zU!sbxR-rypl?J*LxdIe_tfEe^;A|G~AV6afC`Frzs& zp+^&<@%x7Ll?{4oS(Tn#T+ZHAI6BKJ&|Tv;xLTuEuE8d((fCzOni9hf=J-ZUkFV4G z*fLFB7_7L|WTj-Ks0!bel$EIB$|61c>!;Ckw^`lXlmEah9^dgh#QJFPS#DcHPp=Ap zZ{Km#3o6kqD3=WS^huCm$`*Yp?U}}3Q_)ME!Or*XSzxNa7bUx71t#4{EF>FBV zbuFRS8Qe-uwWM2XIyUI^r4t%F$$qN*K((9+Qh3uu`q+;tpm?(UTgGW-?quax#_PGG z^y_g#qsqb*^=)3OBL~vm^hjU#6xVKoqwEDAmzM&Q9M+6YV8~$e`Z`H&Y@V{y; z_{aBy^)W$Z)LRSTUFI=+mCOA+`Y#1~Y6+kH(gNb_JWUHu)0`!%6qDdlNMy9K3UXCU zZ8{-wm9`ITrU$)8+xohcTauzJgIl%h7+Mv@am<+o$Zswhu7R&={^DP2!Q9^{XwF9p zS^y7jW+{38Qca%DtdHL~X2^ojcF0gfZ36Y5iF$mtpMsbVnu{K<-_kh6u05!-)22W8 z3%HEI1vfd!{XpHM{d6l?q89A>kTbvzO2B;^9rmxhDKSx z!V0RiFqj$#eU0JM16o}@PqpXfXj$_FJ-+yH1(bvTbz?QFWTNt_V)WvfT{^UVosRGA zA+IM$Q*s6rQV8ez+9fbch&^Ms|eY+JC9j3etY6pjI zs`TL7)c1Z2hwF#LDL;UZ{Ui2(!P^{Z~h0dzL(Qj4)%Q-MTUFZFIdLh zas{&tIq*L-`8V6_ujT)4@2AZG!S~9B1LE|*E&r!aWcW`2`?dolm3rXP>Zdi?b3oIx zPLMaA(2P84%f;svRDTsafd6lM4UT}-0bW)FxDTT?7~Ju)R+S43=m0qUqt^iUKNYO|@Nf11^#0e0|1JMN&7SJD%*%jz z{J-gR_~QHj?uV_n{^dh_D#tW^zI@Ni-T5}|2W#NpxOqO`Z+5}!+r2r3%PXu_2bRqa zm`(8E+w6eV`CL4~b$oWgS8x0-{+;iKNAMN?-;UJ)eRkkaZ&RmukKLk{&+tBf{-9j{ z{y~ZU?tTyWhYNxK&vO2sH~{Qh{O@*kpn1Kr?u-@{4r?Cz?lY3f@#Em4M6Ba8tq1>A zXswiBZwv6XS!gh^e{BJ|S3nB+cY^#DL}}u@m0GfLmHY#jDKRZg6?GK~4qF0GJx|-n zL-+6BE)SYLh0K(kdFG_{9@(d|h8!(l9i*{S(-j>3j{KK>uDSEx)eN-YCroFqgPQL| zKW6*JPS%8}bF?HSRUws`nijrLkBuJ#PkjO0_+rh?D%GN-W>pV9(AtZ?P`=f#4%4$f z$}TPDj5CM7z;DsZzrilOJM7QBNuJN%+-m$`q0MH>tF_R@-*> z>-4D;I&`F$xw_e^J2_2Z^^^6)q9--G%1{2CPiju}c=q7NqD{C*2YWlvB&gP!ibCDI z_8fh_eB%Et>Y?|)Ze694<3hCsZqV@IK@A<>0~l^hkXE!(MmRd%Yl_o~zAvb@JI z@1f?Ias*BnIzq|hDp~Y7Yp88DQQxb-&DUniA1SE|7*62wLF5 z1sW4vs!5T|0Vj7Vi2i^22>0LOf5-m?`-cA<;(`7V{C`ROZ=88oLpL1ahwZ)(i{|IQ zd}#GO^Zmx>wLLy&3oJ%}Zv$=}_iqIIKHS@_;|y9HK>Y4<3ovQ8x7gh5f#Kh-|KE=f z|33TRi}}5I1HTXMy*T&A0Im<<>;d~lu>n5(fAwC87yqwsQq$0j^1$24qy~@;?z8sN z`=|bI`+BlQ@E=$}-7agVW+wM)vf)3n75q0i{EspGZvQnvoGRx~X23q7*K=8uC$rDRa6fA{aXwnKW5#LGvS3BzcvyvCO|mp|c-Gyt{&UcNzW9 zfF^pR_2g01o+4{@D3iJRWf|3~=<3v-BirODNYy^}C=3txD5V+vAD*U|nrZB`r2e;d z3iuzRnPuaZTM?&I`?_^vSGNW>tkZ_pX7#~~+6A}&+^HeOhb~lIO%e8FoB9v!rH8s! zIVHvF+I>Q$)EJ5fud|~YOu}Q%1ozqKq+6dOm3SrTAhVdq*x`kZtGR<#!7KQEYM$Pm zBVHZiN^oC^&#$-&pBa1T<`{^DiaELTHM8mco3?3=%l~;F;{RNZ)c}*gf1EG=C;xXb z0RBG({~uU(MiUeFP(R+U$!VN)PSycU$vK1#z+M&~*MiDR3W6hSxR307S*tm#*25>k z7DRym6~qQhJ8o!s$907?KdlAC0%MnyQVV1sAo%wSuhk56z=N3C&M@77dVS9SV*@<+ z|BQZg*|*=-4&wjcQ2+Y|+MI;w1M1;T;D$9Ma23GGc{&GGIMzFFvic*O#cO%7Z{`|GiNC@GExDH z0~Ee$xmwwIH_*$xSaq(dE9v*;r|3LA>o#mbQCWd5T|2KUw+^UcL%wE&v@!d^?)lN7 znlgC~@&0tMKSPt}El@;evVxOim=Q1?h^bl-8Ll}|ahe&Er-emZ(cpMn<(Gb`9QZ2P z@b_~0o@XEW8NQ#o{>6V)Ej|3|yZ>8dxBrvc9yM(In)7YozVw<`kE00O7obmUb$zRU z**;O*D`<01=JRb<$7UVksd>lZ=i<;Oi0(&^XDb?9^r-?GE-9e$6n*^zideHxo|gTZ zA4kojtX?4*@b~*Ss(C}HTGy}9%giVZ4z(&~-4xXvo=mNB2D#KzTC#o`vBMZN{3fZi zAzjCIbn5Kx%{sJmn{qN!bn4O>b+k3n3oKUe#%7&4cN&|57GG<-lFA!>Q^9=}dA?~9W*qz+-;Y)id5iIqZQnrMtN#hV;eWB00nBqAaT-T7$jgl1gpIJe zr>lQ3!(^XLHaS$basP<_GtmIFJs^hv3^xN{`#;_Q|06v==6({%11$bG|G%L4w8qEn z)TD$xoPC;@vR9MRum_n#_$;e0lM}o|ZNTORsSCgn2qiaIL0upi z8?cNV%Qz^@>Mts=a6h?!3p}PmO~B0 ze~4wK8hc{6ZgCNKY0Hm+~04o z1AlrO{PUVWzhCa~|4Z`!R_cGI@sR=l&%3XWI)KAJc>pspE9=j&AJdEfS*g9$05@r3 z6u1m)ptf2={9mTAiwhk7M=eOvm^rcXV=v3Z=~Fd-;X*JUp*jAunO9k@!jdf2*A}U| zI8Et^VQ^Dg=u55DGtZq>S1&uVwsq;~`C)C^QK!rb^c*d|A3X)l*HM~8ZExy=1q$|{ zwdcuZ@5DT2M8|6Og5_Eo$EX&@aKc}|!6BSw?3tyK_j&8O4 zI(k>t=JUyCtEuxF_R9_TBQdSRz4i0ZCU!G=}h;s!K1fs;I_iK+WE zC2iPi2l9?;W&zw1?8V}`=M=h@8XZH9LSA8k?PNmsj?5Y|Kw zuu-$%|1C!|!S;N4dg1;v_n$%CFU|aa-#z8_y+-_hPQQWw^9^|Znta*ne-@*Ex90Eq z@8Hic=nl5PaOp6~%&*x1J7y35#ACz$S6svPfB1TU_PZAEyS#nG|J!rE7{k>N?V3F2 z;~$tk@V$@Y2$=1#_`zL=_@7&g`7H+U)d4IYuo%G25c2Q&3jBXf{QsBti|b=c;+(v0~gV!n}d22QciBNvNPlHSa&@50&r%&v9fzt}yqsYUq} zxo)r2wgVl?FG($)# z&F1D(-!l%6m#HHY$@C#p8H9KY`o893$Azp#D+Fzp;wXq@KA&^ZnMxw^L59!)X-|2U(o& z^hk+=Osm~n_aqiFpMMr!BR0|5TI`GM{IdC<3^)5njsf;<2ZV3`SG@E8^!~5`3FHD+ z2S{NKD4^h|#zggMyxD;0?ZlVcHPKGYfc)ZiXhI@3A$hkZdG-<$?AO$sgY*lJYA!Qg zW(Pvny^IZ@FF;(dlCzvy;w5XYXdyf^|I$P7z_)2)berbnY@^q3gk0bJKeLFv_`MtE<{ce|S3$aU;FU!Blh$83bXAFp@0g2f^{HXC7B_r?F-*LZD#Vc)Aa z#@~nUcQrt>6a0;T;yH^&zWJb7fBCRTzjy%ukNH2(AhSQV!{`6PKk+{rfFWznFq1dz z@ITvQ9Dt3Q6y2_g5zP+&e#^_{7sS5RKx(a4bDc}=6-~%l^X6zi_)m(7R&HjZVpc6t zc1nawvg34c?`G|Wi`rCIq~ctU+L%YXK>us!V4qH1KBwN@wW_HP(Fn79dinZgZP-+#n2w1mAD*Js1yh-68Kns+?A%^AUV+sUnA6G95W9pA z6F+pr>)W-VT^ElHG5=Gq<&goJFn7A5YIeeVA_hmJDUJGGCb%@8@AOu%g{k=I{B!@n z9+p3Yd-e+60{6yiqQ_az&%C%F(JH{_yWLFW_iq0p90$XF4tY=}`$av>F{F_9rx5$6 z(Kj&uziEGGkO$cOf5u4vKbKl)3h{s9Ciea^`)hrFi~Zy6F`R&T`DZjbvQHDD`s5eU ztFhsnRogT^Vk^F83wB^TaRGLLn7}V-C!finCZ+GwRBXZY+#~25pV7j)XPF(o;c|mz zO)n^@;i>{_$-l}^QVZIlDT!M&D`!B<)*Qp%Ge>Ii|90yB=*y&$`=|H6fiC##;Qu`R zzc&0e^?c%H!?ur4Yj}137yS9^fZq8$uU&9hcX;*U*l=lBHQV9CzRym$oZrO+-_{iD z^*;Mx&--$IuYDQu{dT<(JK}N&^Z)$J{XTKV*UTC{ssWhY8map^{sH#D`Vn8_`~M35 zfA!{iwKMPW|FpxufA%iT#{W-C+~n{-3H(o>2jI87!r|X< zNgnmqERFL|(fH|s)cocuDl$T=mM&3ZWUvxrSE_Lh-~aY%ZCPK(tV%3)Awj$MY}b)f z`*r=L%kWe-qeWk%&09T6FPN$6%NA>X_$ss@hyy12X>KUGGx4#Su^f(HAbQ&kyO`Da zfL(y(;DK1GzsYy7>6ew?8KmN6ETQZ3BGm3wl@^C;g$kz#$oL<#G5CnEgzu9 z+@^)uZAz}*q=M!R3d_jRDo?t42e&A*C{-nJ^p2j|qm$^DU8LXF)|{&v`gq;!)Ly&Z zqtzXwls7m5-OI^v+MYziXo6-nvv;Rqvg%qh)VG#BOf~u1KisMH+shT2vWU9plk{Mw zDt5zR>Ttw9U_a|H@jfS&7|8YvrJB#jmX#CFuL1XK@cET5!E3ldJ-To!x{~7ns_I{WKV7lE}|DXDQ z3iUs4z7PJ11MI%p1B?F`mtD~KsJ-m#*$r=`S5Jj))>EN98XdBMJ${|ogAE!Nu}R}r z_h?*nkH*Jtm0vvZ0k&XL>TZq87}BKdgPK!xLQ86&p-WtBshkv{E)#ePJ z-+YLMw_%o_Kl0;X(_x?IeR;mmzx#0Ri~kMN=JU-4Ji@bKo$Fcs!Oi{JwcUGIpTTPf zOhe3jA6sMbfyD~=d5inM?FU%QZ_m{_{C|V?&%eX}{nZ=jJ%azV5&xgN&l~@nuD{g* z9sWoBzd!YVi~kM(Q(`;Gr@((`JvD$z;(zp^==YCVoXxBz+-vZ^FlZ6`+ZJeb=yDb1 zc~nlnzq&eKsi_gl0Q+6*n{@EdZk>PjqFT4GQ?Rp6^)0n8r0U>h+V4(-?ch)qF-mu z?4$mfMch-U-mP^yfKJD~+gH`Qr;NJxQ_9(au3p*{%?zEw-qOjMQ8ij2b+c7jm84Cz zdD=*v5R%6(tc0iZRQO|>QI?`q>bM1`n1SZ=FfNnD`4;mhFlUs0^b52K{!NX17H;PV zKEDDjLgO=5jO6&G>_8eE?2wX!THrazoZcaDJ^+_= z101q0jS6P(_=*ntcdhInWL_h*U1LJq;GcBzdmH2z1s^ST8#w~p(&U|PCS@wNU>5a+ z0DOPon#<^*qtl$ljEbj^IAf2N*B^KJe=2(iQ;7dl$@vZco_=;)5D)YnJ?G+o)6DoY zn76q<?{Id8yv49@cEEeQ@&A{u7l`%`@&CVn zkdOZ-_dW#v@%`TT|4oPctbOm$!yWPehJU#G3(Wr${~P`%$8<1L(cUaIj+ z4gWbBACOLuZkcAxorR9ZQua3_DFKb1=+!Hfo*Jz@_*=jmBH-Ay<=^sI~UZT1~^b9(2{GCYf} z860;Hwf$tP?NiH&fxi;5lieD9?3M+N}%Pzh|3z;qX*9 z#4z77PB{aURoED+pd|EmV#mUT84vzv5Suorurxx`mOsHh9&(}d#nc-+*$;xxZJ%(d?Hj>9lo00^Gt*aS z`s3KbTt1Ub=9MjOu>DIOK40q>#N*fFcEM}f_NHRCzNV;#)AWk(!T&M5TMhu`htdAy z*6RP+@PAXuug%{({9E2nEij4MhrC|)5yv%a8XVH))cXCC@#U*~!TUyyTHeWNqkqT# z(cot9S<$SqD~x}-7VddFT=Y)jhAw*2^re_j8y~k-Pt(Bv z{QSLIQFoFWvDN>;KlQ&9_<0eZ7ccnif)CS%cY8m>zK`?! z^#^7r>@}8KSbhReiCE=J>J{D?pX+09oXrCNU!h-S`vuGv8UBr{{I&W25A*qVu2;vA z=O5ueXYX6|Z0P^t`*VjMK8pXX{_n&8^rUX^-wvMw{D-ZP;othJQKa?(?Y1J-NT@Nv~_I;GWFI~0*o zq=NP(?3f&_Il2vgB`kmTmy79jSS`@z`13FPBlQ6^UC+|%poiksW=G4Bm?9B= zPdvJ9G4Prq(PNIKh92Mb41B9RrLBjDj9zLAwUJC$1F*h>`F_&?A^$i0cY}LQGWbvB zB*EX$VSc-yn%Hs?v->k+G#9__pD@JUUg7}a|1rzjG-hcddhTnu^`6l|cH6*njd1*U z4tp>fd*Du3hem~WX>`~I_P=xxD|BgkVlP~N`s#G%(|y=B z9J(1JZk?@o^m?-qBXt9=Yrk*T_xgX{i`@ghIKb5h=qb9`!QRts6~`BInD2M}0C4Z> zjP~!r9)0zGnf^xnZ+(vTBQK!;L;sKZUk-lX%>ZKqY)5_eE_8Ir{}S^B6)fHzwTD%>`2|H+9Z?kfpK@AaG>P5MPfnxm&;H!Pq1!6E0Ke`S{Wg5P z$Kl@MT=*;0_bSL^>*(dzyh5JOyiOIdzSH5d+WuABtz%rZky_RWkH_j*S;V&KXa7N| zV8yh^)4)x_&`+4t`Gj~Mtunsn;akxxLZ>>2eOj~P&A{67V~|CH{RT>S3=`zafl!(;Y2r+y#vUoDDB%GS6^%QbdN zqUM6<+4%Db5##_XTlB=TI=JTW%>%31@mB>mqQ>2hSyH2?7S}R|Qp+4ly|WXeu?Kz_@?u1K^)rpb!0zU!(v17yRot z^vfKcz?k#h;Qvcv?ms!qd+YgM(~~z2uosu!zP{JLTi$@J0FS;JfDh+(t?$|j!>_L| z=zGp#+F}TB?TZ79H}vHQ2f(oJ>;TvI*%gQFk$A)98xH^W{v-Vhe%IoJKRpEh9~S7h z;J@wg^+)*k&HrRl`*ZwX>i?m{|BK=OFUTVPPy06ipNRiA{QIq}(M0_Jgk@+g1Qo#j z&qSv&T=VD6RanqmWu?cej2Yjx4fRTjicluADjm%DKKpi!^yrv^?-@3QJ4W*j%IZ^klVnG?VME)rsQ=)!0~~ z!kh#h*u&19u4bJ*b%5GOnASCwz}Y>m;T;`1b9k#RobE$!bcdcfc2IS-$qH{Aqqtr_ z%~?4X?h`Wrflsg>b0%@X0)^CeD);ES>=8sOm|du;)Zfw%vTNuNzW)dup403YgS%|F zuLu9t#^E8}uK@cM!~#{sKo!>Oc$WQQSN>T=PybGZ7wO+$V0MSx$Mz1S6X&PV>+ryt zNEt$_U=Us~^Wo{;Pb;OSSDrQ7xPMkb#arq3M#^u7zp~bCRo&^gsQrBe?*|S5JwU^M z7P)}!fKDG`4*+%}8J)LOhyNQ&-Ef2Xy&KBh!Y-hy0mZTFBPXvy)m04&4Ug35@pIAl z3)l4JRcL;a^RL9$2Z8lP%$O}K(U?W0?E5W2@3Vx*+~*9i_09r4!mnYP=k54x#dq=V^Y<1D*s;IE zYLC9(^~L{&eS6Pu9(wl*Sq|`L9^20xKbiTzfB!H~za`Hx{M-Dm`TuO{ex?DC`5piN z2>;nT!T+{L^*_VE#s7wXKZpNf{C^>Qz$|J2s}vrwO7YbFJ!x@@j*nAjN~~JyE48J! zN9#9jR(;2i8v35ohGXnpJbg=*eJ7N-hWc+{6#L*x(4u-zMeGMN?co$^`AOj2II12_ z0X68xn`m;7)0W@)FO}o-4fp2rOX2O8nhqDW{X%e`cNuQ_rGKTD|IeL?ys3Qx^Fm`ZH*&20f3nUx&hGNs`jik765{S|6C2IAySqzf zCgYijySsZx0)Yf6R@@;-p|riVTbxo_B#J{zkwV-1uJ^m2-<-gG-#>nzGjrz5IVUHx zp1t?lYp>m7$Tj%ecB2>WNBlgl;v=sqom?dn+(+zq6^?@4$fn`h^NR9su=%CWHTQ0I z575gm1@|TJdqvDH*{-1ic#i^d{k$9gLjCg={k%7ndGs}99C`&!fKkCaqe#H0*_=ArD@1vM88_ zVw`}TPwqAC+~9S6@72x#%l*$f9{_3)9%mW=J5SC(T|oS&{zvER@NfPhM^5AcjxdW{VF+U04 zIm2lrF*#kpC^=vh>sKs}DF01p+i_3iW3y5>Ro?h1VXW6X*_R;z#A9wU_As3HOpW%}= zYr3DRtE!b>T%z2JWMz<#Z&=$weZQ0)8>P-`cPn}Sv7_5nQ<c6upWW*|vR3sa~dBxQ42-FxBLTs--$v zefsp(w5U`qy!n1r|MY1U-SE7!wmpOH3eO*UuucO=9uQ9s5KliKna?<8>3!&aZfEZQ zr)Ym}b+O;-|CqMZs$6)Gd}+SQD;kxZQ6~S{vBZxAg+?bSCWT#KW2fW)IheYDa{!%5 zuZh{}Ix>S<6Z*}=~v9Kg-~@pHqt^9TZub{5#>d;DcQl84(2 zu=4`2eBQA7BfL7y8ZLPbyWiIXbXWz)E(fvO&RW23tq!p~!o9!F=X3gm&iCrQHqS@R ze}VXK_;)!BXT~}7|DQdVMf{(q#_h)c5%bah*p<4`*Z=RV|H1$GtPL8Kva%EZ=zrk< zF2{fH-`%ee9bgXpUlv!gdX4ngaCWo!jivq%3+Df}PzB}1ni-d^Y02fP?AWUI_4}1R zw^37LbL5xa!r#YnW$k-jx$MA5XFfN@Y4|KY|G{zc)}t%A`!ki@N3PGU@%mQhyPh6@ z=ef39qf^IYUOXAcnMpyTnv7Sk^#eT)`;RWyk0fS>(iaOtvp*YtZ#uQ$%(;gZ)p$t$ z)f<%5zDr39wky19h5Aj+(a5NI8ZtSK`ZP^71z9RD$kphPqf}j5q`lkMqFZR!D(3H( zE}W+wTh@Y=Jof75C?_jk2M=tbt}j<}9iKsZf>z?kanq?|cyw=3YWy6PQ8%otYsC9x zpUOH~6j3!>fpGsLQ?uZuPJ>r`_369CZDYQe96u6{FLFD3NA}}Yz`wih&cCbX&M%x_ zNipZ2!#w<6A)iMfd}jgNemWKBvD9rTm z%TFk_vRy?r^{T5bS4%~j>I(wYRv4=aJjjBxO4M=JuT*;9i>i2lGh-S4MFlIV0pKKT z|2KVrWb%OI4Zn08V50T^$f=^>{vtc^A4z@{N&O$)aSJuzcFk+T&#-Eq>KfTkT1Onn zsZ>B@oF>kgt&p&I&6yLc;iJZ>YxhCa9JAG*S<@av@%reG=g(mF{tSx+|IDC{g@>M8 z=;l*<2h=gIM*NSm{C~Md5yKq*;r|@|$@k;X{>7mIiX;A;9%%7)tN&Zj|K{luSTY{n z&CfcVTdcKSKqn@_l*5@}7u*|u4YP(rSAX+!_cismX>i@wzQGjW|2?O}$vFN$+sy%7GZ78IaQ=V# z3{)6BfK2k_;D`tf_YcRvzEm~Km#UHXy-@b*sl)vc`JP)SiYjjwRMoo`U z06qO2G)sA@vGg%&@!(ynjVqbi4V#62cZt&C=BTPLTctVdCEd1`eLU>im|Lvi*;BNj zv0j@tuA~3IU1eqD(rL-`0}9npS*LXyj;nRo{feB2KCQTvc})7CcC-275C)c95Ske4#Q;AZnWac_N&d~omO`+rpqynZ(Me#TL92>cJc zJ>vFx{F-@RG?VOLid^+DTo|*U4I7kM*#sY#r=t9H)uPd?Du`8iP83?>6jhbPsd4{} zs=Aq-@0<3^uYi2~+H2U+%m3|R<|^s=W#FG0&zX|Q=Nt$B@9FXwbobw@&=`@~>52v?_xmJHY=kFaDS9Me9%fPwzh({JZ`? z@js6FAH)BW9q511#aaFTfnm(${}#JF?E7~9cz6f%oCTH>cw8KLIXQvh&+fO}z{~r+ z`rq>s^y0kX+kR$w!T;9%c|SKi`_7Y#PlI1-1UE12;ErZ27+r|DEx_e+2sfIfnll z`d=RYdrZrR|0n*BXAWo*HQ1mT8a-yRA|vqLnKV`71H+Uuw@!skwF-%3K6lDY`RBJO zd&2|DLaUoZuQ~~>N;*A#^PPzUr%C+%=bdDa7npZDwZOgkPZ^(8gl;#Vo?bpRejYL3 z^W?yH7tM<6=^ggw^pfZe#4(p{`-#lg!fMiR`qlx<9>uGio!QOIi=x|$C|#_YRohj( zY`tO{7At8X_;22>pgi=>_{xl#Q>w|-x-%zERRyyPs}{9sFFVI`VuDqcouIOUH0{H) zyS27d`|#S>#7s`p+!Az8@hU1zC+06^&-hN|W~QNmj!{@(kQ$m7Xyp!S`uW?H2hSPZ zuuq}$jxd);-;5eRntopNwm-n@dpM2dd)ud6a>pg-MOsenV;UYi2W^*lA>5wp<#%E~ z4#6nOJ6*nigt>?VzomYlt};C^oJY*YGjMwkv)|!%B`n&f0^UhpmV#X_2wL4Bzr_h&0f0T?KD+Uc> z!e%tcaH>i8f5d_RX!%e{ht8$M-K}8_h2@qk3YHs z^8Z0-)`ur7VJG@ZbQF8x`R;*__yPYho5!{I7hUUV`-$eE4^-!E0OIVXtp` z08b0R?{bFxz!P|{?>l?k`2+7@nt#h*E?E!I-XA>XS`A1?K60ZT|0o#$~Q&{(rd!$1YO;@K$Ci>K*=j&M0^Bzo)}LdDjF!do+OEM+7qW zI}$JcQN;O3#pY!xAj+TJ8iO@%TBH(dcPW3zv&`k95#3I#fP+gmojSd}1mb_@;WwB^ zL7#pP`Tm{gcbL;Fgv&03+b+D>^PS0Mc0czdIX^K!+vZZ>_uOngb-vB!Cm2UgFW>a; z(OdD}M6Vx;h9_VFe$vgivSS9%@`5e$%U`A9j*W^cZ&6CyN@cXKQfl)GB{!_ci;w=3 zUk-aG^EG194EV1ctzuTGFp<4_d6^FPJ2x-Yo*iqndEIi&Z>&-&_ov23llR1G`I0sr zIlPk`8n6A7BuyDMNcm|Q+PeR!YFF>U&wssw(W8gWKTXg4DP`;f>$~aU6Xz4)_~Phy z#O=bT2;QRrZEnqdpQ_?+bd9&dci#L7_ZhEm{Y^MN=9lv6apqCy=b`1vKJhl63A@<& zER*3;(n@mGdg?K_1w4~?;Tezq+V-L*SRcU6{*v2K zSH$9j5#pBf;X_#cKRN(lH5>o~3IWxKU_(PFgWZE9_ruRQh=w+mQ-t2V3fv82~ppXcz*YtMEuA9pMAmIs0FSbok;xWZ_sAaz<+OleCI+Nnfsrw zk)8OTUHt?99|aHKw7+2A;=lO-8UA0z|Nnz0$^YT=-5yP_>~d(At8DJPRJp1l< zXG15}4evhg&v59@2s;C;_XhT@PhkB%yS09tFR!_U1eE$SOm2e-of-wfveoc@o`dy2(=W&kGQ12TA~0t154 z`9^CT`d^#Rzna~>GZRadyZR1g;Wd+B`62jsmCrK z@ob8l*FneQ?a(CGroXT$D|8v(F&Tw?U~&RDLa$CU_rSG3QE{tmaBl!7(C(pbv8`{ zaDmHqU#HjDi~S+_|3xm-|2X`Db(fD5zkPYRi|52si}kLSx5v6T4c6h^On>8U`MLGq zZ2tHCPRyHb=%PKBy@ui5aA|n&JOd2--dW)5C6u}NZ|96%XTka9nuD(|&j|7}d%s@( z;l1~Z%mke`9*}(CJm<|hyuUtsHb-x;=d5w-Y2twO{?Pt{|K!g6-}?Vat3CXWK{x5c zfB*0%^qF%xTj~A5{h9v1_v8ZgqCaE$pKHeR`LmB@s^4^t7}lTp-77T+&)t~%b$CSa zcd+vXazpsw9naCXex4pPo>S;}(yx0{73k6{(5ROX^DVD+7hJa6oqgJ7(Es0D-`k&S zo*aqn&yC+lFWh>1=w4&!Uq_%l30m*!Jnu&4LD(l;yiI`>o0zYkubhU}=nKzZrQNJEusa$^8?kUCM~>wG|~=vtpsrV#1X% zCrBHXEz~NwyRxigtw7gQgRkl8RVy@#S%vtJ0M*r2Xv1;C+cHHLu4PX382vly;=PL6uDJzKD$fm3Xv7q)y5)dYK0$7>`&rGXqhDHh6MGc! z7og5c+)OTK`TrJX0+{_vCH_Y*HvIn-&EIW`Aoho%{q=9VRbdP7(X_~2iVv8t#SQ#F zR5xi}{UX)Ro3Er?e0Th%fFB`uk>Veek0!7j*nKUC(y@t%zl>_|MWneJ%f24(cMWr3()HhXyo>*q2arEFPY})z zJ5PN1gzsy^wvShEcs9&)zE~b$mzQtYefFFl&aGCl%dqdxtjlK?zsFhRYLmxv_5M@Y zF8}`%`hTncZSQaL|H8ld0Ga;x2mFsnTET4F0`-Sa?H63f{6E?s@bC1$)X3%mU_O90 z2YfB(@)cdX=_kzTj}1&x`m$qqZBqkMf5*ZNCE&fCunmuC`oxL+t$Q4 zy;^=}%r~8?Z#E^z{?5tuv*7nKj+%~#Tp#S4*45%?0=!`?K9-Sa&u!Oez*72oEq7|t z+@o&pYi0#LJVh%M0@oLmS*6IFa+NhN)Vw9Dlu*O zOD4~DH{eNl0{w31=aIx`V)_R2ACIw_Wj@;w;&IGYJl$5_qQJRpl~houjHnq( zm^MVk(Id6CIZ7>ftz-m`QAxr~hy9Y|AT`4I)ia~iSdyf=Qud0JB~lB-!2za&$2=`M zX`Xv`sb}!rdg|A!xZ>3Vpyt>Wm~j64K27kNEHSK63sjbkw2vB?Qm| zm@^;!fA~pF?z>ar(>gd`7NDnS*1X16xJUMpmey--O^Yfj$OTgQ{8Q0c!v)NM2OKym zP+bOr|G{&>zZd_zTKvb8%FF+2T>KvvvjF@rb2`@81!xXT_uGm6crb6Zzj@u52iTI` z$Mib*H~)`|hHo(BtKWUKwVUe!v%Xy2tI;jLH!Kt9iNDsC+c8{_~sSLi*z)dC(5V9)P)l6$#`y)WN;Gu->Wb~y<@v**4<4q*O& z=h6P1J(H_X(Eq-U&r##Hn_T=)UT^t7dB8@`2hh|1dH5ek{5SpYpy-9nqcHnJ{O>uF z*gqZZKmO`n>5+BE1EBl(Y1J9!9su{@euP_vnd5Sqz1D zidz4)!qMvoFjqc>m_MPK9b+{+6*X_Crld6}v;YsdNOphJw<@op1>QOh&+$T4E?$Kf z%2Ac9*{^JN1!v6PrddgInZct!8BwU9z+i>U7_W@DaB|>OZNz`8qpcB--E{VBuF#;P`Mq>BBAsv%~a*49U=9sCzYj8{!splWlY zRl`+D?w^+)suFU5^6W_TOmSK?FJ1GB5;)sZG;+!&^`3sOx=wnAp8M09miklW?|l)U zF~h!bfXo}44~WeInr_(U1&IBY`-ivOrjTa#3%7v(<~uYi;k5imp49k0YczI1qk;mM zp~|aOHTxhN<~ET(%~K`4!MSA(%Ee0|EhCfp&;)9L0DK>3>#8AhbQS!c<^Rs_4^QeI zcvJQ9uhAgl|8VM7&UHzczf?*A8o(5q1t(J@0aRu;%)8#9-fJ%tze5wwP^LbetWxb{3c)hu!Dp7M;8x z&*z)_aXCKU+pX_xak;*|KELO3kT|IPhN@tpND@}`_D4&uMq7Ix}T|Ff2w(aqsJN% ztHHsAid%FLZ|NtQ3;#JezWGm?9|iHB+Ax`Zb`hRaWz_lB&vW`6!#)}wyYg&K&vJcg z+H81iH-m1uE!g+X<|m@xOR(H_r{((0qQW(WGN%`?h#5WpzHBbnj~y{V^*c4AV38&y zRcS_AnPw#CsCp^$Q6SbxLblt-Pf>mDs#a@wIFCyR6Yr@IM9q zFCZX5B^lA$xO{;U!vnRr8BOo*9on>J1%76!@K)tYK=0VzSgk{+u2%thY&0-wb7*vp|CeomS5gkE!?Qhty~E_4Ic4sQAE(${?>x+sVw&cKp9g2S}Zh#$L5R zYX6|NTj2kx`I~RS=j2uea~s4N5SV$B!l&G*;86$UHvs>X{)^f1P|4g_wJOkt)mNjd zrZ-qoR!2Sn7nq&Q{s{B{)53Hm++UZ$%nrHyAOGob#z7JPJMljl{?Fl`{WJc=f_P^C z%=hau{)ziu{C^nyKcL0L|5qLUtvfzsTd1__{qW${-5)6 zNOXs*r+Uq%_IL5$cvies4f}Af*DwP#y?8UV#6ybT{4|={rxXqTE$_FQKYrJ*;Hdse zp3nY>yFXS28r~e6%{Bi$v@7|}hs!)U%xfDC+k9tSO-pQp%T7Fu-!^r=>EGh_Sgp*i zPI^o+F6KWhf96Z4HIPfz9zwfI{a!-<&Ob%7b85hT5xecdbxDa*N(z-$#D1KW%h)@x zR5gvw_zf>){-}Xic~AvQcdLB)LG(&!e`n=rY=npZLS`9~!UBk;LAv9Hqgso9UutA1 zdY=Lvz2TTPuU?ENFMprx77VG}&K~8bnNu(>&$P?G0Sj>O=xgKXcSh2eh(G)WoE|!6 z^C>{zP{3K3XZ4TO`u3Te;w-e7PmOPS#;oIba)A5v!_4I$#FKFUn|u~8JHEj50ionT zft$!BI0FLf?p5yzOO-!=h1Rzvs~P-H@7+~Zu@h7h*2ePkYVi~Vju7-+M)xi;#!>v}A7Ai9(OOx2=(sf`68fo(X z;b?$Hp#dD51`oyGq%Z!5H){lat1)S-720%?x!=3d{M_g8?{a^a|9kP@@_)m>>3=Q$ zpCg98%M7r^c^^K#y4u69<>iLw%W)W-S_k%yX?D4*yo&C(+2S(j_ z;;;&5K<(i1`5v$5&XdmPc3<;2J6p`t?*f?jy*I1fbv zWBh-HUVorgEzJL0{5Slk8VBGs0QmnB|5MgJ!o0*`um6XBYH*az{?|M1ujdSSzZt}T z=6E%J#BMa5$gh+E7gc)ed6nM9-#C4|9MkM^x!oGX zd!L5K`gt~sZhTfcGrcLtsPT_5hmIdlJeoD9T_NU2Q)gOU5rKDk$P)bE+fK13;-LKI zZehp!5{*l$q<>eVX^FYYYw1uzNvV>H3Y11KvW}g(TX%0!ZGE*$ON-eZa4T>EsR~v17R`^8bZ^xys z*A>?!YGzQo+L~ImxjI6v)cVtVU!kh#;i`=7ugqC}Ri89Q8=GRZpdwOPbH=DXGf+*% zoCihJ`x$VZ@BkHw{%W8XxTLvQJ6G0genE`#;zBf>{@Z|IQ#E$jX!YvTQ{k11RCd%n z#o75o>`&&LOD8W&-1`UR-*Zume=E&@@(X3KOE>_{)vUTx=&o*|55WF-cEnxvlWCeV zxLN+g*$X>tuVxS4t_cH}IT^T6LkCsJe@ZF)P3xE=t5hZDcX4j1X3UCbN5o7x0Q&#K z!X5s*j!AX+ckv(oe^6u#_$QxWhCK>xcMSM9-M@K$8~&r={A0=cE&osC`^CopGyh}t zzlR;-F6V#`_pYZ87QOzx$LIMlXPEVPIWOP0`z>ZWtnxcnBV0Z!I?n-n9>cH2ecxsE zgXQ|}4B$Bo^S(2{Ztd}2ZO?N*#yNHQcfCBoeQ)@;bBoKJ1!x#-C!q8HdOF|5|JUxX zRr9tRz4{;QTl`Ok1F-zx`v0l)1r7gWnVTA!WcZ&?@2{4+3hr-avAQttcWpqaW>ryF zt-Fst+aq{jKg{3aW90pu0XFwZzMFeJbv`=%qMJWeHg#+^ao%+L#_Q$Y1m3x9uVfZ; zX_>@)=e>Chzo}!)qB5hBY<0eI`}{4%@O+V*O{@AeTGhwh9Qv$9r`VT^PO%t&$LxAd zN-Abwe6lj@S{00rHYzhikx6k{vT`wd2bOC=2mAlZ^Oc28udSn58+Y!|((SvI-Lgu# z?VA)=y<91CSIa-KT7v^}HJ=&jbsG=}Odk^f^iM#I5j_Yq_U)d3b zRqs&b8a_K_ag*WnqIRRHw;bQ%KbQII=Nx|*ojyK1%qyFo*?M|~T#oa!UWd)*^SO9_ zgzxiNczXxZkI>6Mz*%TBzdPaeng5I0#vbv_@P=E_@NfRTLK^SFi!D!G=!;h^-J|;+ z`J+}>7HD@xpxWYy|9!4dO~g>u#}8EI%mJ#28>{v8)b)jtDh->U_MA}7FH2T&{45nD zOh@k@qxPB%Ep5zIQ#pP`XagFu0#%(pTftKYY3Aes8b6}1{N}LNWjo$cyWn-{?Rr`c z^J8KjnBHUcBY#)xul|oZe)T_E{u|DL$Nz>W>o4$7xk2IeH{j*oqQOJ_bnUg1HK$4++3B=Mnh};6_#~gY7E$5xTejao9x%8^@$!GJ(XLIP^n{Ll`N93T{ z&15bumASNJc5Eb(-)MC(D=k^QEjd|@F%Fxi&hD`fO%;D<`+b&qbP*U*^)BE?Idss1x@E;-myT0FY{#Gsa z^EVrh_ow;)B%uXZ0sdcksKJZ>^#9IN>suT(3>!WjM!}!s;5xte#c%tW595Y!$jFXSpWktx`DH4_XX|vlvYW=V%0ap~&@5QQxu$ihh0}9G~g)oyR7#xY<_2o-l11 z^XGs>LM5_g20nf_JE-dBlr#5rQA>0)fhMlgOnfp~Jv;{R@D$0<#2 zT%$4hISMOC#4j@myv8e#S%ap9^W9ATDm*urEpJj)RRQz);i^T8-_l%#7I%a8?A)M* z?M-N1lhv|>JbA+&HElSm3VMHW6>W+sV^?%kA^0!SDswznU<3G z$CWNd$95C5^31n0ryN0!Z_&AiSUYKhf6|FRd@XMfnm zy{iY0Q(k3}?z#VNJ^jM-dg6tjYybWe#PSGj2LBByW8l`P|HB8VCFxq_1PxGC!f0)3 zVuv8TzHH)ub3ugKsqvfW`PJ~5mL!L$IWJu08FSQBRR|ZDro|QUnlX8p{1VcXweCh{ zv3Wn}wj*~_?{nVUE=8NUFi)0_pZ|{*{(`vw{C8>v|MQhSG=YUS*U;!^le9N&_uo-*}q-m`Zl6J%#`2c zNc9;!0}fy|d;f_4!@c<5Z32BLdeZ}#QyUV|;pTqkG+nP)xc{gn9{yv|`u>1_)BlqL ztU~{@7yLicMDL#%4gM{6x18TD7gNEJ;m^Y$=K(dnciEZX#dKeOaG7@_$2UycWsh;3 z0J(!NUSGy_XYBWV@8kdM*7voK`?oW}ckbA&c?SCS3|da&&Me~p1I1&hmfX93Xityx@$gBkZdK|MdTc$1K55sUAPz_0(?Hv3KnR zdRjD`oT-u2PvKxcb`QS0HxSc_@fqa0nQ&I=Hj8&1@yzjTCu1}3DKd-$%t23QHGW;Zr1U`J9J>zddJZ<)mNyrBv(t9E@VgE z0o5$ssFd1vbO1}0*|tULZJRYNG+$GK3N$4oQxhjoQfAz2rNjm)Ff4`|W3JmBo5JjD zB>Fv@C-(W{C4l`zdJ|dKvrCJeqLs`nmXhliq31E|yE@-;ed9aP?dKTw`ONb0F3jR{ zGC#tkBU}e;Zjm!_H#0x@nkJxY2wwFpbIQ-K`{Gu#3G>)@I9U^C$7s*~gZlXkkLnk{ zeU`lcA?4&`YtYb%=y}q$p^W&SIbIV7c2RLif6Y(oucE*Ksw4jI>d4h1`ukZS6I7QS zp#_zxDoqWh*PpDp~H$uGXr<`=)w!56>Q&Afk!Fopsu0eAX+sv#BW%$4f%W7FIqExLwz?0R zt!~4J|0CExH#UX-KRbVCmZSakwEq+HHZgO=oXb-7jacuWxNo_?@&0k}|8Xwk#W&UZf2%Z-{a*t(Pbbkw9ba=uQ<`p+ z|H8W!LM|S$5w4r_Kg;TO`qGJq(4mpXr*LaKa#Id7bIxrld40;E*PXwd)%8i}Qxk0# zl{xfytMBQBhm$*opdSgM&JTb?pSkuqaQ`TCx@h(b>ou7Df#VxzXyV*q8VCM2u9}Ok zJ`Mkf47@fo$?Ypum6xh4^vBm7*rwfE*Jvqqdpq2GNp_NobCR`p=SJ#tLM(>4VcF2LI=R}=m7G$k0#dq|LG&YI3H@c-fHRes9H@m2xmw+kuVt+{DgyW6b2sDh#f&!I0creSB_H75e8loK_A7FJ7m??* zo%v3SUi^Bn|5h)&`!Bun!T;#d*Z!_Me))m! zeBnLqI(a|-X<_Pe<+U1UJ7h-tsmHa$)TQeX^}jY$QwP*5a5%dahP7){zj+!n2z_rq zcFu4HTs<@d{Ih>DOQzPI1CuMNjO-p^`%U(IiMfMNN^mwSyL-EVb->jm&Qt6L2J#$mi`v&_7QOHZ@^ zQjheh9LsyKtLRSpELIczL$ggUIrr?XUZ598Hxux2< zex*vl{eq@yUB7dkHZG{wnnm@>N{(j!CRSBN+1j*<{o-%|tClZf_h5>$bF$R3VwpA^ zy;(VJ%mLM{R!9{(@d9$qfOPpqAmUAFxc!8ITA77>ZQ~pd|vc8cn7^!vJR_e}QKd<|K`2uIa?aHm2ht_%= z-26B#-E=_LKmN9Il5@2#Z>pB&P0{oLSE?jvu-an>t2S(~n&U_5;EDpRoSUqbRq<+~ zzRyn#P(e~4TAw_vTTrE@iX^2l*O0O1X8sTD|MF$t8@q$JO$GPH&*YwZM|F?=L#@>I z?d13!KmVr|{@lZS+w=d&@Or&i8uj(|_yI$KUDVN8ji#AO1ssc=roE z^*2tB}P^#AOkB>wjgtfp7Z&*A<8YOhliwZGxtYJV61jjJ;J!~Mstc$k@M z!$11JT_-N*|Av3B=kDRx;kwh0*H_CM=Iye&+Pl0uz-j_7KljcL!@k{T__KK*FHh*q z8-BzixLlqXZ@I${b%uTKaean$A75Z+fbVKEzjdO#a^A& zzXbQsYU*;ZzW`tPmIpO~bLWb_e&DWFvo}vyj~Q2JfM0J7FX+$C@4@n)HCCHDs`9;d!IQ}uBDd$+Cw_w8EN(L!Airg^oM%$&{FjvZUIddF^6 zEZL;MJTyBE8^Qlt^eioS&%sB*Nliuvl)CCR`1fBcmN~kp-Dq5?ca!Y>^KW&$K3rza zy`QV*fzR+FWryC~%;kdpVtRV!zgJ*BT$~NA=fPu5+hAJeG~+$UnUfEJ{X_IRI3v^H z5tEnRtki~O%qGlZM_8D4E}pFBQu^Fod&)l`M7vHL*1dmxPB;GiA9rs1XjJYR*8_MGe-$<;4G@NEHMR z*TRBeb(BV_wIY>VDNhAuh00mDOBs9c@;>q!^91G-#-7iEXbcXJ=inP!%e+SWGvxNq zeXrJMzvcEj&3}e7fcxe@`yaJD_iwHIR z&%V-I?|-7V&wi%=`s3Sr@W~f+dAyI~ z_dSQl{o8o~PI(@S^@dmPtl;b*UpFkf@4>vy{@XcV`1kb|TtAWTefW1d0pEL%vl_*o zhZ^Jwi~H0kPuX*rF2U<5oz25n2b%# z|E$q~=*8?@S;|g@gPKkrYM%JBS3IHF%b#ZF^>g^=J%?Z9bLf89#l!B`Nc^9}h~?qj z+Bsq0M{yg4FGK`C55;3HWbLyGS@oR!S3IrhJkRu{&ud28vznB9Lf4YVb-i|?LL#zN zzo$li%LXyqafQaj50>Aep_*1WfZ6jAT0@*_tIpKPEsK;M5r|f#LLKPvTA0B;{THXZCylnWr^4syM*a2uBr46dpo$u|@Gaa+d=p_^H6TrRslG$^ot%rxMU$6AiVzn(Or$1SwqN3T_znq!egeeM( zjMQCEJ)sAG{;1aPU5+QlSoOH(3i$_5)!M!5btAn0h9_Rq(g$AGl7kPcATdg-^CoFo zHlO2=t5g~|RCDL_SH+xus*f6^U2Q3vmll8)0A8~-TN_&NGR}=rcEdttZ+nQm-Dvx>O`X|04r~kK_&isp8`}&`11@Fzz!S_G&y_P-qr4Ik`AA0f9 zzxCeV{-X~*|EDf|2L3<)&fQ-4#i=XSIKfSMqpL$V8PTj36 zW-_ntF;F6aAJ@2yt@tOe1N)2BH*vitRo}$Ut2;D$9{aCa?v-CFKEdq|Yxbf?@o6#Cl$QnjDl9O8+Y~7n!^>c>I{6rQ_SVztH0dN4tTw#PieAYf631^sq{__oZY6M z(90S<#h;zP8@2c3Ju2RlEB_4xbmfFAG(75B@K4{latO8gP;_?<^r~{TzM)9-i1%C9 zuGGyZc5Cf|I-TFjyE7NCe<(33NQrS#aR2*MvJP!n`(aHh zSj|4lWeRS&PPu!ZQzkwn@!R3^(D9_e=O^*C?bAx6H(_&2)#!I>?qkQ8s&4Ie@?i+R6{r51dpQI6euhb}ZE0-@=tmF4RsT-d7rMA)Y>$vR| zg*QE+F8wQ%5)r1Q1(Vg5GD@LCx~g#2U{x6RPakl8@)+%3kgSbp0Csm|X;ot$`^id` z+HnXkw%@xRtm!RHf0e?Oc!U}cT}Iu#pQ;1QH$L@G)jj=B&74O=Wq4S=l{{iUwo?%iSrkT`4@@#AAI(;KIAO;;A75$55MO7f9m~@zSJv!Ij^_R zf6Q5MUJpO zz^&om>30lo;Kmn&Ep8jW&y)9GB=2`R0lv5Uf5_|Y>@aPP_c+@Z;Hwqv)^Y`ResF6T z_F>s#yn8KsO|BoG2iC`T`#heo{J`dmyjGS0ee3C zoyW7mf4b8-we7qS{1bCr{g3vS-g=VF|8GG5v*t1R*Rb0T{EtrC#2)5#8j`(_-PYG@ zczd>`1qojqgd_MJ}&aX$~-n-^CGG2eP0wpS4J$LWtuk^?dk8;^5j3!^?nE5zX9)dOa9OItDpZ`x4iO=Ui$D~`rz;X0_R`r(kEZ(5^>(U z_#XUU_|bjmKl(f0f2p(FzWL52z4rEb{pl}f_2e_Z(4p&Z#wQ-`X>>6A;0CFC|6v-7 z7HiPZ5%~Y~P}ge);{Uo3-hV(Pnw6!R({Pg_T<-79`x^GG=kKfi zJ^s%)fJf;6qyNMI<0aew18yDvPwe(-Z7lY>_)Dxe9?!7u%i+C#z8BZMSZ{T@m)~3b z_wfVo*<3Mai}{EcA7B`_T;J90oo9@_#^oBozP9-86G`1vx?GP2>jbUaVn1mhEA#v+H(d@9)y2 zdUms;PxEVJcV5%In#eif*Y-2{lRu1#*`#j$75Vh`kNn7^WyCa zz)L4&<5%>ris&G!-(x*F3jUTeqy!j)RBrp4y_&%Ju9y!1I%Sr}^$BvM)OoULqNt zej?rk=9d)@{?m`*uTO4Yd;ga%?w8ZcFN51JVVlNYF7UAy2#Fj9;6?9=uKeyxRf{a!2X`kl7l{*rRqpTc|f065u3AFWZthQu-3 z8?XA9@v4gGr#YjqQvS?=YKTAskTOVXD<^4lZMcf^@{}`6K=YQvN{e~yMSEJ$osqZw89H9P5YW}Cb)}h~gqhFu>m(G6iADt)W zpZn||#P%;8?k{`-?k)avUHbGJhyU{*lmFX&VE_C_e{(;7j~c*gfj_?XwqAeh9X-Jr zu=CJODr;G((KG1p^&F|5z3`Em;HQyerl>ph!BxG$|3LOc4T->mid~f8zfVxLrWByZ z#$zA~uMc0||Nr6N>VI$_M}H~V@W1=0{`--7Fvgxddg_Mp_Y8Yr^KuSu@!w(EI|Dp? z+t;0Z0P)@Kb2{JMAK8hUx#`<@M|}FM+#LV*{|2hb28dnH-sSD;m$FNJQ+KOh#y$V;J=4@P3x|H{#R>iYae#M_t32HA=C9=r&4*?bPUJ@`*Uzizv;ZO^6c1T1p?TYpp4z3kRtUfJ^fV*2^!L!6HXr+M#| z!tWKCb{Brnw|5}@Fg^u`z&?4j%`BP!R?>!t*?YHNx$_#7S(K~@bVV^Se)KM)RKRftN%R9@t-wtD7{0){zXmF>6Pua$TIPSq!Wr>4_yschRHHDS&Ty0Uw_ zx?NeOZdYe&aK9iGC*`U+i`qVRm@=mKRLGbf%Aegw4dDY+IcKQSqW##tgV*Pl$C)d5 zojGE5e-Y<1!F>k2zP;!CQ|Hz6*w=7+-;wWsqsC{xQT>^3xxl@{{?}Uni!b%y+u!NW z@cJKo_PyivFM@H)_0L(H|MV+r{D0^IIlkorAAotgeD{68&o99RocrK!)B}IlJLf;q zo74cmfB6ml@%6X#YN)+(um+zY$(wi-sic(6H2< z>{;HeA({I%BKNRHa2|{9}cl(CUM$q`j{y16@PPjwh78V!tYxVYB&~xp?Umqy@7F-5;;r2+gleW4=C~ zd7N~oGj6Mn$7_7H<}ZlTzRl^%W?rXq+_&w1C z^qQh+QzmO+ULtFrW<@)FU2JSxx z<8b?DKmJB%Km1^Uf$9C0q4VeR<@~-|)BIWfZ#h96{J+cpcOM4-@c&jPgMWt?%g;M8X7#xj zYrzuyzTw+HmU0%jpV|HV+%C?p3&j5q(f_~lP%HC)=Kp26 zzg;#5*ctyf6935s0`brop9iK>*`XY>QrCtqRyY54br0y!H9-q?P4FUh4_U09bC#)3 z*h=+}TC4uCoCgV8)Gv9f`g0x(PT!{?Ir}v>|B%Mq?{jN8s$GkGy z=Jez5vrFq;IQ_q(X~KJaADUfsjA`^Y(-)mqa7Kv=BF1WUOb@Nf8H%SSF*-d8U$I$A zik_)DuwNMGuiA`oWk*d_b9tQBttirpWf|oEiJDi5hIz+*N?@37WXK_1)4LRH3>x^J z{<;?24;eI3v;D^Loa3~-Hc?}S^jB&6RMpVun?>(`pWs?eE!m+^yapoKI}%N=FUIBh)cIik$N0CeUHnG_kc<{^@vcKI|2IDWtmVt# z=e+gfxO_Z-;orQtotB5#?8B$&0DWhH-DlXebKorJh|}Xb?2!Y&`JFW$fLptKG2P|# z)Ew@7;dOo5K6?)17u@5(w&V5eeBw3i_3TV=_gM}=9^rZchWpOT@s^xn?>=Vvhdtin ze+K&hCJ+D2?Ik+=d)W8o|2F>@1pX)F?PiBE_>U#_hk|?mMs_l?Z`IGcy8G3;UCcda zv6tP>hQNhxCwu?sRT>z(MuQSIX;{p9^_^IwE&e;7jM?pnq1ct|Cj*^!OLcK zTeXhhzuTVAd9r$$*05tZGklgdEvVG~ZEJMmx_$Kh>J>{qk<`9VsqCpqqMo-Mb+P34 zZdRB0A7lLfLH26gdI3(K9Yc6-mfwMfuM_vB@cQPpU4(Aey!UcW;>Y1|55EVmZ?iga z2mZp$4>L@A|IBR8ukpTmNJ};xRdhfYdX+0w8{AzRqkf`QdCcJz2jQ_Dq1fOlN{?pt zwlWJp@hCM{C8?!0f&D;DXq({!TjR8MBRj^b6BL%cg_+lSG;$H@-E*3H_8p~u114zf zgxQ)t!yj*s*&1+d7iFX;YgW*7#RiQ~jNed=9Wg=vIUUp<%z2!^O97mxAALjFN2yJY zFkg+YKn{Aud5?TfAOEXP+<&XOC%#wR<8b_se5+<|xBTK;J@}V@>5b3+t#e=eQ|Bz! zf5I)-*^f8_KKv5=enwx9>!Z(f!Oj5iZCJN6z-}*m?0s*yc3tAWbC*8XB_3zj`42uP z5BS{u?EOoh>g7MZt6%-$HMGtDt>3=#mJZ#5R;PTq#)jiNIR^iq-cxkd;BdS@GTB8@ zq#-fHkosc^XFe=?sm1q)!MI(-dvxmmFaEt5AZB2w1D1mSmmaAjUJ^4cre2Pv9)JFx z!=1%f@CE*yPmjZt;mvSr80*BP{hXY@W^~;wF?E8sGuXQ|?0fOw!?26r#D8DU(eQ8j zVfTFYddB@(egLlB<$Hd^Iq%iDKG*Z-ab7==bBdh6`;$cs@|dV!GFE3o>qa+Dz)as5-v0tlS{cO*w@Z} z_8IeZ?QHfg1-ENR@O<^3T&}LYBlVLWGZYk+r%l^;>h`;C)lE0uppNBh;qXfonp}^b zZNF~0>$I-F^A>GAaZ-zR@6pB+C)B)Yfu&yKBM9IOuv@^ z7oQ1FU-q+4RdxR-`0=ne7e2r2_P?pbIDNxCejJ7P@8zGwgX;vj{&Cwa_%7J_3%xPo z=K=N!?S7&8`4bi77ze?UUMk{91eUe8{H!|!sAvJ>D4ds5IYBprGK zO~TvsF+NrE6JY#FYI|~f%kS%n_qC6b`#=7rRz3F*-TK=1`pt*`)>~hEuXpL=pQV3y z(enGxh}(w!3txg=@ZO1atLx8!?{nb!qTNqyH++9U&i^4ff?ek>ea4vq_POukhktWF zyGR~z_WWmh|H9||?%(zHh0pYdH~ymEQw#j+w|~&DUU^#w?|MRUbC+-i*XGKWX_s^)=Xf&uVas&tTE@+zi{D@z>$+N4Pi4{`eeld_rg5f9?t93ZKY> zm(Owdzhvi!;3V5E&n&%8>isg zJ!Jlb=1p>u{{Kf7{~u^n%TCk(cjo@^eM!{()(1%4fCmWoTl}AF_^19K$ox<5&{kr6 zt*)7F`e+rm~;$nkg0R1m>Dpq^^Cz{Z&&ma9E%U@NztOaIbE; z<+wI%XQw0{Z!@Ec*af;mr*FDm_uO_;$4(s6)(GTKp)DA9sbuO`V|3`G=H^ zUe7!N|ehRwX6yjzAd2{>$b_*YPOOeaY(4S=9d`_)4EZw2J&{;awN>v2h8SGA9(RY##;Bm<~TaK{G;A*|T{gdsV1y>};~y z{>XcZJ8HZH-yeS)52lY)^~hJ`_g|yo{YF)feyz&K&@w&#m1?+Ma^@d8{?gZa{=$FY z_rBMA@c8e1^tIjw`|qRQz3}nZdLPdIEcyC{Pl)GW{9|x^0nA&xKTloYV!Xw5V!B<1 zdpiftT_i^U^A`K<_Cp?f{^A$9aOrP+|E1nL|ApSY02jd7@!q9R^cuCmf4%aip5hF6 z?$tMS>_PmaD_5z{#7z4B`NSl4(uXuFVD1q`pc#)?{vdwG#QXmb|KtJu?ElBV?f*Wg zS0Ak>X6Jj@J5vGv!4!CUm)LLkKlilhZ46`7=@wJ@+QTDXcjDf10hgQey{iE_`Tw)v z{_Nx6{z>Bics#j*(+@ekdz_sw@3-f*T);H^zIg9@Udt=I=k;F4S8Mpr4(B&UEnvOB z^Spn1ug+uG&a0<#-2U&E@i}eXVg4WBpB+G{;NNrhZRX;^HD5aq9@SlU-=y1bJ){HRe&y~Pv})f`9lG(97OvW(u!I7&pn*wm9)i}k zhx&s5Yo>Omuh3ny=>Y_%4At`bG;L`r)Ux_q#l=q2_!(U_Y!ZA|c7}4d-l+_9yU`Bw z?53r!7YDy@_)mu4F1q=g=AzrJN9AX`VWiOuu-!#e&PfTBhF98n`qRyF&Z**s3yYw`QvXIT(U;-^l)SM|4GsNU*o+p zgN0vU`~mV5a+M6UNo5cIUA0eqL#+RYsvrG_${qsyTs83hb&vf`TYvSHo_P0rz47UH zdKZ4qa{Ko`_)_m&`bvMj^bfsz$+$df{!hN4RtLl2-*WknEbjCDJ7{?>68}G<2YA71 zf{)1QxwXr306Pop4EVru0;~VQx#bUMiTme?`|p!a{PpY?dgsCy;QtF=`*Z#At@ri9 z%Wvwj|9V+x{_tlVx#u}eOK(?~DV6FL(5xY8YZYcbQR4rj%=kR2 z_|=bH#(g|FKpgQup12=x@t@h!RmA@{9sdtkN9;XE{C6=F{GTH}`*82OHC$QTb^cxs zla~J%=-o$hFYECQrye(Gd_S}QkL9?!-*N!U|35H(!OoM;bHwoP;(6y8;={B(kDV{R zTi?9D?|WBkSbbvWhUElc-*`pCzrEkT@}68@0ssGxpMn2>nEIdipJ4bW*H1J2bGdyW zmIM4>w%!6P%XIr5o-;Ehb{7hYVqj6y-KBtvs9*phVxd@IqM~4ng)NGSD1tN=Vjzg! zBBIVX&X^PDoH?=nYu_*L9Dm>S&2`<^8$3L`jI14N@4a@M@c)eeiT|!%OVGh#4)>`| z;SBJEI{b6zx4s7&b#bKrk`wn;lYeCm_B$PY{Hw%rj@wZ|@$|lTSzYB}D9>UTs+b}$o9KF2@q9aG*=sZu(wzq@T z(q?F3`5o#ReuJ)KbME1nK>=_xl<0-~4))${YnvWw7Yxw-(?AaZ0fP1s)^8@Jfhf;%UXexVmx7DyF z{*0VHWBrZH^VdFvJ!5_6;D={uS=_vTm|X#_{c zEEXN>Vd`MP|F@VqR2NYJ<_HqtAb$Kc;`Lp?`kEcwcz^7kuv({k-z^i~(xj^7Xrqc>Ccu#p4T)_XA(c zc}X8Gk75U2zW!PHe69Z%uJ6U$cjOV?qngj3GyboAO?yH+^P-k^fX^Q?Hh9Ut!OM4^ zA-nVza;{b(@8%sOpU6jdFV;RSqF^v?8%)UgSrE@w^!v8Nz4i$99T(6R5c6v8M`C}4 zf5`z(*WrHy?y>$aeSNCWM&f9NAB_(c9BHwyeXha3*ape}$>|BNPd}^WrWSpVugR~=pdlkd0{6caJi7|$&_f_7{=klKTdWH2mTOh9$@1SN1_;uz2 za=rQ<<^pPmNga>yLShc~|G%aFhu}Yw{@+3Bf3yJ}jQw4C1pls!SqJ3*D--7by+*7h z{+Dw9rw0E`jVE#rXB725sr{=@y>(sgUE=IzlQx5xm$qYm+L-;?`WR^C1Rw5hn-)G9 z&epEz)2$En8$+;i*AA>WxDE4D)?i-B4n%C;fv~-MFm?ZNj9Ih>{W!~SLCwhpOQ#@p zWhjy+_QbG&mi%{P{%`g@bPT?O?r0qtO=^fy6MJK7B=g*U_0Wd3z`h|PIGdV5f5&-Z z#=V}zy&L=Lodo-=?R!v1PGK0be~&ubJ}K3- zCDm{w_U$)6fF1RA?UL#Dmt03@YFBpX?S~bK+i|wI0tZgqz~;0v95{LdQT`z?@7x?o z5&hxNn>#W)e2*B@Mwn~;E#?gCifJQExlhj;Ue>)4Hp(7JOF5quJrd#M|Ax9yZ;aZT zi{?jAt7HzQ%;pTTdnCb=uKXJgz*Tnj(pBd}F#|v@*wT%Ct@wMQ-THX^o!}p%Pe#=;c?@K)J z_V2j+{1;rVe1h}0p5S!(V{FN}fkW4yVP5)0bPHOA!80<|&KGlPMOd+?$969LJvC#* z1~|o?{r~s>%mo~Iy&LEM<|l6?{=*bT1xGSJqs^s-msffJS8>4q{J-#Y8jMR1fZ$Ps zK@EmAK3Z@}zMuA?CR;cF+5xE}(57gP#&-+G-xP_CnA}gp3#fZ3drJNHcVU3eXr}15t_1%nz8#3xjzG=H&8=*+7 z9M`g!kNR6KtiicaKQM6j3)b=Z_kDtT97ouvPkwJWbqNE~>h=y$i%0Z!hY|OFJ6@_B zU$hL|Ha#JZo-&T6KV`h{yzUvyqO;M=Fcj9#lW>4BXm(*Gw)1arL)ulWJ#Ynkj$Ox; z(PLoNr5TbV2EnO!Q&@Mdhgj44m_Ot@&g1E0`UvW3QF~|FcsDGX9mE{K6JGX)m@wLw zI^*nPj-}q_evMqZavV$%pCoP&S3vi33-VJuwiU( zD|HC5Rn!W#C;r`2Utk31^u`eTqq3|j6z50kp^!bm43*`NtGEcZ#^#m8o zZ=w9*6Qt#HhCFIN496VAQ0|e6K5~z`qa_#;n@24w>d(#NY^uzs@(BLr{D1MU@%_X; zIRZ!83ODuw%uCvYyIIWt6+cIODjzF6nqXRDZ}H2bwWGo9|Ka6`Mdh=JQ-w)hufZa( zRre9yFHOv^tpV1GFK3P++T1GUr#(>j;PvwOwV&7iEYbRr=i0bK%Q?uk%FggJYB>b0 z4S3EPq}YZU+Md@c{wM#({68{rCmdz|U;2NDf8qX|*#qDteE^LArT-S zhQq98>@#5h&%pZV=G&J22%Hf&)8h`wxd_~puljiG62$LwSMHkYjPrStMRW5B#vFdD zV*OFXf#`9JWKD0x0djo%MQ5LT2Z(*Y-5RYjFV^(jw>@W#jNIPl$FL#4XP->HqNHcE zlQ+@LbuC(uZ;FggMowWBj%DA#u0y5RaIh4s_H%YEy&MORR3IuS6y~}Ou`a?GZidZZ z-|2hI8(a_Z>|a?cPy~*5#Ix>GSa^n1>tpW5LJXzmLqaMdC_V389bN1;o&$r+70G68`!)^$aThv1M#yv-Tcin|rWKx)0-(_o!K1 zg^0a(k$&|xu0DE&D)#2yt08WguS>t)>$j}wv7i4X(K35>9(|_dt65aD1qF=zOJFmfd(P&crT?cbn3oINI{Z7- z`G4X59HbwBJwd_&IH}k4f6N)?t|tGV6-Im!y9(p%&--cyS6iD`eL;+|mEUHJEtnMS zDgR6CDol#4sEflj{l0a%f%tui|D_*DYJ<6)&m~u| z{~es>p*1xCo0&zRF?*~U^$Dcb0{5-yIiqn$OSI%1M(g(aXx^qRnm2F3o$dWFEQoWs zvu7bVegOhvmt$-kci*n$zWeoC5goq_F7EEIvh2%UxkGU%H4MAga1L#P0q0daV!5Xt zlAOC>CADjV-CDyovI&|Qf6E@v?@+&26YiX&4*W{)vfNYwYsU7r?6H$wI>2 z(QAY??A>+m)fCR1zen7F@3DB$->@{W8)B%-6~_L2cPq{%_?aMXRuFZ?sk1l18;fR9 z^L|1Y=apg*Gu;<{6T6_@kZ-8N)_``nH;l%|agLxI4&?NQtkH0Mj^z0)iCuHX`>xbf z9?d*|BJ=z4#C}NjCydSGoZ=b!e)cjt?|Tb7&MVk)PRWvf-i%tS#@wf1wxkrMOJtA0 zC7AFGrq1Y~g{APJ#@WWarzp8si|hBQ@t~UdI=R5wH=pR+>DN{K&v@V`)&YodP5jUL z9^-`P%nu|N5RTwEIYHqC1?SIR{)8trZ&A(q-lLi~c<}NKp3o*d<4j;RImY{sYgqHE z!qq!Zai;hdrY}8;p1y0)ei(N=aK5q6)Iu1|;Levh)I^(G1e5vP`$l~<^98vWLatBr zp#}dof8n3I*XsB`@%?gS=Z}+mqy`4(0OzjWjN50V|Bv{gPgZ@i;>U?&4Tdy)pI}vk zd4BCHf2+;Czs9E~hY-vPCdmP^_eb!rcK^`d|4>)T7!Qb?f_{h`0pk9|8CIHwPp`mSA6@ohO9qx zF5kiyZo%W=M()pL^jPlIo{7--MVOwj5)q5JV{cR_EX+)~JI4|!E63o(juA**+7F2n znqp;O6Rh%UgXPX`vB;_|A_uiWpo<=C13KcHw%<_OsV{72CBZL+b^CRrX8n(c$sk$(kNHeRtyCMX)cpXMN0> z+?o5jN1PwKz&!wS(5S69CQMz2jJ)eOoPCY{{wfmp-bBK#E7+EP4cm8>Qh)Fu8Z?~x$vFqu<`f$~|2cT;}OA)GI_ zTtNKKrxrbr9gicm*#-Y!bAQhC{j&GhiP(3eEzsg$;$+dySM#!U*w@DX8XRhUvNm_8 zpB8Kq!^$?WcCXRjVLZ?GH8nnRdE^yU4S;q)aISp&S@!>D6Vr?%RPT@S<(gbW^E2x( zE?B28m-`F$<+<1i?R^CI+M~@0G@n&$g>VC!7~vK7|NK;k{|NT~JCggC{y)+G)%t(Q z|6Ldl4VsXu{QqD0x0!+F=9AEbyVaHdC;saT{`G9opjl`3_ccQg(;?iQ>xVe}wY+?BOA|A3%REeY-xpUc!UBHQc0kZ>#k1kkcdfZCNXKqYw31b(=dQx8vJ3 zLFjKc3u|^2;ABn(wjR8QxBgl+>iVphTz?#x?< ze$?byHPH-ChRxxu*8ua3zDI)b_gEgNj~T=Js-2qC!UGT$9*DWo!!TpA2LgPJ;YSV5 zk>j{;FOJ&0GY7+X{4ki$+QFRWCizF=oqpbW4eNP44vBYZ|5)Ekd4Y-49-3459x(;) z75>L%Gv`0`5q`|^-4B0(N7~N{BRVFdpr_0IKM`ZltU*i84s}N_hbZ(8=8k0U=I9wg zUpqMyJ)=(2&z^u`^hs(xoI=k@M=^XwE_UbNMM=dolrtU>48I_L#g9LI`A*dWe_~Eh zO+Wwq6?1*sgGa>pW8z-wcyb<8|A_lM&lw*)rcIDq;`8eFxOTk?WmQ!;Tyz!DD~`d~ ze?9xhxr1g5=fxs&I5$cg6`hBH)AOi3$w%UUKn!&xX6Exb^HtXT6bCT7fVqFJ;s6Ez z_KXD_mG32XwOCWN zKe>kZ)L>uZpA}Y#$v4amRR5p&b>S5x29WrII2Y{Jo|W&__;$I+Uv+@G`>E^de7)EM z`7B=_Ev}WV;@7n?f_xUapV9>3bLnG{pZB`Z4?hM-^p7d{vUU~(-H^bOpqL=hvj~qvCy4!JgyxOX{C#yMqOan zqa7@JwS`UpuIS!N5ABCIkfYtnT$ek5R^4O`U-*3P_@<7S*Z_xhRZ4Gs%rWWJf3J24 z2OatuL21N4>-m9coXcgb8_2r%Q1FV&95B---U-lRTeER2CZd zi9!bh>Vz%ck0Uvi*nhkfTlSp8`rVhYcHc$p%q+!@^waFmO@K-N>D-4i3xgcCBRcUO z61H$waG30}YK-J?b2#>D0Y}~MFxTi?EHkc$mBS4X6Uf@$Sa;4R1tBhG41&A|!o#}{ zMok%wNwEnSH*Gosr;Uc=-+In$?M3$q`>FSK0A0r)LMP5{cIJ$?C3p8O-hUNYWzSJk z{)jXCZ*cF)Pt@{a{!V-FlD_{(#sbfX|EDirk-vL~d(814K4bj<@;#ogmiMHVoFVzW zXXN?HZ#~83t2c3`=sKpYIEa4aoZEYCg>KL>=uON*k4cP6BF~_2bT0Z$&E@h-A(-R$kdKoiI8=NbV{dt1 zi+jljBnJ>Xqn+{BVpkj6%RS|O+Sgj#YwdtM%KbF?fhHD_{y({ovI~4(OMX%Em>QWg z;%9ip`rjMs|5S4RUxR;5{O`s%K=?n2|78|f@IPo=DtZl1hMvU#t_zd~Sj!=kxmzVv z;a{gWdw_bd2SC>mI_(Ccv!MyPo7=&_&JD&se(cY2L%+cTVP(_}Q{DSwW280q&alDu za3jQ#)1Tqg9W!luVUo2zdkF_vlkauh@C<6?Cv%j0Ww7!F%LV>`A|jgBjP@r%;F;d#+*U&I%k%zm6j( z&mm!TD!jaAqL%?<=z&`>W@-_Z(Vom_44#nk8vgDp(W_kpterdrUc|q*UOmk2Uk~$) z8zONOH91EPz>JB$h#~%mv+sB0c=kdrNJQAm;|Pi4T;}AZ7`8M6o-0LXn{_?L_s-1m zorr(Cq$=9HyNqLM5s~!~GxEP+M&WOq)%lI}{g3eGt|hlU@5u3e;QobQsKt_}`S)ty zo_z(nC_m|pnoHyGx@2Z124soqgA# z)37aQ7m$LsBe$b{;5O(5a$oY&Q`mp@Hu6g!;d1#CV*MSeX#*ZUVodOo_^2#sK@EHOp8ZJNzsGa(hmYCkQ+D$)^3PvJ@x>e5TXLLx(m8A5v4I*h2cbLeBy=a7 zf?jwQdQHyZ3<4yg;uR7ay$VcZpN29-xZZnV(DjPtEMn1}L83t2jXHh8(${*a5+` z@c0ssXg`-cDtp8G$j^}@_tWSD$!p0gG;u(+{0zA+&j)Ym|Esv?Q1*X2F#nexK#l(= z_a||H_18vM^h3yaBUZaiM?Sk>X*NW&hU^#AO?Zp)o;dIK2$JD|r9 zTXZw(jov-lV>q$Be5^5cMVTRGq7jz*bw!M0Crq)|LxfFt1omfbuSaWGF-IO|tBVm% zhOn{VtN`cbyyEu4cRhK0a{9KbsJpv{bss(|U2M+S1#$j%EV(_|pF5Jc7k+O9aX*~z z2NL%I#K-?;^4Y^fUf)e{zx5&PHZy;wzqj8&zAWhu29Dl?2Au-XXYe?zU40zcC$D2a zYkE7!iE9Osp;B6VvvcYZE{Vb3t=b)5%i@8g)a_7*lCx`#EpuVB%R`$*pN z1zyh4=-cT#B!-(P{?A>%0j3zS{?8eP^`X5H7c_`^`+gYf?}8b#BB?!>f>8^%Ab8O} zIL_EZ&hY{~iFv`j?9^~ue~-40eLal#WgnpQEk-c6kL0XgWZq{?Jo6a=^!JYJU9w|M z!-uht#iDY~Z7k-F97}Z6?}Yf60eD{MhR@f0@Udzk-rmVa(a|*Ir60h76!u+5PlKhg zC3Q90ajvF2ns()mjo!n#_ldioY~#?}ekq!}B%+xsa~+R$XyTCs^N=0XYq*R{S016V z@)@e`*5c9gceu-#;Qmu${|ULhhqZVj@jq(>j~NeKE5C!YXG?LKXWiCga2mT3?Om3m zdfPoocWkbY4-Q)CCsDdqr-QDd@TI_r$WzTkgI zENlOBnD^5L*pLUXVJ*;(T!6&?(gP^7zzX~H_wwkdascuF-2aor_@Dk?ldq5B%pCnS zF(@^FuYIq^|7x(OW&r4;l@AtQO}{SB1?$2QYU6XQJrF#+rAP|Ju9I|JzXmQ1DNTH0d{r@jrL1vj*6xt1IKmL1>{r z5FI#+*12P6SoCg(2sZ;Hjp~Q>!9B6qrz57@w?>q8dqmoH=MMVz@aov8C7wg}<;ds@4%l03i#Y&cwq)XW=L z#rXiAkp<|%z9>`urbrmmkM;k?ugE3v2Ur@s8lr6^+S`kgW+J&i~49LczA}+Obnb#Z8_@8 z8P7aTzfbJXKBI6i*tZ~uY0bVLYx4d!QvYW?z?QW?2mWn~&bQ!SX@Az)0pb3H`}ZUd zDEP0U{-5~e8sbH;FY&+lT)~_?3J$fs^fLdaeJ=QxBQd|s4htp)%krM!SHI!8`ECuDps+9ZkZa|>5_|B79n!`C+H17-LS4hJD|;Zegg#&N|5g0J zd&_mNRR6y(?c9{V@_)tuYx{qs2B_kHE&h{L|4&C(Y5)`eEiIxL|BvU6rC>BRZ(_1#3q+W;M6^vO1eS+y^5f!{In-8GBS1i>+pCzmggR^!uJ0q{o=t9=ZNNa+aYdIgih|oT%Ji z5Shc?<%}=zBe&_k`vc?sU*N;v+iZ3w8nQ=Qr<)b`X$(O0m_fLG(j9+)=!XxFg7N;* zY}Ax*$6f9Yc=PNDo?K^bZ~0&}=jX6d93-70~0hYwR_- z{e^!G2jDrkj{oES55cAM&5QPzn*WnHK(I*vsW^A~a4qg-4p_zf;@2hLSFwPG$NMXO zSN>ZQ%WG=^+Swn`|Iy|EDpu#u75vKgDlU-s75r=B2#GH=If=4I3fr3Nm944!K6$S8 zesJfns$0hai`Q4e=C)pJ6lrn)fd))HZi>*r8!GRCGgCK2<&CarkdVc6h~jPqN>F>v$V*lcik8t1j3%vGyV$b3y+U!#9XqbZUIt)OUew;b*=#5ni2jOj50RH

    2i#yM8=hAiDFS~?m1!u5z^%5j6ibc-R{m3}52gyqpVe$NV z2=EU=hxT2#lY@Gj4ZBir*MvHZ-2H4chVvE^Sqqwk#>SirF`I=31E!;c#Y{|?w+rbf zFX3qV8SY%$j6*y2)2O zju87ij0O6H9aXxKM#TTXC}Mx=NthDU@__OHReJ!5|- z?xh#}+c4jkxnMiy{BorK&z1b27xBM9;(w+8Pkd)-Vt<+ali45H`K96j!8?!OUmj&I zkeUOO=R6wxYw#+uf{F)-VU-uq_p6*=VgQN71+VnWDoz*w%p*47fBK!8yh6TL6GLcX z0KvY-4hT0uJC!Z<1ntk)`hU5f2J@mDsOA{eHQfJ0{8y^}pFOmVqW{6#ANzf!{wKYE z&P!-7sfW^k+;)}!tNx$5_+R{gbCdCE2B?vtAMwxGKV3Uwq!&h7c1O5*YwnrQflt5Y zaPP^!9fOt_)J2E;=jy|{XLE$QcE*IE-QhT(BMc3DVqm~z_$=f65#wFE<<#F{EzgPk zzT@&M+yQunGk1?Um&={m$3An1-UkG5CO;tUBmC*_0}hdABd702e$ThAhsT5c@1n(_ z=5opHNiE-LBem`5`_03SpkdcB>``(-SVSTYoh-w_GlJjC*mL+Qu}^L<^A2_&y@{}x zeK6=h3H6%{LqFpfOkZ*w>yK3+k$m8?qZerdDzGc_A?7C*V!)7PXwY~PwJ0Oec1R3- zmo3EXxS?=u-2j_C4dK;;`0v^T!wkO1RO@C4wrC5ZLFU{ylmt)CD2eW-xG6LZPcz^(S7)~ zFEz;eQTJiMG@fWQ?;p;+>$9+!J=yD*Z9vG_dFW*xiZ+9z(AIthT6=D$j_g+E`>E(O zDh;}!)RU){bdS*eFbF*e{jurjIqop}O-Q4zbUOY1F=GD&jH5GP!nog*$9yJnA48uX zlTF_*=ZwO>6)|tcnx7mS*8FYbxr?61f%tb2|4+Qz68EAFVo&XVN6rYk@^~}vT9CX6 zcclI&I3;#o5ck6Q*RY{Ur{N z9z!kul@2JeugwF5$JfO3n%qI|C%BjRU-Ev%M@X!p_4##ig4iI{Kg7>1_ZB;$$w@Sy zSLXlUu>bF77Wu!#J+K%2llxcwzsv!||2r=x{@D*L`0q(hR@X;U|7*=1K;r*}{}-KA ziT@jQXAe+Udvs*qze}IC@aW!*yW5+fTZd-QZQd9ibiTvDE)6lvq9evS7{J4{8+z)t zfq{`F>?UtuzaHoGmQ#;|n0MiHySZ1GJ?@@c$?a`s50ChJ;@*}yv*nuGW6=e4wqD5DL??2K6Opq1Br;Ez zlheC|-G{DHbL1-082ca1e1PSN`LLwcVS^^#Xx}LaW5Tv$!-=a%&bfkxCoW^z$qKAL zdmoE;ai(zCCbVo5f^X`NL=*if=re8&duf+(*1!wiqgukDV|}cppZ76n0B7oNdUS4t z0sP(jy9L92$!Yiy_b$}nb7ilh8+-6vH&&73q&7Hfk|S8ZlRX+?`M+Xp&aVhO`31g* z$mbvW75)c)qjuk8?mSOH!%oiJfzbmd4n_zW--B9+z47wOP@pyh|EdnfAJ1pvRmBqA zD>{gpTUB^=_YSJgUBbO{=Wy@Rc~o93!NVJ6xPSc;t`;(OKd=ug=FGvGMe#U&Xdm`( zO`#TP0_M+K0&h?5)os;H#RA{fYlp@y2U2&B{og%?QJZx%n)a2vFakOQCZQ2+g0AT# z=(AU>dGE1|59XkyT_Re$tU)`Ujp*plxN5{c=#JWlZc=9q-V6QFtTm2J;~ZZa3@02y z--(Ag=XHeGKaRn~zG}wzG7v{sh4*we8PfZ}= z-(e2%&$z*xTwomMf9^2;f5EvtVKq|HBmiS@&c7FZDl7|4$w{K*mbc zO6osu8~bdNSpTPn7wi9mf8qa{k^gH({5S0-{6BSY-iZhG^8N5gOI6 z4}-Ri;9%4W{#IS#YTOA%U0R_tdxZPBgpq5cmfdn{>@Mc+cs{yvpO&*=pYuAt>`5O@ zE?+dZM;_ulK2Km8`}XMbWxs~>@Q7ZO$F6F)P-EMb*mq(5&XN7FHXH79R~50(-d&ef zRTwmSD;l=v9=}drm_21ZveVBahg{wP@_RcEUBsS)HyHch!Lm)I+;z7OZQA;xMLTCW z4V#PQn{%)wy9z5ZFJWQk1tjEGA@S5x_Ty)uqro(MQ!j{h&#^EVnm|n{62>=XVSf>>vA$x}zW9zPb{91JpPDfI{dokwJei3nR}%5K`~==SVtuaa2`bKAz|$L7P*YJ(zh8p;*RJCspWiG! zk4w4PxLTBlbEh(JVAFc6T^xs98`dH-Ee$(1Y^EN`a_W>Y7BCuuZzUG^rWL-|G2s7$ zA=Kh>N0Xj`Xxujh4f;_d$|xN5`b@I3W z?||Nj9WV&ytm#;4$A=z3pYiMgm`J}LeuNtF>;sx|LSf&Geqa2*8GXNs|H=KC#-4>4 z`TikwvA>4zr_W!IPu{N(Ht~EU_8k`$@Nb%2Kx`Ik|2)LL*Z~Kb4J1$KMgDKenq<}g zFW9Qd5u8c<&i;JyyTq?1-X{);Gpz=&*Z_I0zNRp*#XjwW_T~6*DC&w7*^P% z|9_J^TH#;I*=y>68sD!l&U=c@Q9ixy`!qH{I0?;Bc?b0eg?CgxhxZlRp*}Z%7ts+? zJA*iXTuuJ(ZILf-(aue*!@n!zf6@DKWu7G1m*d2lXsQ1h2>x0B>m>Dm@_)^x{%<@^ z?OD*_3~+<)-t7Nn{g3lM-+uQ!8aLqIdaFjTH*5p{A#LGg+!}`ZP0+SoE9g^q);e+{ z>vtDnvG@{fxd+T~c`3DcIIl;(&ubIsa}WK%{yom=kmm~|-bd_XuDhT79(&eBhbKU0 z^f;66v#pvwpIVz+o)G)b)V$je*70qqyJ^3ok~^Qy!oYG4nl-n8m(w`xO*uxb(PA7r zl!vr~1vqf%9OrZ^ux@)Pf`*gCy;phCUIYeMJEce_RJNe5c^X$ z7sAkKIW=cT-!Hp?rxmyH@ahdzlw8E!tJm@5W)*H;x`^_UVmvIrhVpZTxJDar^L#Om z?A(r}vu0ysLIO@5OvAy{R3tB7iI{1zaCc>YKx=*4fToOpI-p^TerVFk0ZsJ-pwlyi z^P$v*>=TJbte5B*$D*lu9A~zOf7c{*@Y#S)ew)x`XbQRpQY$`)duD=n!jQd1ea9a_ zzpz8-A90vEZ$~khzTc$I_nS%HProm*zwrKw`xoC&{96#m%J(zxw_U)xACFypA?%2K zIgX19RUd#Yv2R1%+mQ3OW!^8lKOERIRr*aW=Pulz@cl|d zgLo9YOCBH?6I)Qjn!n)m>mxCNU|){(_G@bis%|H-KkdP*vy3f-^W#x`z4Fx>{A>I- zZA1}c1Y%e_*QeHEb0>yr!8bztt>39TAa z=SHtNhFY|Thgn!(B4$H5%lH zMTu*%vG6Pwmy}>pW*(9<^0A@dDi)ojjT)DVMxA5uO#{w9wwR2;!&Xo~b0ceQ;jC8- zM*Q5y*gxlcY>NMudfhEC)KUkn>oq`^?t@@GX$}0h(ARHfukL2zKZTlMoL#U<;=E$g z1M+t@?9cy*@ciEpR?L3=Vsd;1%ymwE;EetUn2?+Krhx&m#`%XX+~q-CF(3EtSiGP& z<90jz=lux$_f0r`e=PodF&A%dEyCT)JMrXhIdlD&czov(s_)-M&D|>8zEq0x;`4Y^ zQHjS@mE-}=;m(!I3j5cJ3UIx+fOer2Wd&zZeEK9~flTb;y!MjWGq8WlMjYL@8!2nK zM=fqCCWb_E2cs2v!FJ>Z+TuIzYpma95c|Pg7#ng%tXCKs_KhS*IFoautQlG@Mr+3v zXyZn$c<*HHj93r7;VJa}JBa<==o7j};opck{{Y7QfBAm)AB*o7>}&CFK0BMdKQYg| z-kPyka)lDXtevt0q}i=Wr;h)kBglUTg3ZmAGOcrYbqWPJH)Tc^;-OkZIUCui`-lG z4!tf3#LvZkxOFNF6PEA#3;(Y4|567~^MB+49qIr3hi*d;`hQ*Of5`k_8ym*|s{c<@ z|JO0_r{0$*{l5c!VrTT})C6t=TEl|9JY75Ja2BW)^!mBNVft>kC6LpLD}xnzYMW)m z(-P|NEh^)TO%+BTd_!(udiANX$-F*bKhHkK`|M@$+rxdi^!MJ|*ssr-RyW4_j;!I^ zrf}{}X581xtULL>1Zwt&Y{Js;^;i>?hW>^lshKqti`VQ&;^sqGwe>JI?aAg&u`7sQ zQ3xyRC1~`WBXp@z7&&Q1oWRZ=8#1NoJUr&Mn|1LGb zxj%Cg>-dau%-7z;kmO3{`}fJ?zooYJM@%5k7hduOp(Ve;pYuDzPxEB{f$)>RV8)h{ z)JhG-M(4F7kc*U^MXnv7b)t z*ZKabn!Mk<&hMME=C61^`hDs3wWjaa*7~ei>$j%wvLXH*78bJRFZgHgG_f!K-f0g$Oe))ZZWzqkYo*oqk)ZzT==UUq!*NV;1 z;$Qoyu9f@c@DTs63V03k|6hpzyX61Er2mh;MQH%Wa}OY604MSQ!U4*T#Qx*9pu7Jn zbfNyQ%>QZdAF1+x9rpj|aNkM;=6#JiIiYE*?$GblnDze#Xwke08g(#$kOzv6aD}etaDB{4qyl9+&&ErDumVJb&U|e7H$ zvloe5G7+^j9VTu|(X8b#w5UG>?w-`w+{y1LIfLZvbSyY=9C2BviTwi1-E#y3{Maww znR^M_#-O!f3~YTDa4uy&l2UxJcDXJ#tZ0F4D>`CpoB`&Cb%n1pcajb;M1Q|ojHmJu zz&PJ$1N(T^Rj~HR7+-X`R^Or)7kl{9YA}&K`%y*g;Va^dKKJJZlH>O}$~m0_AK<<5 z1%IzXjGR}DqX$nRXZKiqD4&4e9=qZ1udMLrTMzvHV<3K`-~avj61=TS!PA==sD4nz zy8dgte)bfv?%%<)TNQYCsh#%okMQm zJgyedCKMOZ9^~WF*=$rW4>))F6mpLqL*dCxtc;7NK5-OwtY6PqU=OyeSx3E+#oVzx z1@_k7Xw#|(b&A<9(4-q0H8(`P*5<5DcrhMe&Ny%?T3gL0m$VdJeOIB!@C`5w-im%9 zJJE0aZuAe^Pp#KO7&ti{1E&!GQ;(DDXYM~egE_x&{-3?vxQ1r!x3l!~N@y&8H z{G0~+f`7>slrI-9Q2h8;e*P~TKpQ1rlWXNYxn3JPC{00mkM|P%Yvzmjn))9`I(Z zyzi@3z3ig5=c{z}Yw7P9>u({yx2c*wliC~PyY1KyV?k|wbI!ilCq3cc!e#VuT?P;T z`PjSh7_tviizmGpN%5JSqo=?1J;?f88lvZ>z`<)4TImjDtjc|YM#C{@*+wkOJB!)* zXV_173{$g?V>x60m8WwsEP4Z4>d$14z#P=;xfJ@o%MdU(7E4x+#@ZF8*pS$ofA>w1 zw7d&qr}l+Eb@~RH8e@=8w6dFS)E0GTPU6YF1pu*0};Vi%qk%*KDo>ETrv z{#`p8zub>Ub=g`xzmkFHx5`ml{T#JVpX24-M|f6s8#l{J$?=`Tqq1vwexrhZ{|2fU z>z6UVxl&YwJEg4sGX}VQneXN0qvX_SoIjO?3#ZSZlGjw8E1~Z%!nv$7xSXGdypxOv z$Pw<_mVy}(lQ^p!hhu3+$O}-%anWMTo;HiMKI-qAdT>9lE_51nV$G#DYlV(z$~>$2 zpebl)JCAzu%lLP-7QKSDaF25;{mdTpr|%ySegK0a#rGedu!oTKgVr%4R z?S$eM`27->2nQhZ%rEma{Qu9?|GIrD9AV3MlM~VC{|WwG#rN|_4&X%pKXAMj|J1p2 zoI{Ph8EDD*KMnqw{~P$Q_lNZ-T}yo1OpiT)-KfVp8RpXtb2gh?&8%E%vgGmqcRu6( zbKHq>1%9k;4d*PLKmENwYj?iv)AQLWJ9H$*XDyHYE?(TDNZ(C*vEPc={{O@Q+)J zv5O}nF2R}_eZ7&qtTRsw#LaAru|xa9c%T{ec*5Zpdl>$#+k0=mPn*R(dYid3mpw-= z;`7%q&);|lBaXbnw8Bq_J^vXo7sT)Xir}oza8LgX%k{O4lP<$|5o_h9x@cUlIX~|} zv});%P95fB=eo=I{NN6K{Q-P>l7dG$n^@D!z`Mr}@vgdtxUWXdgU5J5KJU@xOZ4>@ z@Vx3Ks#){9M(mas+(*%jh4I0n6Ev1LP3@CzM9@qpT3T&h}%CjyXDU_NtKhB8IfF zzvTO36C{^b?}>jEj1k{|<^6KKR*+f)V+y&J*Qmpvt#D7fCH6{Yf7WsY|ARs`{6GDF1AP}X;G9V3 z!GYYjo&eM6{V?MGi@`A`m}{{|V_rUnEGnVDzK)^WA0vo6GQH>n-O1zmZsQ&ba`+zG z$mP@5yHjV+gU6k2iy1-uvw9eUegZ<|hC>;Ue>p?@~EMd;FboQ>TN-`q5~WFN$c3)z@( z=`?~iti%9&a@IOQtm#eU9PDg#cUc7Im>JY9@WZ^h1F>RJH>~7YJXa6Vp}pW_IfVU# z!(lU?b8zbz=WnC0-+Y_bWM2FckXpPi2|;q(uj(c_#RdHtAkpSVAl znp`K?zq9Wrn5?O$oxH?-)#DL1Umq!JEYO@>U#sR_(4nm{3_DXxu*WiNoX6O`qy|sU z-Nd_x)%f)02Xgz*QFHG${rycmxKhg6UMXJQxsIBf*YW&X8LBQ6;_;2EjQ20#T5c|? zii-KQ3fv^`S56;)x9kckX)DT^3rJr;SynD`4<1Irkz=@;a~2ng`{K+D#sH^iD|r3Y ztEecwh=RJOT(c`glVFC{9*u~gr71pd;hgq?S_^vs3lW`8BaZi8f3=Bg{ z+gNHzC9tNnhS=YN-eY&6@3_4%;*7Dx{ezkJo6z^0)M4L@zW;0NE8G+Ns;`eRzt{l9 z^Ur5(kN9`sv0}_=y|{$^(vLB<`0to``M;Qak$n`G{(%X^|9Cz|U;htgmH&s@Fg4_& z3owM-g7=(b*tmH$p5}z&hg{C+XLH_;IDRQUULJWQ=GXXqjsI8g3*LB**aEHJ*XH{Q z`-(^4_c9L9=n6~jFEx4IS90<^*8g~oHV07g1TijlK*a!c_x?H#(dHELb5xF?Y=OiC zv>}Z3wKhlWkG2LN*VfXOy(tm?U-th_MmXbt2l{`_4xrQI0r*_}Kl^~3>Hh~X|5y7z z-4`nT-`2!`bM9DZI*@%H{es#1$NZl$Kv&o4=r=l<{V&Yr!uMg&RK{*GJvuveE@cOmAbPv3#_ZnkUgGuEe$EwOJM zSAm}Xd(p>a2I8XEBInRq6rH+&yo1Hqzbc1%BI(r4JB8E~?hu_4fiZ5w;K4l{Mm-&2 zH!=VbM^X@U>Hu6$(JqvvBB<;r`-SPvE%{*w{kvbBV+wdcR5>pm)e1Ms3CHPHtQby zd!A!FInBxW%=1e=V0z)Nn9g3l@NCZN9cHY*{bycR%^ax+Jp#gEI;K4}(2Y1Jvk=D* z?LuUDBy6o*&|1e32A%vdsKYFbF+G4R&H?>=pZa-^ZlUIW1?&2ycy_ZC)m3GvxqTTm zRaf!!S}AUq_rJ>+;6CetrDx94 zP88tI`Ah5v7Mhs9)LTZ*iRXi^B%LV{1?%s|H9-8|HPEi zKQWoHf5fH#s57bbp9sJF4@BJn?ZV&TvWI?Wc@9!`CUDQk1iWR8UCmhe8U3Z~`;eVK zYJYB#;QoJgKJmHYvjt-szb!tU{+5{4+5(yF7d)zXfH8sE6HH&M?E@73KgInKyUK6# zTCo9t-Iw=!!J}%4b+$<5A`*iLrnUZF@&dVsI{bcqCOKjsRG+cjLq0q8KSckxDl-b< z%lE38C@ue|!M~;-(3$nnfgvfx{|a<=i$^>ASpFMMC5J2eADa1}1_r~>&MF*TJ?EiY zz)JRRZbkn|jN_&rqXttB$<$WrtygyNr^2Ry#`CeoV;5?pOD?#<0Qrx**$Xxy`Il%L{S9X!LesX+;g}6~n z|9>_gw}|nF>;ZaIUZHHjmApcf<>cXdz9#R#N`CO3a0l!kxN)HjmuNpK$SXX)a}yT} za&h&1A?~t&FzeW1EQpy+?Xn1RqZ`o2*ar>um>>2DMjP`;bY}mE-q2O(H8O>}n|CPe z8%^AYf#C-s*dHu8f1TeqXUso@e4i!reQnGyxxOXyb?NK3Cf0>pl9*rcFFJpA%Stfz z&P@D1phVcpLt0RUCZMO_5`bff7!3A^~>_8eXhlz)ab=7h#gRz z0Iyelz~cYO{mYz@7WaaEd9Gdy2fBV&B43EdUN^;*f;mSE)=j5Aee&3PL9oh5ZL@vyM zIu=%|Z^AZ_bM7nYH&@+aj87fA73|@mwvG9$%hcmoi*AO~5jAB!4j(;1v0;=Eg8JGl7N05cDylj=AR|@=wfT z);*<0Aa%i0s0+H|0cQ*!GM2ebT`uYqFlHIfIi!d)zq5Du4^_(#FZhHA=KNz${RWTy zA7Hcd9qdwmVr*1~_N=MYH!y=u@L*(=Ov2^!EAipYBmDX2ANb+@&-n7kzp0~Di-RXP zo3?m4^m-YyM`;lID8n(Jwej|MqmH< z?j1b5bqnYC-r3_Bcu>iBgZV}o^M}h9N^pn0gtu8!C}Uo6h4Dq)tl8`n_D8E8wrIqi ze60pYpo`NSa;OQ!{wDMuEq;Fw>wL`lh3~7wzBb>Nm|u&1tJ&=7pRJkaRa_tayEXHA z>FslnTwmt<$@>l4Lfuu?Ph;rwC)3wQ5bts(ou}_V&p6=xzc7V4Konzuu#5l3_|pGi z;+20Qvhq(Xzx@hF$}@1iXaQc(*S{_zN5+|+=fv={T>5_c?`mSUny+6M6Nj_`f??6< zm;7B@|9?{~eS#7XX#BF43y_>#@^7upklx}KxwIpKe>sBxx;$Qsc@_5)=ZX*D<1=2T zb`h)VH1BI|gt9?m6Y7qVKlzt9VnCnFgDQ~#5>zwrO=ivJV- zUxR-q=KuZ2Zib$p^!{;=uMPS0AyYZ~6OLvBng1Ix{x=+sHn!2!YU1vzk?T35nWk*C z(^Agyk>|IOvy3%A_Wam#uE&af{?>f7p?;6e^6Ri(ehrpNl+>(W~ zwb_UtwF*l|&Z5TFSd5Na1<%y|)QXsg_JiHfwv`jQHT59=N1%1@x#;FaUC9YcVHg^M zULFoGw6K8TU=!%|F-F_IPUvJC1GC8|8Lx0ZHn|Bma+1E>qv=b&Lh=^b^X3t9~FK}Rz~#3VSQpgbA{ z=N99Km-q1B-~WxzA3x*$r_cE5=il(_Z-3%7wQ^48oJaDu-P8yRW}j7m7<3+mUTvdc z(s~Ah2PR_2^mN?G%tJMK%;&|&QFAU6ug(|X%~gp9F5%JD^Q`NgWh{_~+sxzdm6oE4 zoWNE3`kN&e*fVg0{+;mxYXxQG{Hw~YGPiiZxSxC1EvFH8-zZNA~GF#4V;)kf9@cfjt#Z+{tb>^t_jm?^}mnqqi~NC)cm)eDwRQ z^BG5x@1x%r+?xs4Cv`sZed703zR&(%ndi5q-&cotw_%TuC1U`Oq-%)Ce23W5|6+P6 zeLiFT2%d1_Je+tBKhGWJjPED$b@X}qe?E>c`xnBm{R0V=A91{_3^$5a;r`iBVq7#k zh(}_iwupEymOXv+rS!iqnfKSwCcNU;1^<$ZYw%4UEMF6UFR?g};9Tl|+UE+Nd{6m( zxsK~&13veTx)Cq??>!v?SQrxAdiw?$fFk1DmEbI<+T?3@~DkBG>^nR-}|8s z|1X%&ye_KS|8Y7B5#;{`|E@f4^#3k=bo>hcMj^z%k4F1jYX7ac|4a7&G#wbC^nV2Z zt!$>UpK~eaH1@%S|9&PhqQ{br!LxIy$x1!e`9&~YPz)0}aV0Q~FM-*@3owhnsE*~L zOE6nd#v0*uSj1mr9ry^^4v2!0NeJf0ZN!m-$8d&z|Im&@NQ~Wqxl!A(eOn>5Ca%)w zpW(i&El6Cv6Gsl@V_y>c_BgMzWl0*gtz3&?j_$Awh^9WpWYlMmdb_qZ=+xRBx&~qB zYPphsm+PPxJQD`~!_dpp9K8owL)XX_&Gjw0zc>_z%uTHlsVmBO-Zh0>ehM{#$y0c- zSJ#R21@=5%snwiG{+WBTe#68(#*=x!vu~ff_0Cdz^W-0JIQ$W|yFbBV^<(s&uovyj z#-J^C_IZsp#4-NcT)q^E{Jf?3`Q1bO`Hz3%mk+<<{pa8Cjytq|`uGQadjA<8zWjk- zzWk1Yi&fZjG?)8;=AvsiBi0A3(Z53o1~iL;Rm)k_SXhLU@w-rSCX>5f((xqkBx)}e zQ0s$sfxO`3a$=R-eR*-Arq7QYz;*Kew~6DsSIdd{Yq-P5=XdU+=Fwx66_==3p!Pm{ zh{!WkUAx2?0mcSra&Vj2skl^xTh}h&`eo{+Y&i%^hcRfudT^`0qtRk;7}`-UzB{pR z$Q@F>$81Ay=KIp$qkKMj{=rk~@_p9$CFYmo;rm|{6Niig#2!4Qzn1u4@Gr5y$^rOXbiV}WVhiMG@vrs!f^osR z;`r+9mevMz3J|Sgm<0sr3F#;EX3{zk82h?COtp_A_|hM)q#)h5`B9o@2Q)D0DBea}Yh*$7#quu0B(^ ztAVpyMl-T_&QPm0m-a#Qn+wrDR`lD+eNV|lXPX6R&cC0aQPZ(wD}DaqY@FDiiOmVy zF>l%yBqg5032O74&b)-}TaFO>+pu}V3HI|}!iJT{uyX1yY)jaW?HhL^DS0vcJc;#Q z{n4^BwaFOox6$`Od)Ha$IC2H_y%%DDb0}*G_UK_^ieCM=Q@1<&7&*_}iMfpV3TlsR zWG#YtciKumGKHE!tV=j=dcatp_5H1nxi9=3#&Un=L~?pzoYfhZ%UQj=KgcosPM!U~ z!!zx7IHkU&Ud0)-Hw!^agTAoz?vHi5hvVw4891FihCXuz{{Ha?{QLKR;%8$2XJY^T zN8Ekc>^7sG8)?3C&ai#CyyT8~*u;3O55ZrxmC%6TNff+^^ z+yaE4!JQD?-Q696TOb680V9ksSb#uCHrd_#`Cg~GfZgZ+;(V&Rx~jUW`&_qNx12xh z{FNA6z5S?-ojKcrD%Z7)sq$Lx3>B?F`X*N7y>?bPO^6L^Jk-9PvxK?3(Z1Wdf?AJN z_A|Oa=G-Yu{NXx!{kmO7|6hmmyYlr(yLR@x-Mn_)e!g;z{GlJn7rbs~zW&-ykq>m^ zn{Vw0Z2og_{%5cg4sPCN-@qOGh~59=x2J5+_FdL7bdY7r*N`=zn^EK2$x<^<(pJXL zsy~|gaqPkl=za2g3ZnOmM!@sK^OYdRC$7(N{9wPFVtnX)_3{WJA5XYybhPX=kre31`!-T^1@SFb@C9o z4nyt6?#>=wu#JYr;-?m;$80(_%*TE6AKz}IWg({{W4*DKt>Hw=)_5}YUDK%V zq`rd~?}r^1(%XT0`p^}YKYXnf!k#V=xz!4H-D$hfkNt=pzjmu_ z*t8prud;!?CfkIuD{a*V;`=*J+SU~ZZ1T93HhT0@Tf1t%tt9q8ar9!FGGdO+o-)_w zOrK#3zGRJY`dEFKr>K39r+}r)UBEJzYfa6`XnVi;C`;RfwIVAtx8enBS>e2uEJyCL zmNIiyOI5Thy`||50hd=9?!Jcf{v3LP604{MXQ`aSI@5lzHsmaYZGXx-xxZV-z4YMP z`-nbVPgpnmxi#MS%<3(FV)f?Tv5Mq6XQ@5V-pgCY%GWDw<7R(sUter*=PtChUG$mw z{_+m{>(6KQ>^F4$qo?+S^(`O$g06r3)E+(l)qeTqH|zpr!sADF_ZQa6y?@`HJpCvA zGoRVEBj4HV)!Qw&dw0v3Gq0shk<~uTRK{|rqp!(7KDI`=n%LqVpW3xm%cv>eY|#gH z+IQqST|aq*Sm8J10#FMKwr^azVmGc{vs*v>h_8PQ+y07u2Os$D*|Y4wYG=t0IIv^4 z9p87v@q<5|SH0j_JAU-24H-5OJFSJi`$2t6UATj#EfY$Nm>i#)L+KexjKB3%bi-UP zw+~;xXyhu#@%#3E>E6Wmu=mUNBOf0>U%ZiWdV}!m#qU>w->=BF3^_VAh~A0f z8u=Rk^6Y$Y9}ezi^G9f44`|31=mPf_g1!$q{g?H>_@~V~cip}|OpfF}^83MQ{6Xq% z(fJN@U{^Z-XLNq_ffo4w$N=>)6-AKnaE;N#zq^=XZOXySwp*WgS(faBvO8@QhE zDGp9?Kko19ZKKt zewMaucS~73%-$=~$=;?1)LUTxt=vtp1?UHbO^~uYpI0DHwlaBw)rMGxTG$QssPm{l z!Ll}C4bhe}>G6atkbTf`IWgb0R-p9)%SgTWd+94$l85ias4< zmRb$sgsD?k!51x!jOu6^nu7g0LoHoJ=4ZjCmLCo<4|U6F)04Z%b9@LVT~6z1kb9&x zzbX-%tc=ZH6`rpuamgA}f1(D7bw##Pr?dMv*3@DhO>G(NdEIOqwmx}0)t5XW=6TgV zYCg%*6|ZSEo0YYBOIq03i*4-OrKa?{?PS-kuCTv;O*Z@IU-taTZ}#-p$M)>$V|f2x z?cp!r{#SJW6Ro5B2tVM7-Fy6tB|lNVrW|^z>BNjw%FM0iWi18I69$d5w@!$y0*Ds;Cu&?vw=aVB=4(tcvD{2V) zitSa@0QW)I0F{Ovw6;rckT3O@^~BGQkZtd9FYF8R@O(%9v@k{&u-}D!!hPqH9Dn}b zHsw;XZ9BEazTMvk{I#a{4gT;!bS>Ms!(f~d1MXtc$HG$LLG<`R`R`!(K!ERecE4xi z3&U~91BZRZ0yszZzv~CaXR-l=Kc}0)qF?tPhu)Vi7Vj4=p8|Xe$Kvc?#kSAu$>x7` z_;!JhZ(YlmCAx3nT%YNhy0#}D_zsTqWQy*!InS>6e*?QrpVJ6%QWc$Fm6(4udVp3R zN`GWzKoxqWDK9X88+tL?0p2Ory;xmX8)dGHV7 zyzg*K%F^8|RS>m8l?Gb6s+?aFSx}c=Zw+M^Od*axg`WQ1$*V78c?wpwA)k%0)hkxo zrnMWe-B+>hucM!{fNz*v%+RfTW()b2eWdi^5J*b%r7U|%o&qy)r$GH ze%U-*vwEQ|T(ru1_MB|(8w|G!S-!GtZ;eAg4YmArs5`CvDf+3sW&EhV<)DvUx{PJ) z-AuJDTkVln8lJNB{8%eRuCaOot5;Y>hSt!mGJ~81e0;4P7>a)Hyq7g~cK&MZcL@K_ ztmRhn^;bT#$_pM^=}AA^huFEff?C=q?JLt?psAfW^@*K0*^+Hfi@vqN{{08By}$lt zzdr-_PoCH_F#m+D#^Wd8|FL|3xIe~y>;TyU$q#<9yYPX@_yP&w|IP!};J)_@Ji%Y~ z!>tE4XXS1iLtM1a&?#1=bX7~2CaY!7kjDySENUfE=d<>us@cZTqwV|M8|~_W&2Rv_ zE&kdiVuYvZ1%?c``W?2&WxIOuqJ4Afq^k!!zHgu7_P!%O=n}mFzQ1(V4j$NV5xqXQ zY{eUq&)XE-cd@k8gJr1P$FdP4%GH<{F!p{veEmY57hAC|%ckHUmpCcKT6cTC)WS8k;C_wMIKfB9-a@ozN=z;!aqEJ z=wUehBXIiI`khaL|1*Et=NF&YQaH(P4i6)brxQId+EVxKVP8Cc9Qcn0H?jM{2VB0F z*W+Q?*Y&dfot)q}FW=A8;hvtCeg(&p2Yjxf{XX`5Tl|IoSNu&jIC4Poc-Q;GkNJaJ z9q;rupZPq1wtn89&jol`_P$Hs=j0Lh;PVOobtDUX8F3pQ$H^Y?e~SMfZfuu#ssHy9 zt3^#z4RBv;nDT&kx*loL|J8{L=4(C8ve5Sf zIy&_A?6vLxve&WKa@4oiay7L#^R>2r6b`m`i2b~c+<2F~;gl5yB1b;A%w@aLv!<%O zm$oqLdbhW!6Q|g!CChB_g2gtp?^uiIJkn;&*=qauoU|2lSCMc3l`WjR!8WX+hvd@j zwh&(6%Sq#H$=u1ddBsxOv}&GhSiH!lzyo$}Jlz^so?v4`=UK;+tPlInCd-p^wiT!l zWmyZ?vh+EE=tEGJ?=Fu#=w`*?C##Z=Tx#Y|R&4SQ^bGmQt*cdq9wJrf8CaKk!&da% z3EBIEx_mIdN9$;M{dj_Rki)a#vDIGj1Un&_niYD~)(W*|ood>erNQXd2>be2d;9j= zN%rXBd9y#CfcxLiNw1UCJ_CuFg(eM@U`{MS&eoxQO zFE4)2$Nm6%cY%4oc26}t6^QSZWnYbPCs@nqj`b%_*Mm5{;`ieCG?dF12B#+*AmYe> z;Q0TrVB|qZ+55!wx}X2cre2J(eP1uOANO}fFUrnGC*zAJA4VUG+XMf%!My9|&wgRp z=kWB}^XJ*jV*QW$c7ZzQ zaQy$;=zqokwFZ#-fGGA~4gFsOzaW3Bsg{BNH+6*`_8vX|74u7#zm=s@-XA&Lf6rRo z{x54)`@bwz?2T;Isg={Hg-uY;UZ*eEn|YhFj!qkTK;R=3>1e4x`h>pn`K(0Aa`pxN zC6_Fn&sc!3KEgh!)86`okFr(k4%mh*yY0&fGi>IB*|vG}KHIozhs~Wl*A~-*X331{ zwtmSR+p><&R?Vn%?G6{U6#D| z9$eWwIqQ<6wA#vJ=NG50Fb}U{yP|c#_OlNe*EwuzWy)v;2}C)G60+4K5{^Iz& z8v3)9EX%rU%;B$3FPqi z1@qYX>enUPUZdI|dh&pM;lBLP{nldP59CMxVg2Fjx`FG+6VEJ)+?^%J6&6#m8id-z8dAx|9s+3(8^T|=*(EO=p4h!0SGp`Kas zKd=AS`5j%T|6Azdf7NCCbv_9n02%v{Ck_BXRsx7V}Qus5&^-o-z7??ZY+Q74chV-c&@ z_+y(seW=ZvGl`n^{#K!KEh|7QYvh2jwtednr{5<{NA(QAKJ5Df3fG_`?p^n+HVgZ*fa3{>w^dIeh;t_JQ?r{GT}bQ z+{5?R_AWYKGT=Tk;V%0m2mFzA{}F5N{%T410`W=rup3xs_u+50ZXaCPi0Rhh^C{LS zY=C`K3VVy%tQ6P-soqLu8Q)FC8pY*p*4Uxc_AIxQzNghvXVzOz<^uyL4VA(z%LhU{=l$O}~?ZAEK1?2IO%NM?!JU(*xvGpDH zeO|vGHT(Ga>eu1%d*twe{mSrss^3>kug!@bu2V2uQNe^nd4k66HN%iC&SxFvsSa$;MLz^ z`)0bbmN|EEi|83@U3+w*w|sfl49;Y^v*opk!^hj&H9Ks|xcN4D@KoEsZmVsf59QP^ zhPys=>sQXT<;$iK51e7UR?oEAqsCb87Nab<(oAb!av?HcxlQgd&*n|(Wea9Cx0N#* zS<8mm={rOXYKG#LzHG1+?vL*|^Qx7`#xG88VR7;gON0Fi`0Q1{e+_zvHlp8dd$8{K zz3ueh+V;dcY4X1YkqM7v z12FC*7aoB9``8E%hy^MJ=wbgM*nfyG@Yszz;suZ!N%t9dS$~_gH52b}{?k9~+>deA zZ|VYTHfXFh8#tCVRk~T8ay2bw#$43qrnR^Kk_LExf@Qf{4Kt8b^Ccc&3Acx#lu%R+?U4BF9Y^n zOfQhfK8|?H zRsWa7eM$Gr{!akEjtjt6kj?M;?R=*`mkviB{MTT=0l9P8Tw{?_v+#B1W~ za4)fXUi|+g^8YUF3A4y?3$XuL{{Y>uzF)5I=P>#w;r~}gR_AUq9lpH3$Nj_WWrE{R zhhP7HX)D_s>C4-@S%VzzU&~mLzVqIAJ!1uXo!75ts>Hs^_BXb#vwbH+CFWjH`>+se z1bvX(-g-N|6(~^BrcIt~8#ZskCYWzi$IT^=oEjeL@g|SxZ!4D0vh}Oy+v?@m`>W>I zhG`R7Pj`q#)}Cy^71mhS>YJ@^$CcQ#D{aQ4LDpkrZR8#o<4m;g;Q0Ia0Lfrp_}55A zHc0nPnk=H)r$7 z_E9Tp_B+t8BZNFY^u6Qv$l(+2%k~KHd+_?o-&0IaxYuwsI{{wb@%+T}Tg)Mrf9N^+ z`T-vw%tsvi{l8>D)KOyhN7;Y;nT4HrP7NP&fttRN=YO@;XHSwdJ=$&@=s-@pbUxTe zcixfBe~7rcFmj{Or?fe|-;U*Yn=<2Rt4@I^MSf z+%+W^kOBUF^$(GLmkjXz|JYq{2)pt9#ToAOH~`52<>pHU2(zya=MMyY0oen-P2lN& z+4!;%kOSlnDSybXA9z(}$X{@B37*l(6Xydpa_fIz+8Jt*a7A^7Z1w8?hhXy$A?^zH zD~_Uudgvib|M4WtTnYcVBRp?c#~1Yej3x;p`^W@Uj1FbcxK^Uxc7!^ zf!C1>eiGVydlKF~!!dn}Yv8)n<+uVf`C@W?d`JuutpmV$IMsHlQ8(J_{Gy=o!6i$e31E ztP*)VrHLuj#_n%B*-8vMX(h*8vErjHlFxIQ^>eS0dwi7~nLYaB5BokT*>;`2V8^fAbn9QnC)~GRlJDE^*!;gE1AY_!@7}ei zjK|1aG%U)cQ_{To~QE@?mo2J$;1QivM&i)fDaIltVu{t zwm8<@jbmTJRi^l{(|1_Q><9uQLLdk4EfI59a&1SZewLjnX8VaEYQNz z6sJ$WIQ>d}EKBvl_)McLce6?K>Y!IoyIJ(+T?D^}t=|<}zuOA+;|(!(C7_TP9H4t?KWaeiUn`TT69>l2AF_}F*b z-S{Tb;k)oRkO5k6*!AZKTwmYeaE@FM?tT3)`@sK9_wLyo!Zy#vbsRqsuo-l$-s`yA z`-m}e9p(StcJV*-|IQHWI&Pk)|MB@<@6_Rk=%s&+TGi*)pIXVhUDw;+i}Zx&{fsr4 z=UbkZi!FQGr7pLxMCfA6S&cPcGE}v{r9uvX?>Do6dHev$fH&k5WUORw@cA2=%d!vb zr!9uA`M}a=&cJ%J&1}M?9`v&dw*h^lY{iW6Hto~Sws>+6TetiRTeWPAZCNwb7R_Q! zx;`VVJNZSCRp;B_`m1ehlX6VgJ{=_v{JwfjGeXjC<&N`TxSb z!@g{OMiQK#e0||Rf#J4XUvU6oJ^`5$eqkab)Sj!aH>nbZVO9Qh}>OlJk@xp)>C;OIq3b6vxWL}%%T>29zA;C_ri(i zN8;m?!>4#&$zIg!_92g_pYnKAtBb7h-{US1_;q9mVy-@ddho`%vQf5oi8oV=p|kEhqO-vor_}k2wvll9}HL=y-AZ z@rTighrD_{;Wrw6cZ;F=S>^C3pI0$(@qqeVy3)tHF#bZ92VVPrzb6+w9D24kb^~0$ za4((j^zbXMh5vZ@3DWmY25@g+FHX2;>qbCMs6NQA+1LFD#~PmP?)mL{FB#x}?q2h~ z;wGK!k&FnO^Ni%=r+KyIVw;zBT8f3!qZ$4uGkJlpr2^xr zOW^+p+3Q(k3wZYb8yTy>50(f2@&iiS-&16^niY#vquYi^t@MbK7BogRMHj6c`Mj#ltvxT=ny#j=_&U}RS#!^t ztzumfVtmc%t>0`FGJw%y|el9KGFm}q+f z)}NrmpWej>0N?Wa@8Z)xypQew5N;0{;c5MCr884^dms$x0zx2I?SW5!y+pVuU~@tu9A-5Bc|7jzI@A?=2Xutr z>jbyo@7!~nbLLw+c4#{B`p$L_TmLqkUJQ7bj~>tacyzmPt(e_ybiU)vg-u?^a!gFX z{+F%b+tN;-$`=P4P9ErZhGc>;=i|+j6P^#?xVV5#@FM@7a~ubt_#U`+Hh`{$Oz?RD z$%`1_pL=w&03RV9{p|Pud#TrTKMZ7n+)JcgSh}7hqlF)vQjb+V)}E)|Nl}NNZSg5O^L)@2cI;4`toVr2R&MMCV)^9nEsSHGz<6u8gr1_T4cvhD^%o{u{Y7^io*U6~ zxG{LwR`x+7)*EQNNIi&?t-+#XdiUL7%@M|eB>R~C^%ru?!e|?|JI>BTW1l~Hg0G)s zvH0Zo@cR?+11`iS*!okKZTrQm*Z~jhJiR$D-%hePc>V8h+_q$J`3ztFF~0m=Y=C62 zfA1cCzw&(8mwfMm-GeK@NO75+epZFMP*fUqmN9u$Y7gaDdnX;Quyy zKNeXK?d$*@muxqXH($ryw+>^MTZ%ID<0;*h+I-gJqOXJcJ7ujqjCkGzeEjL;etrd4 zKyN-5%U=fWSK;G>du;tOV81N=UCQ?LbiRE3AjR*f)2qP$;PQ5z41iO@)(_rH?<4B& z)PJiJcn<;Vs>$gLj~8<6-(a5H9dy3S-(efV{!r!l(5Gko`FpnY_*T1qU;w@{IXw9F z%9%@Ie&0JxY#-b!pU1Dw6?Ub!eZ4OHJKmf5FFmf$Ud591rEu)z2Iu;6z^muyTG-J} z|8lNAbNCKi%l8Z9x9fOcj>snPaqshiE?y}68#(0Zf9wEcg6sRUoA2csKE9po;l1wz z`1`eWvO%^yc7-?!J&(To)nmNZo-9-CaXh@=Esj;Mkwoq-9{qprV0W8;@*5j@=?@!v z`7i5pmc9sJzXzimwpJu^I`TB!3v!?@J=3}$c}~s#MavPg&T_CmTLIQ+FMuq_)sb}@ z>BU#5%Q`DgAE#WkKd0wYUHe-K)&tIf%};#bEx5vW(v`C}(-gNf)hCuS z)db7)j}=xqH@#%%&a;KX2HL=e1FU<6@fK2Sf<;vwYt#BJuo+*jw1q2{(QB#~v4Vcq zq+vIEFLfo$Td9jx?7PnO7A#Jm++stIS_yiL7H3;*@JTEB88ydKuChL80=;=|TD3{6 zv&R|%HRu~$hqXZK&SWhCIDlHSqUqVem>X+#=tEd{-fc#_eLO$G8q8-+?YRkjmSDB# zCbI580wbQ@J#kiJZoE|`&%ZWneupmo$<|)HV?To5pOfP4dh{(zd`#}{AI~l3$uD-| z#vR*q{wF(pJ=(5<;R`YGcKK$!{Rk%z3wG~64Al9!EqwnyIKI1&@a^IMu=(#Q#)qvC z$GHh$S@wRc?0kIw+sKt$VE!gPe=OI#ouK`Ejtq#w)`+=x*RBy8_;L$AQS;C34Yq+G8!tHk?hll*VBERTSRjV^}zqMIR53XasS$F1<@^)NJ9o{vP|M}q$lY=IE; zz3b71?K$MaBU?j0@;Q1U{J1~Vae9eh@%H}qmUO6{}$V<@Ta^wr+WkV_2q!{ zy)Og&Tp`CnA_HP}kq3Zp;Pn~Br>Fi$SeL&p>4}dmbG`%mavo=idfn-bu9ZkL#e}`YdKzsDc&BThxlwjA9+3MOJ&*F)KTS+QH8bThK6YKm4#2 z`uvC${rtGI`|Gh5aLaWM=pla7%8xuv58{(>n`h`la2_7;l2wHZs78;m>hvP2PJO?6 zi&vk1gWi4A{>+SVqZZp*lqkGJh)bb?)sO|mOD_i`a_`RaU^t{^q0KbRMFAwg$o;<|!!M@M&xmZ3qJnG9Q8=x++{0L(CeaYd80`H39 zN5bQGLGP;`PrdlM;PZ!o|1h@V_JW;{|ECSQ_?xTMIdb$X`)NOGagu`{&wLej;}600 zVfV)!f!BubyCIvOAs^t?*uAT-m4D5A_txO`;$Omy)8zp@>hvuBv6BIAD;eP7%9k0Q zT@Mar*S{hg^j^cyyK^{2R=iq==X`VS!L$A0|G~a;hF@?1;s@a7<^Q{UePoF*n}mPg zCvdhwz`oaOXW#pubA4yOuwVD?;omz(@<(#u_O5o=8stA>dno=8E&k(hJ?fl3wyV3_ z*})U@ZO*sRHs~8-uj-3}t=5&%4SrVJs5AIw$brttfv)sSi=wuo!Tg^rZ^RCG0kGeh zb(*R7Ex_7Mc{{B@4p8S6w#tgOnn4XrJNw)FB^U+l?evB1{mg~vXWQH|zc6WgmQ4$VyiWX3fwA7DS$6 zwJ}G~H4Neu<%S)if51`t2^^=V$T|9n{%lQGv4+6fd)9PWq6LBZa`*!pK^#-|bMpN^ zKWU|hA`?bQ7JNf&fcSv8LHdqWCN5Z&Jb@}x$qmqU+6`XcfIlEdXzESY7rfzyURR!i zT_L$aztYN*3)5q*>++kn|0=o{KJI$lEsMdvKlft{asc1p`M+%a#b{e|Imve4gd2pv zizSaQ#$j3<-aT@76yq2F2e$|A6ZKi*UAu+ek4B%z(f2bBJ%97|19ZN(FB-^Z`N1E=q5 za+8U*XC3pI_bjuf>nK{21xXfDV5l z0|K0Zj>HkFWci8^Izi;d7esvA6 ze#f&dJUPHIl7ZZdZ&!F6B)-o9^$_vSlMUf)e(p!dCm^#F2f4*>>Ea^TB{%uaVu=GJ z@;fFSY-~5#cYfbsTX%e~P59<6`BcAI>ojY&uXKE1Vb*Ie$hwUMS+}`BXVz{GrH6AE^}h55Oqs8N{r%nCmNpZ$xF1xt zG$|Tb<$~R?eG?aF=VETzt*5`) zuJ7pUbs^Ce)z@C#yzzw(X!#eJ6vEq$H$}V6Y>2Mh!ftCy`Pw1H*Uw<&3J5p zc;4Smu$$oi$M{6M5l>7Kf8dAP_igiuA1r+0X3N9x{#MYZ_HKFlDg+Io9&50@UxQvw z^fJp%t0&(>ggeRRI^c$BwWraS#NiRG8?MGsf_J@NWpJYPL|u=AC>6TrRV zMnS~#8_&LC-3~nSdT^o7T@Nm8(e>)Vqu71t6Y9l59{&;Qb&v;b@$uU+r+R<$)Rvz+ zXBQ7lpjIaw>~^p_#POUCJU~3}Ab#s1^xJ+{j&Xqe_dZbfcj_qazOiC&mYGliZA$Y?7LWFzz%S>g{~2>({+96U#;~g z+(+*M=K=gX-VeR&<3AQ3K=!}%zu)^;-z_ZbI_}=sO5eNR!95Gl?Q96yLy`xcOmMb` zdlpY_NS63R_!qYki;tu}VCn%L=QvO}6uw()6W_rXNZ8kiwNKjEp#$S=IW-_duRO9& z=ipzF0bz`&)4y9+k{H;1kw zHJt<12~#1GCf zzOgc2T()wfzjdSR7gwyzC~^k_qa3oL+{o{&LPI1pfb`vq?7N>p1)B;59piJ@9?ZJ-haEw0(ao z#%{#IBg7=w$?LK9%}@7<*w%!^gYHN2uJ6) zV-B@FL#+S8a~q8e+;(EU{dAy*-2v<3_wFzp26?YFIBtQ1SmtaDIxO}O`7`J(<@Y5Z z3u5H~dVz_S@V9#Xykl~<^>e`K>r{^;8J4P%g-vQK>Zm3V-d;9>X(e#__`jadUN z#P%FtYF}QuWxX!I$q<7H$36?^KU3et5cI!fKo{hl zO;#jw9dnCVVaRf~uSD1q%USnxD^ho~HE06wU#7A3YY}2&N6)rNGuE-r%^>oE8(7IY z9j$VwFRVtyVygwmyb_%8ac?SWIAxb~Da z{A>+9n5SFnI;>$%PeZM-own{I%TRkXdAsysXgJBTW9#REXDQH*n#f>!@5AwVF}+n* zy4zY8(^EXZjJSQ``Q`e*TyINx`jz1Hm9sD0R~>ekwH$6(k0XE3i%0yP^t>0lQ{Nqz zt1CMnoB+`_XF+w|*Gk>*e$Wz8n0z^CdU9AMuhdo}qgS+~3RZ^S)o}7&|*CaJ>HTH$&E%>;o^<0S8#WmIBdN2 z8$Qb_wj69Zf;wA;maOO1XQh>)k8t^(o8k92V{;1s*qomoM2FKqaM&rUIsGTr48y)( za*zI=aP)BY^}xCCUI(n#nFW{6s5Mi4I^ptWux==8300%cxBRFJ^d>xPh5PKaLOpj` z;T}7zV0Zc*X>?=ozEDr%iM{Dh*7uMV?SI&ce0tQ13_4+j2eNL=z|*`wV?`N72A;7( zgBh&NQJ7Jb*G1`NSVZIV^Hvl&QDn#kYdGo#!q32RDiVX@v{+xuMmEPV^~bNDhFxagFPUwh7a zOxtTEh|6beKHJhXnq}#mEwap?EVj(e=Udk1bC3bkEpwA8mi?0%mb3LtxSHAUw94aw z+sDsWUyjoJ&SmM#A#UGaXRmyJ&)yfW@A~dyzLCX#+kp$ z)nTY^RW&j7b0MGv^EV^WN!U@q1te?D~3DICr|+ zVchefoez%;ke{IAg&E<>w*`FcI4n6iAf4=E)We;QSKgslqZ5!79OJk@?CqDiL0(Ru zFHijM_TvhYMbg{)PA4Co{tw`t>-jvSk8$0j?#0V_)V+K1%{{k{CvN!a=dS)C;Fm~# zDo03iAXyyCo_cnjdgWt>C)+IQtHcW^9@vGRN?q{V!uUTUumvLU3zV1L8Mz;MmO2h{ zh{CrdTd96~EEim0(eMpcf>8ucphySuNoo(Uj$MY^&_Uy@Mc7EI96kd(VV;$VTw#^E zt6#)&^2(T#z41ZGCD$A*hrL+_{a>o~$*5SRWi8y%IirW#s{nMqdv;4#xL^`R(*5+-e1)w$Pt!qvd1d4c~0} zBDPxosO`vw-Bz&YJ}cDwkQL~C*b4MHYWe#dv;2LJGrqP0fsvQ}dHWsR%UPg#L}Cy-yqZRo~pw)*G=J8+E^9wC~Q$~v=ma+8;%hGzeWo)&S-iAvp zTkFM^m3+;tEoR#Xty#mV{aku7&Ud-HKJJyrHyrDUDzDXN8$UO?Ge~InV{ETSa|mA8P6Wk?EFA14*iA!dmtX3Forn6rQH#>@z^>W_f5QY zBL_PeKOmTV>=5|DaK#9i50VGs1v(?|`}0h%w^30;s;Uq3fK(gnU`hhWnG*#1l3Qt99&ara>=CkYfQaDK2AJ+_rW}R z-N(Gs?O?q+qiTS&uS`u}#fjg;*TL1{lWUY6{T=rB6?%>1%VVRL8%bRNaeyLy_E|pk z{zu`P?4vI0?IUs=@^n&O!y3!oX$^e~*%!9O#XItK-D7#9_FA59`|YFd2P_vOHzQAv zgS=*RV_&yJmb=?wwudce_ahv4)N=Ry+Ol>(VflIEkAt0 zhOatq&8K{gZF0`CbvSDUYRKJt}Zp3>O+ z<>cpM@B18|a4$VCo$qV_w$0~UwVsE5_xf&vap`>Z<8WL)Sl2o`;YS!pg?sElbU{aS zL-3h@!|(lW^Dlm5XO2!_9(RIsrY4sdeloo;Rqrmo%;{m-$C}r|4adZx+kJdH`x@M8 z9%~&dr~8F>j*%Sjd}Cn@n_K?3QvGIx985{su!w<^8(s%f~oeR_1kwN(b^6~)oBc8ErK)(9;#5{x^(?lOKk`ko=5y*PGx zu_0&ODES#RLW7QD=O3irVY}rD*=RXCtg~G0*U}q&HT_GM+XrAO2RY9s>ZnMEMwO^@`(a1=trCwdNL;p1mzBs?t+AuEHNn2>b+Gr# z!|?^tn?pRGZ2odCZ=c*9;U1ey*snA0q=jv{=ho4SAbv0Xu3o#^Du2iI+dPU65bhaa zT|8>S6jJ)_Cbp8YEnk{yHe|P2*^BA3<$b3o`e`5Ff*mj3 z+KcUbbJ&~5%yah{{;;p_#c2iRwVV5z!@O7SjeKU!XCIrA5s9uQ(8Ic~>m>_beJ#$w zVT$Yc`q96JFzxoeup>Nt`=9GxghL&teSXcZTLYN*o9ut}0!kpKQR{fR8eP6$`r79z z+`Z_TcozTr{}=!MdwqwKZTeomQ+B@V0}S?Gl@r3ZFO%H4f#YRkc(U-NjU&!75l%36 z5BKWgtk^62>e!Dw*O9NM+3fGWw?3Dj!4IPM$rtP_>|+OXh9BsRk07ov48Nc!F@jGo znFX)E38zHONW^mH-bVDhd`@~n_aSGfw|YiwCcku}mFUI#q3-it)FGa*I#a&0I^^tC znM8b^7=6&#E36xGnOGgXJbJwBh>KQcIK11?bL9S>{qOPF8FB&Tub;L;pPpjSuLqmF z;D8fWh*7Zr*H*CKF?hdY*!Ty~@w+W&@Fx49-CFGE)t0^0a?92dySn*e%Yv<)t(A1_ zDloSeyluc9-{eNN;LVn;;}*-R(Lt}b@%c9UAb5vm?X=ahA}_LrvhB3fvUJ{WSweSO z-X6OwKi`=xY@fZ`Vw3%&?g~rSV5MbgzS=T0Uu)?;*=QMBuEPdk9aC)ljND76R(#%O zwPk9v#xjBVtQ}YI{ouayYRgIdH5c35ipLUr$sa~uCN&yGBl(Sq<$G)Fs27)Ne*(Cd ze+Bj{4cKm#0(!q3{dOx3C4XG`&)wwmX{o7>0dPWPjK6#rMP zi$9NL>-({Ew~upP#ewqgbsgsubDod?m-9YgCpc^h^YX71vy$^N# zoO8kKi&|XWyX20};d!*xeQN7u0l0DXzq=cIJOI3*a+(zbP&`5UTCqqkmY`?!WS)MH z7qWzN^tp>mA{+I6@@Mq9!#3aLYW-i8W$bgZP@nP4x~{f5*Z=)JS*hQ};~U(1VLDI0 zANEK*-<6;^A+dschd;)4sbiP-bhi!Xw%RyyU82sy_YfxtL+(2pz;Oh;$5-q^eYg6t zDvx*&{$Pg%SLpw+nHp01Mxc92!84cWMUF`?{7*(1`oxv$y^VE5;F<@K%isTimH70q z75nsv73uGdV*QU%n{*6r?g+g-$ouQZ=z9d;T>klC%P(I&u+7)&faULX*zz&*_C(M3 zJdC}5$ns#X=aIc0wIAKP&vI)-?6nU=ci0CVH{(;UwM^6=WNNn5GB#Oc=^HI@BV$u| zw&uu$Ptd=uRx!Y%2FGVq(bMSfwwut^8<7p0(B+#fV~6dQrp-qD z`=$17odxz@-Nlxw(Q;?=r)$0f{IABhUu$VUS;<&t>08Rr$FG;Y&yddlAb5p+NX}(0 z@~<><6KBms{$)PuF$xlUDU3Z;EDG+W8?`#r>b>Z-AMo?#>zDbI-2H(&tP1+R5)}y&_w=!+lfC)w`~1!i7rtbRFmyleo`gkSzx;mbOX*myE4_`L z^}pM@zK46^-{bAP^JP!Rf`93M)c_~%LGBQvQ!LW&+a>uDFm41BNr6_2yvw+Prp zULCXlOzd~Rlb%oaqu;>$ef{g`y8rcC$zRg-^_<4lzB!hKc49AEY z&^l?!d+S>qKEe58!)?*G=jgNk6rB$@fE)-VHV_Uk*o_e>Zh&}!YB`lpIG8v=@BNSM z<0tA+kKDaW8{t6g+IUB@^szB*l8aza)s}-JdwM=Hv?P!LyfTAZu>zO@Xp8< zvfZ+F-e%c4p{v1v=Jp%VxvMQ57){r3k)>_0z|z%YU9x&}h_TH@SI@JIjTT#`rtoo1 zkrB<|;96j}vo>dXVc21Jg{A*wEw9nz@b~FkuD17Etgti zM6Ba?A?AW#RRo=1qB}KuJvUiterJDeE#Y1@y5jVL28rWW@BJNCZPZZ}z}xkJ zyK!q~2e^FUKOF3b9VP#Z5yI#qTmR&9$N5CS?~NvRY|H7LcIn_Cu+tuHA8wBro_g@b zA81e99?Y;WmVEbk=6w=#%jq`etZ@87|1v;$p!n%g9`?b|Q=olaOPQK`xfotfy^;%n9)8*!HE%!R`nVbd`}u9KKYTf&K3-0)`1j2<-Mt9cjzb8@AuuQ(NWP4I z3)uwj*@aQ@Bq2grlYjP7UuV*`j2 zgzdb;8i5DFv}}InDz<3JK0DF>JE$$)4*qvw8?a7Dw;gU2V_Ph0rxgLy1*3@D@xB1> z3wFbnW}7#XzQuf&H*$x46tVrq$P>2Ba))ijclXD3Z02p)%+xh@+G5!mvfneKn>EC< zWk82#Yz20kuLA!|EX~LB?fts*7_*73&$2Xih`ZI9gFKkW=L;>ZMkDasSU6q+Kfjc< zMvx2OI75>amRkC~-XijHzO;8MO|X_L4Z-o?V?Mj@b+XmKP-sEg5ai~7-{aha5z5=;C zise@#kH6xOz1C#zRf{_Ko8$MiriS#q;`x!nJlIz(uM4l0%P$!a0`|r6biwW$eDQZ% zOpW82eN)M4?Mx4z04Cw*62SXy^7hqhU%6;^$j?{<9~8Tn(V~zMc)7DL=d*R;2g?i7Z zbMzbN`GiN^gNA+^Paf&IzCL%yal9`d^Czz z7o$PWxaV=43irja3CNJ7{SEBK&ibq!-_O>aI%p$^^>jW*&L`YJ7`h|^{U62aNU-09 z*CAj3$K?|Cfg9+HjA*g+C&%*_?zY)szI0#K5X1*4+LJ*%z9{^B(XQL@8Fn(qSx1bR zT|r`V1-jCw7|a&{?|PjtVw-!P5A5d+-{#(b)MYFBaSQhG7W(sVu^eDMNAMPB<7e-< z5$+5>o^AFH8{yEg-(|N8_bt~r?5Br|O9vPKegkxNow=5>7TB)|_Ss5@r>r@{Qr7v( zQX&&lA{$bH=QQ;eU@t60uP@;BTx7-^OIdve`MBfl%`&6yy-MT3{}g+#201&DB_AVG z>dmsW^=Di9hO?0wb1f6t&(d-M9OPowPFZRnv|nZ)V)N(fOdkgP{JhxudGYbxno3~5 zDA+Gf{#8kMlv2I$q55odqnzxizVzf!Zw~Bx#Z#5fQ(3nD1bT&TyYFz{72eME;UG6x z^*ZXir5+sWwd?S%JU-sL*Bl!K&okj%yzM!*inx4tyN_PK1J=dwCupvMGcT7`dPa68 zI!k&apkJNN3d}WM-wJb{4wgRC@x;l%jOLu?qT||`%dcWUbJd&A-qy`~uH)g;!>e#1 zKiQWLz8n!ZE?dBH{nD3#`;k1+Yw_@s5&G^IHhKW_x)<&9?Ee5R0@!>NU+#X`?|J}o z4}MQDuQv$#R5nQ9I_~*6Rf4>Yby-`j zl^voN>&$bPtnY=Va036v9`M#&?n)kU7(PH}-pjWPg-cQ&$A0((Jz3YS;aAtKL_hG} z9iO$=b}P{{fPL^^1pF5Q`vs%e(vP5E*DVh71+W7o1NAnCUPqT>1H6y^PFV|# zR|oIazO+;|zOHMgT=zPzYDglS0byzjZ@>`baM{Pbmx8>VwEnV6Aj^A4bzei4f z5B?9gUao93hSUG(clq|A!v0Zgd~n|x8=w=o?}TqR==?KVcKVE+IWUHt{SbR_sD13V1*_Jf=I4!hnw)-m4P=01J?Py4vGh9_47egbkp->V@zT=(JMi>~2( z^8h|x$QE6T=W-YW_v-hhygv=ScK$!tQ4ZjLT`PcXS1Z6ZJgn>YQe4l&ypsXwVo%0t zzh^H9my#Qj6Aq^wnvQywx@IT{_>B8=g%fe@6L2 z>MNe0I3epMtG__(-bQw1SA?xQyxzuKOmO)h9pM2&oh^VJ07nqTyz2@d7zt+_sW<^V z^FZo~y6t;xb*GX)*L@55gPZYTw_7oW)A`EJV+j8m1&{^G-^t&VT%Ewk7s*;2jJy$> zUEE%=`#fDXqc=Bs_z&KQ?q^8v!{cWomYxNUJri7B=9a7Q;o;ilzc+)AZvys(`}+9j zU_VV=bUHRb>Ke1`y(-|o$}~%jJ)e?ZC9>yJSDR+3s!X-_Don8V%9Dd%<_r5r@z3q8 zB7<2c?Q?s#EJt@uyydi{5L{kGY@=(i94E8y!ln0nr# zsL$0Jn!>vB_7ulgE}!%B)nA|C`tV=_gkb-7gx_z6KiBo#UpDc4obCO389jHqQj3qi zKTM6z0dSAcuR7Ccd`7?TmgCORGkzT3i`#iROMI8)gVVX-%K5C!J^7&C{FLsMzVmo) z4W5T`WI{FUpex&z= z70C_H{`c|ZpZ8*mU+#|MT*n6}7l(8Fe&8=<0N;)L@N9^e_|&<6+~2=nAAkO{O6E8j z%6ECssOJ<%>e&I_Rvd@-{QfiOy{@VE{xd56CtF8;kh9tJ`*9EMKJ*%nz(3!e%X2Aj zT5|Iazi|@3wemJ%_mkUjun|6A6MH}&@%O|yPwgLUOV1r77vK^2$8W?3P)`BX5p+>L zxbg;w6LckaC<-~ygPf6mRG^kX0V^B*)lgWg8lT3;Nry1 zVb7<=_fJ)O4muv)UK3kiI=}KXd#~aYOHpZxy<2gjy@hS>bbM*>U2-UEpA53M3xCQw ziGwUf@qzX(9`p&zt>|x#a9FppWTd z)>U2x?w5mmYOKkrE(rDuiQkvK5AKWi*y6BXvbU%6%b@dv`hoTS-kN$r`1<9D?^PX1 zuP6Au-r&3^vRM7L)O#b!;T|p@tcM;YR&#{B9X<=j22hWlFb(25LoPqI)u)fpJ7x$N zZ)^9MGj|TC|7JUEe(--7Kha^GdOiH@cvqL>=~5S`M^~sHuX1@DZe&}6Je;#`(D@r zU_jUPZD#kGyC2CH?=$}z+G@D|B%C9>x!00w0Xg7ue|Yb7zvPemExg}F&!g{kwft=T zdwOxdV9k9e=XiF#zTcAp9%rIw^7i@nqT}^i*Kog^uFrklW~*y>Jcze-T*&{(SSJIy zFaO@uXGPbM4HHXGG4)r|TI$Njj)4=52K!0addbuk-QM>x+&~LEcx1ZG{N@Mylzgyo z)e~ZSy8aNz0`Y>%ABw~uP~Z1R?9AS0%=*F=v|Mq^ic)j(5&Eu(`~dWQVR3#8*#wdS z&Ne^>6af1gKK}E8{k<$?Mx_dC2X_jSTp((>na<^~iv=mIE1(6B&>l{AX>soEZNy z%MAWA!rNtl$IA#$pRPW>JltPuu%Ak{f6cG3|MA-yDbe@uR+?mQ}Niw&@Miu7ajws(rs7o~VV?1I6pi8jnqSNVdP+;Nt!&P3KnoW@$I zUs9hp+j6#=ORvQRmZ$w<`teXdMPG&d#PkcXesU4{`q=x@`^CV2$zH@x`s{E+dcQoQ zQa}9r0lTpS@cGH-X*%m$>#^^d%j5C$cU-Sc?0exI1* z_+G^`mAmNbo}C@y$(}_1`&c-ESoj;Q#d41|$8PT;k9bcv+j3$Xd0|P`hx%gm69^}U z6h3$#k z{#G(T*!Rarp&P*e2FpdyB8{BjKPUZ5a*)sCVrvfnOD#*YCDhFj*MrN?1c#SiF~7QV zJ-!crKUFo^{gWMkm$It1lhOSXECu83vZL&+l0&fZ(eEYE{l)s)TZMbtTZMYCCSo__ zK~L62>|-g)4zg4ghR};+l%=mVj=&S~!VB!b zqW6nq^OpkqW%`mU$iZTjzpZnl(Q+ED;_1ATFzIZ-$8jg zxvBXi{Gt;yZ-gbu0{1#FfBgN9dvbV(r{_GMx6=I7yx08IYafGp?dC39UH8R%=Bj_J zI}e#7JNQ*xOJ?X8|2Y3#`6}8cJiox2t{phm|4jFw>%GLi_npF&*7x-Jy*TN6zDvjX z+@I_KUlw@&0KbRBzW01y98dU@o#N#Iy!y=kJviQizTdttq}W3AF3;}&f8k&Ep>w_a z=C%Ht!1tUj$Kjrj^Yr}w|MWuM2KL7)cWN*HJIBahiRIi_bU>{7HZipB zTJ*lg_=8ROpU5NF)6jmx20DI__5WC_?XwHNxIQBx;5-7{iw6kDFVH%#J*g9Swki5a z^(OtW2ST?!uu9`j+ehfWkGgJpq5pl`-^c!|Hoyz~ck%F_yAxaVe=hQQKWvZv--i4- z#s{jSA;&LEGh*&d7J~oz=;Zm9L9u=~`uA&o>2!YTYE$v?Ct0e>#PBMO!}cG?c07GH zzQC^^Y44PgkN+9^zdviHf&Btetc@IH?-cHeEzsRklycL<#rhiKEK9>F zPWNYTG0Q$|Gml!mg_f86{ru$c<|l_&How#R0qi?pAMDHK7w*fV@5}Y4Cki}Yseb7E z&ktCerMIll5qK4Gt;*eTem=i3uYGPmlDd59`w;v9)$;^n2LvlelW zvHKfgqctMmsDWMI+0ORk6U?C&uGa->i{S#JPX0z*fH*T;KriG$PhwGBzowTI{FCNy zPh>zZ=5vSjiB^6XbvM)%=8f>=zzggLYysy3sE#k-1LVaA$cz22k-H-~{sH_;|4Rl4 z|JgoSO6@GUzTiJ|!v!z#Ux(NnxjJcT!1tm1(^Q#gsVk4OR29df_s3XD#yjOk!TF7{ zck%7tE-}#FEZoc9p~um?1-jZh`NQqq!jYD;M0fgb_n{xpAawsQOJ9@v{EsJC)<#n- zThp1AqvafD_dDzdFLl^2K#Z>lIlRi{Q+!YUzHELCAN$hzT3fR;_^&(d8|rhP!tGOs zsozbpd|{s9a(J)-+&=b4;q!N?soi+{7GWd zxA%8&Hn!^WMh&fd?b)Oi{&?vvNj;TrCD(tGgjALo9Z(^UK{1pFemR zJrh<~LHehtkBZCVC6}*6PjP)&+#50KKpNe8I@s5bC){kh8z>>=CnT?>Y#KNvbkTg@ry z+!ynix$5aO5C7WlY*e=XF;0hR&U0V7hA{KO&fqwQj{wg7`R|^OYdf7E*uFSU_Pyph z=g2?M_v@ZCr2F0A9Qgn)$49&)_x~cE!7;vExV;lUVHdo)^eop@9pryKua7BRPxq?r zD?@tP8``hmlHZ@t|9|%#IM@5FUj83C*3b3RIrem)WtWY^2k^^@R*0ZFY4ej*d;jH6&)<#}noe0)@ z>&kppuhFimF(+QwjXB*DemWeEAehf0kO{H}q7MFU^}hU;{J%}$T(NL?fk^lO@Lxc= z0IJ<3{^v#r_z%?g3i}!#Qb*(Le`tDd#ay7#&FoPG9u_8IT% zr~5wp&DHiFdHj1gyXVsBK`;GKwf#SR(#I<9f0VZGH&$r(AI*9Q*GWH?`91eWJmDO^ zrG4%fSToH!N!IPZ@B@E2z4DqLO|NEd{uQ+E_VfEZ4(9dPpL<62^BvdI2QbEm4?T%E z%UJ)M+y3u#8RrOHzvZ&&yIbL%(;(lVwi3*MW5hI#tSyuZqbgN zfgj*V9=<8}J}_(8_zE}ia+{om&~?_;d~U7V>$`_H`nz3~CI|CN5>&9v!n zpvKlZm6=YQq&Umi(+|B)M}dpwHtv!_q@`uVdshxgp+&?mokI_#HzcRHN&_>O$R zJEx;w%=tXOaq09B#`_<-ru+P{{m1YPw#U{Qe%AD8`^@b>{#@4ekRE>yb9!&NVOsZR zcTMNr_+!p&@3y_p*T>G!U>@&`%{;^Y*TF}}^Vb;b*M1%1fo=abz5VvTpZ@%&&rM&y z?i}KJ#`dX?unZIEI$BH0qxIpys*tF_eie77XB4T)e2W^Z2gY<)!Yb&*fW2 zed1XD2iuy%wVdWO^d#UGWdW4YyzAM_6)Z@&|p76`>(j(|Ilts(C7Ye)iyux zm;pQ+w9A{c<>C=rY#iD*mE8`yrN?*LqWD_I|MDxl!q>Xiq^MUgNkx!RBini2VWX_t|H3oPhFwf9(q>_nhgr zYhOA2*_S^zz4d1I7P6m!xZm^R9TzyAc(%p`86)&u=}pE0juEnE;Ov`zJiX+TJ2~6) zidrN0FxJJF4`BXZ`+pc~03W)JvBP!r0X*Zw7=ZTw7{&mO!Tyh74B+Tr=ll%%07r7Z z&XIg;@bKq-0Qd9|pUhj2``=7_W|5?QR zwE49E_xhPl(@*}~8GLW&Rh+^9>glkj{SM#Rdh>L|alc30|88vm1Ji?FcKKqw&$$0* z=<`2X+h_0Ik0I87BH!8hwGV%Ldes)b&w}myeGT_lt*a|@D$Jdn zM*QzM-v-w5uH(9n_wLDX&gFO6tM!SSzc$^rZJ}8)-YU4t04k2533;6?j!QHt5wljpuEv+s?q7wz_O!uGc=kHVzNm`pu)=B-+==(YD#be$jZZb9( zUU(MR&A0Yvf#bWXPo4u;zS?#-%%;8M16}f9?xO?mKfurzNIPYWS0W#E%6r!o?IDKj z@1XGBc!9Bj{haZ-pIG2K#2+_a`=;rmx869t{`P;WxdG3W*}&L;3Mz@52Fw8Fy;Xtiv2%i*#Co0 z#1H5LX#bC89`LAN>pphs?N`@iSUZK}5a5c>Ux{^Dz<`#tjw(*x-DAH|s8(Jy}QbnHtnW3KPR z)596>tMfWe{S<5Y{~PC_uy>bzJdbBx?~_>1|3vph)9(KY>-RQ}^?R=%COQM2IHzCp z`dsVWJ$&L?;`}wdJDGNW9ei33&)l2!UcNuD<;#COeV6^Ye}~=g-%4VC&i-py%SYPf zxE*u)_WR?@l~?J-@b76uj0e0gKY?6Z2ipGq=4SkcIubi>y-ln-^l;dg@6^fGyQLlY zfBvpuM7K(B+Nb#3@hf8h^)>ok^Vvgp)nD_8vpA(b?WWSOg{qTh!pV4>?Q?MR8oMpO z09@O~@-O&=RqSuoIg*yEKVc#q-iL_$R#L(SYrLbwNGRzgB;y z?i*(Ck1ka{d5HgZ59BWTnA`CO)~z47i?zgiuVS9ysx{M9H+*z@?>5dO=De}`9;|bO z^k0p2jR)MTrVsFp*>xn>6P`(*;IylM#JOR&a|RDigqnFo9r?f*m9{t4eI z=`p|uF%S4))&ki6YyXJ>9{3{m;Lr!S|MTnvT*6%ad#3w5`(4wazjSfO|JeV%Y5xx? z`+wu~6OVI^-#OU-8N~gYrl0uPQ(5;%?Em;zPWS$W^QOa|{yO6RH}ma{w@=5s=zY|6 z>Gabt|9#f@e6+rsP;vjJjP=v!XDz>d{-3As|FjD?PitAfcOGN)uV4X98X@;&hH}L-@DoMeCpq@ z{TlOYy({~!xtygRP=5U!TSIrW_1H$)7cqO<<%Nx*AI9uCx7zlh`iSb6wgyaX*LlY1 za$wH?3!`ig{Fge{<8jgNYNy1@1bp?_I(mO>{7KiPo_k)7de~>@biCX7jS;j-XeSBJ zI@EXHA7B^cD__9XJ$=3I4;?43Yh~IG*yd3`?VI(Qs*JF~)BC`zaSPtp7=S)>*jMbX z##Ql2v2CB{UOeab;+*%sTb^68CAvM2Ak@2@_A~>GMDT{a>HXWPHzWYH8<-^YzR2+wPs?bDrOI`zOPb zHH?{_$hVu$W`FJnZ~xnA>-8UEE`MXK&A1C*@6(3Zv*-ERE9~EJ%x@=Se>+*Pv4g&V zb9#PD!?C}N{~6C$yN++@Gj_4x@(aqlW9jH!>4SA3>*${E`n|e{&Y+W}6Qiu{Yiz)N zY_;RV)}mv=)keymwH5xGgcJBxH}EZ=`?6K^*RpdTQUA63FF-4Pah0{ri@f4El733N zu&&TkK0uq)24frM;eMIGOMACZK|f$GeG1S1z6%?X2JK6ly{62naTsuVUSRMRTI^@7 zXkQgmK=X(l96Jbq#Ho4T{PRA~@?89@E@6%Qm5;pLUu(MOwjUhD%QC`9>biMeyiwlG ztF-mnzbWHKeKilXv1e>Wxx=5kfa$zpee03_qds8&HOKRP0?rxu(hJy6@RDf@ebx_t z?GDb8W?aBAK;r=70lzh|iSaGh5IQEHJ#4@qI0t?XdyCG;7o6~kmFZ`QrH?w{{cJ(?lFL408gh6@U*vK*B8k_jy z>-#8keSSNIxc{`w@pC>Bw*O0S*vfat_g9;5jN^Ey-^=TLx;>{~^ZK;=?$fvbe+pxx zj_aSk?cb+2f9-!xpS}6Y=^J0V0C{VB_;2R!_7eBkK0Vg#dmdlK0`}SQ1v~xD4$qWn zt=-|7>$+>c&oO^e?!DGnPWgql=koRFhqhPpMfc}=*EXpRp+D52j#!tv6njv|$`|lF zdMyl7<<%xFkFG?ww0~hFU&9!>ug=cw(>%r#6>AP%?R9YCK@3k`Q}lSz9u{7WQ|j`% zupw~@ZZo^q{&&&dukzk=Pj-KvJ@d@}6%E7gJ05S_kq>1+IRJHroXy!J!j{Bdb;S2 zebXy%CEg|W-^iL_#|73gN3fA}CeJo8cen}LIF&K5^{jC_%k@L7aed+Y?_gf=Pni36 zPpxO^FdyJv0QUdt1CD*^$5@N=k?APr0UyXbz>zQb&~)VUFQe`57~nq41Kfu`z#+r_ z52X)qDE9x8*#A#H`c>@rVXoi#{>PrheBXJ~p-+A7biZf+?sUZSes4O8F~0{h-v2Pp z=zSP#{2s-4-_J4L_Za#pp4IiU^!p!k-sh&{KlDw;@Bei=hw~B6y8eg6IINduO@!ak zF$OpTzt-n+xON}a;oTv2U+6u2=JYwyWrib{pFr?R<2m+74}txVrL|ezkq7ucZfE z=XRuBR%~y(jcr>l?eE+-WL!D+tDPoaUD03lKE9)D2)$A_)!&+PQ1AF}3eNejE!MrZ z>HAp{e(7k94YtopIpGLD@Om5&Sk+$l)v>>f4@y(=dM#Y2tNPC3N8b3x_)4!kSX&?t z=Bs)9@@0Mx$Th(2^Q7M-f0eoSOS?jDz%SqBT#fP={H`lFgr;1@uY45c5(@|;^!nbo zCH_DkBJPnv{8#AcxNyiqzd&B&%+>+r#Q!ULY+w~ySHW?IdyMb{``KUYw~B7P_Vv@J zZ@O;!-EIF+-zZzpcjnfVFX%q8XH=fSfBVNZ=Z&4L$41U&uHY3{{9t<4o4>*w;3qgU z+cUL#FVIoizjJ|{`}sin01tc->w#W)IqP&T9pixS<-0oXo(_K|dv=Kd4y6y!F@W~} z>ggvR^D1ngb$-nE{nQf~^M49sezg5ZJohcr17G+KVv6@o52o#Z_=z9sZz3?y?{`vu zmUFuv#o0a2cq?b_{`c?kO)d6DGLE5bZ`#tajo;0*y>~6-dhC7u)_N~dW{%q&?Y%|=yQ|t7I<88n5y$ttj_&;NJ8ON`) zdf>Zyq#WDUD2v>B9?xfO%cWE3Lg_R5q3tNk=t1ckvMwJm^kWg{3mcfFD`msZZGn%v zN>UfS3R9ccj=f*Z&6|(s$)_(-=dIHe8`Eyehj101I&2x=>nrj<`Ih71U3dIh=jYjg z6FhA@y|4K{=&Dv?u|AV!EZaT?{))49PSRey`adz0`5H%Q)KA^L zJ(o|NmgSlcMYld188n}keAF)qyJ+BEp2QFMEc_LwI2O+ePS>^I_rSMo88|)WKzVR# z--dlx+uyome^Y%boOs^zgTe=Ab%n74`#Q!^`>$cHkiCU}bJYp-3Enk*;O4KhZ}^Ao zB_tl8U+mtswb*{m8R`SDCHJzf$CgfIUi_>t(^%xj`$MxWT1fj?e_XLW`l|#$Gw#Yr4>Q&3SWjpLZ zIS&0&&m#BMIc=V{5ZT%G_)Z^C`N-RTM4w{Yz4RaENxP*jr;n??R9rlCGrI3J?YX|7 z{ttVO?gxJC+C1hhKQi=Oy{_c`H_B_%$zy(fOyCQzXaa_1=JxA-;)fppL!YneZ+`gQ z&z=Kd9ANmFs)xF?slpFh8f99+EaV@_9OmSVoZ{7)y{63-If!~hb z+{3)UF7^|C8y&56YMe8$dYyj2d4dhom$rUe_eKz_Vt+eFne!p>MHv=rf!Fz?nO&1w4kcwvQTX0Uk&{;0V?O-tYOn z9_Vo90`A9JfWwFZ4&nTcL+}B{|3CEv?EkUn;Rnts+rQ6KE+9_0a60mrFPe^i{yU}z z(e^)tx&DW*`2)Vm!TJ1r3*nJx{>k*Lciudme&s(+=h5b$fo*TX_8reSo%#LK_+IWN z{J^R7TTZ>W3ZH}xF%Si@ZCD_O^L*==`Do3Foo`qnk;knxLZ?XL4Tw%c{qzU{xU zmGaPj^sU&qcJF$=^yBM$JN&OcK60oSOTI2tug(HvZWr+ZK5Ki_FR0tIx+xdCHs68mHaKAqvEA0w zrJvA7w2zK8+WwP2KEN{G$0jrG=h`1_zt;t`e$qY_?U?0L#;g7@ zJU*Jn<6~;oA!$xE0b9q}wbin)AzxSq+hvUfu zPxIzn65o! z$C`ov#h3tR5E>_Yz5upg->5PUpby}g)%FQaA-?wNeBp+x|9N`KTW{tZF!lj4_WvN} z0*fy8S1E=GkHWClGerJQei!=D@94`7br_#@V!yW%P{rSz;PG7&~weXfRIk5MC*v!}x z{d~tzOLpA5FGE{ffsZTRvleA9wqG)^pFhU)>pJXRxutCujw$wAeRSI;?ThgM^R|q- zZLpmAJjYX=aEvi}E44aYXEqY`=d77fM#_*vGzrZ1omNO{Yd zUpP(E(k@f4*P@eacw%00Oj-L5HSPv~=XCU(=ZGUl`SPR4aD}mnrpdNix;y5mdVy_S zfiHgk7vEZAF!)q@HuyN_r?#vN?Q;qv<&r0SEL?@Hy^9~Z<2%ciU*KG+UCdRj(r(>_ z57>u}<{J{#Uu8^t#W5}7fj!q8H+}2Mmrq~WddYO@?O&f>bKCdZ2QUu!3gTtQ1C0Ud z+##+NPt(4x!NxW+w|+K$;H1xfZ+hgpU*J1Dm$M)6Befpjh~q!P*nQjo{hrI70QLkO z#@U~TJcDrne83^v{}V4@uI~b30c`)5UN_y3xc`Xb-irTt*Yx0Dd;j#%m*G>e|3{qm zsp$ome67BzbvARAr&G6WeD(9~>+|2edUXaDaetk)&l>)d=*OJET*qse`}pu}-zXE7w2&8*!+^|yYR&CX1fQo#*5&kdoKK5_8#Y3(M9dI*1|d7 z=UBe&(unm-2HJ4RK)W{nZQCf@CO$I`C>tlwLT1XGWZNNpV+jBIKB?OLp=Z(e=vMTt zbgt?q9vpg@JdIPwiN+xHtseBpbp`6J?P$tdr}bAKR(#249_y{xlsaPXfgAk{Zk<=1 zj~~E?(tZoedP-+w7sHl)R^xn()fxXA_j~qt)&S`TLThYFyi=z>ac+y=*MG0nTfT<- z%nQ%It8dmamhK+ob?hlzOU%8Ac>=#%@XMEd zV>*Jfw+?yX2N(zZ@N^_+bJw20<7oe%%UA$=g73qg;QO*B@IFsv55SYy*Z1V#p6<*3 zp2MHTI=^4Jh;@F%{V!&%&u@H?eMY}OJ&v=C*8VATdf2^t^XiOUaIu|V+P}|P!^gAr z_yX}>!`#LO&g8g=HTzfG^cT~&HouB~{co~X_k`*1SgW^+z1MfL@%?v_zRkPu&^CK6 zTkY4kO~Ced;~VPRdi>9PY`&>yToC(ruST`M@K*lUJL9imFKr{OlRk57uzYsex^_aj zsyjZUpUum~cDzQfVha^xpc@@ubh}ge%I9<&ynsLSOnkMGz?kc2`5yGVVRRd(?$+4& z7*9`rudS!uW~N=#ZnR5rtGwC?XIstt46!BcNc&%v2h6=C_SgQi9#C9%G7mVX-#Vnh zI&IrcslVcp;d6u^dlskIX8RDzj(&K@XyU7{Xt>^szx9S!1#j@D_V3pFwn^V5Z|u|m zWxvo@u*NmMzT`ZUi@H`mNq+bo-YZ-2Yk$OZ#`mipc|iMOKJjzhZ-;FcK1u!D#r(Bx z+AhWecB1F*J$Js!GYjOQIPdCwr z_WRWCi(Ah)`a0GRtfRku3VvV%w)rZ~9yQmV#uxl^7c#yB`S-lruuC-^LcWJ+*$9N#A%XCbv{lzZZj{FY}z?$2>|AW8y z_V_>bVgI)G+I_|S#{R}y#A`dRfd9m{mWxeUcgO7Qr^@cd51UDQD!xSvbz07Q>kNM? z{*q3fiKBCK)=_0fet8_fQFV{!i~GShVOqCo!CT<94lLun_L*~C=-97hCGOR}wCpl( zBn<1*kM0^VfU#O&yLYnuI@cQ41dg$8zlR65YvQR6qq{waMSF#BaG&8SXOix_;`!6J zzwlDdu)BD=eA{16=W~AbI?ov6{F}AddG!ZugK29k4q)7HJ$}Ld_Nmts3ouXclF#m) z9>toRdmYC)K+pdGK7g@6z5#My&ip?7IqVN|9Pp`cB<_DB|KBn_fbssrkAElM^LsDn zZhv9g_~-lU{5{Xz+kmaR|Nb;$`+P^!wtvG`=Jj(PT+)kIa(*9Q) zitYBhwv*VKwqg6@we7_J!`6|NWlF~4R(?odq|fIa&(y7MQ}sc;Pk(7^qipneQHQ#K zf5$h~88lS~bpp?_!qHAs&qD9DPx_FdQQI)7fAUP64(t<1YumYb%D%KSY`<*MXZ)Yr zy|jj3KBK$+aVwF#3zWi@I!C%dX4?ZkA+76FMOl&vd+P)&=uKwO_J6{Iph#plGk^|A87HN z&m*5)%ieiDmq&P7@eJ>}Z2S7E&$bGEF5RY`t8)feJKX2c`i&CCUaow>^v$bI#g;xj zy_<8a&%Bwom-cmi^$C~@AkOYJwDu9`gR^F6{dLoH0(QFg<{xt|$-hs>|Ngi52KOIw zX7>l!8_4+}^aBq6W%dR<lIk&*I$h7I3 z*~{$sezo=X>shbI{@k^WLE;xqWS-&#Z2n~W`5XAA@`bm5fBNHXH&1tLzKFK_q>A@z z&$u?unUtQnn=|z?k1Bt(dFS_Hqw$~i@tp_gbDPj7{J^vZH&XV_D>uh!Xi8ad~A+oSqO8QM_gEq%5p zo;%U@-To+etK)4O@CLZjVT!J62f=Hub7kA&#Cywowag+Wx7>IZSY=1xmgfzZwurXR z*nbtAYtI|_dUoD!V)9Cs89eKlYx!SXL(3db>#)Aqt~g3xU8N7-Q}pJUJf%$XB!6(v z^Y{wuvhLz7bh}0}KEN{K6n?dwhQC(#O3v*IBX6IDjwdM)&(W4p*M5k@*_Ogd<2Qk2&I&%!(6EE-MJmOXUe}`D$_AkDcwL@E`i&!JOk+Ff3 zv3KVRZI^q#jWvUg$7`?j#ZS5J-=`Dt1C9-BxcZ0FlU}!Zy2o+voep{Kdl}=qWV-(| z-ZmZiEY9;`Z{HD&_dks@x6kI=+2@YE2_mq!Q0kNQh^wZhnx_7k5QUYC4kcI)%G{HhFaUAtZ9zi9^~?bLQ_o_}60&r5F# zewRzxZnt|4u5}d7z}4o=qp#67)E+Zz$@xpyE0|Y3HTBqZ<6HHK7pe8#KJaLxhP{-X zP<~I1d8y<5UR>1o(sk}*(>XI!czG9F%5|6aL0X;%#C!*R(F4Z++bdF77L zf-n57*a6(+VUoP-`=u?5Tax}Dwwz~P#chG#f*@B?JdDGv1{-x6`tR4C&XI;LUwL`YWC(|d`K;K{kwy9lu#>htE zfQ_uZ@&8MCelj+FHfsof{f}3sV>qAlkY~SrdH}Y6_%q+c89tXy$J6GYMPK49;);!1 zSf?Wle95WUynTJYomu03_Vu|xh4lyPn4>(KHT!Sn9IpTI)hnjIy5@Xjti7{-hc>(R zu!$9x^d+bS->W zwvlh?mNt%mah-;13>UVd9%>gQ(~en%wba?ZFTLS;_M12#Rd!YFigwU?ARNaF7GnnL zpYYW|`_%X_?Z0RsZ~MEUTch5)uTP6UsmHa~hJ3Eo>9x7qlr)&63*9F^udylDpJKCl zUi+tbXTLS`0osKreShcwcCml0?EWg^f9756`}co%s4vrRF&`?QTyM40`2zD;mNe&` z@`z6;dv^X4Itq?HQlFy?DTZd?mYrsocOj&j;;+7x;l^KFg+E^M9RLh)wLJz1+olLVJO~ zN{py~(N3H{*v;C3T_pX$zAK+Qedmgkrz^L9VtV^6d#AJbHo*qg*{mnIe_%cKx!!em z^vPXAu#Q+@&2>MXUQP^uy88)e)1Uf=Yp4IrnBNo5`RsJsU+m}1UB(a@yK{Z6>-TC* z!5AN(;JTv?lv&Gl4e`cr(&n#Yzs~FL_~GPnGPNK4{{W5bwd1x+;XipmU7zBeBHQhx%gPlJodY}x|^XOjm)jnwAhsez9f(O=t zex%&o545iJxK52_U(J{c`-j|5?A(^;G`WVr{e^$e8HDa9xb2I-J$>rSo2LtzCpeWp z!AbN9)(o3>e!#W%r!gL|iF7hHe4@5&EWr5S@gM*0bT&4Bru_)oeA{@xnY(EViM;kD z9P@Xs|5WB4*3i#?`OU0Ly#0Sq@8lb4SKjcM>92|PrRDF4^VO;H0rv5?z?%`@)ELo~ ze8ZNuXoc~3+xpmk+W-6yAH7y0Z|imIeD$4y5qk-5y(Y>3x!$Qm(T|3y4Iu+{i)*zV z-j6z!L*!FB0exP5Zl3Z7=%G5MZlvCYjLBDdf!%u8$cwD zE3T#cJj?SM?=1Y(UHRQ~EaX9b3z$Pr%431|cyFHAyYJ%{yw?{LjJZz|hrwg{C68_Y z>wd`7_el#`@Gf!;4K=oJOhsKjlg0%dLtb~+Ip3S+x?6BZoz^EVslR*zI2O*do30nw zN!;(+Vb3SuPdvRFf3OFCtUuVp9Kn9hBVN7wxaqst)eW03nf~zR+olU{`A7QY|7ZGj z+UYf}CBzpvHt&8y&m3DzETA9o4DHkD4|qP2`*1g6RVm*{5kT)8b0Jm+3nM(^4WwBOdY z3oba$@rizjSGD1ze00C`LHq{3)^93XMDHSJ>t56wTY^7x-EKRFXVBW;(QP`5kMfo} z3OBCN!=fA5+IeV;-xGGVm$Ubcm-qr(U#2erwmzWjWQ_go8sq=_z{UM+fm!^7@39rH zbCcHu0BA;>-7kD4}74OAe|D(=u z?^PVVny>P3U;lmPJ^5O{8V7l1Op$(%?@e=m5P77m*XW|>GCKBSyqEeGxF`cp?H4c~ z@0>jLZ#+R<&dJvgt z=kW*D>wR!lm#aKBZ~M^uX56oCpSX2Ax>I;WwtXY0K9q@9m#S$FI0azBv%F3;@qq)#G!!m$228=SV^HowRI8HWakIlksqKJkIt zf#Z_uvGBddA6c$oYqRP(^|q}wE{>z@f-k-kzsfVp3oG#Q-16J?AF0Q3!XEvxIbYg0 zw(qh|Wtj1q9;4A0K-;kSrSHZ^m5&v-rX_Ggvw6Lj&wZ~BFkgc1R}7E*a*ly`M91g8 z$!F24y!ZPoyU}yU25Y<+JF@K*N8x0Bi|1gvrf4r`5bpWX@tiw!{Iu;$=TCon%XQP6 zw|$>)6S2phH326(XNVtIhYxVgP=7P?$6S9*T)-GV?>)2B{Y9O}bM_faU~J*S+rBsb z*-ckZU%TctwCyL-=D&<_2y~P&{+hpJet$RdiR+z=?OmT^Ki}jxcxpW_I#uf(9seia z_Av%tJXa=>yLPJ_MvDDKkJJsHl@BGae5iFo?<*E+nCk&<}u@sq7klCSTDqdl%j6a@Juv`v%K& zcm0fBOS`xztMd2SH1|GNzg^#2aV~aWbKb%Pmn8jydS8jSdKYU2JPTt#-z!e{-ueS>TWp?MTR{Bqy4!y^ zeRSJj(YLsK`p#8nAZy1a@ITnM-_W^>bN2Uf4rler!SlerJ^UNbT*EI-maFlRa_fh6#^(sD{0Z>OKMx%(edj**uMU|n_<7A(cH@KWN1n=8`k^g#oeTNH zuh^aM)HD53_~W(OG-&`{p4-M2?UY$@eGhyr*aLrkSLPJ<5_jw|1|Zq?yZ$G9Dq1NM zd-m$PvL)(U@=zb3&wy_Ff<5ohGE;t$tvCixVMNw*+pavplRV+AdClkhlq(+yk7854 zPg`%D9Tzokx{k)Bp^ zVW+O!$LDJk_yS`R{nH}x&UoMU-FCiW7~ZS5C2Pu+Z<8M*#V6Ogd7cS7zA@#(qlF%i z_9J}NhZ={M{M3VyMTQV2U$a{b;jOX{TB!_nVa`F@NY7GIURs>pJGN&UGjBM-RGx-LwS%=tRXd#W#3UIsosD zjYeIw*W+VZGw7F4`8B?Tq`fo2pkl z*DaE!~w1)-f!GM>~`zs*GzxJ_e+20wtwKP4%hAXTE3HL|4(LI-`M|D&h5Hr z8{gEr=_}K=Yu_?`=gLzGFVEv}-eHx#z31@kCGNM~uJv}q{;Q8-UEt#Qg|?sme))jG zGwBa4X~XM^FDn@Mu<{3k@8&J~2ak3${;eG3SNM?mnZ2WJQ!gXS>U+yel6;EX7BU8I zWD)tr|C9{Wb>&Vz$KFe)kZtL&Hoi!&*6?$w6zUp%_aRqvZm`sk%SfU9_bgLykGWhKt`mrU_FyT<*3*Ml~Fi1sfY zRSp`2Tm7ur^YAY|vusEFso!)U|2j5GneeXSNAxNFGjb{2E?QdGQitX1U3?Ze7Z16L zuRIE$mV8i$;2H97J@34AFT9yP2j^}B!P&6~#spSrgI5`&TXn3Cy@lU&o?z=G%-P>g zpZ@!Nhrs;;euv;+rVDQU{`8@n|C+rzm)AL5`#4i}KV$f-%EmxFti7&*lU*w+(g+nRY>_pICd=tg9v49i#Xu3`kr)>wixDMRXy z{A#V=khjm{qoTLLFW1sN^7}qGRT*WCPMH>XA%nnc*aJR!)hAQ%@S6XnC*WE(HEe!f zf5GH=`T(`>huD8F{fhDjz5})~Rq!kyU@XP+_#3Zx$74a$@L}>+9*3qSzWvPSp)tG( zkFDEe-O+=h4O)fc{T!e0Ks_$I88%h^oqEJK<31_3!XC%)%lb{N2VNUTWg;Er2Zkh!s)89;Q-1dX%J?z!_+zp?f?)cIL)19o} zS;6)_w_qRfnlalh+WTF6V{3(YB4=l7yW-aSA+`JV1HHzz{Z_4Y!xvOuaRR@RGxv)t zaN^@!<1laUq8k;ffscAy{a57yET6|VOBdjgS9xok>3w`i=~L;Pb|jD6o>Ir%eR;`D z+^mOW-6cO@7LB$q)Yt1uhCVLv>ao7)UCEmEFXR5vrN9l&(doP|U6Icu`&_}b#@MO* zfHNxi-(y$HHY~a%9_m@?$nb}OVI9)8h(TP-&j0$3&@BGf{*C{|L7gxD$>(AFB~RLQ%5B02QKM;28HrL|e z;8oFyj>+e05A_As0i5FJz{5IfP6(dX`9s<+=;O;H(6+f`bx9<0F z{+Mg>Zol@orfs);jO#^=?`_~cZM`wRt5SBB~Wbk<&d z`uMdkr|@c7Hh;m_^2ku!o8R~)^u+Rk(`|wBgw7#?_t>jSfTYuD>(qnnX^O8IF z%X~g>T}pe?^K;Y<+`K#RD$HJMSI>c0c8^?32dy7Dlr9ZfMK`T?@qCuA&NGF7b)GrT z(~h-2Yq<&|NjWEu>G)NfQ1;?7(?Ri|&lA0iJS9T1S+3=|AtwW`#XcWFWkLr;arL z``WaW4XQ(tE%v#f8N95!d5@p8k4C=m*0QGJC%lWkwQl;qWmYhRBW~)q{2B6Rk@AzuR2{JHBy6_kC*@%h{EIIw-4J)}nPfTn>^OovjIJ(dRVPUP zuS8jSv(P^%m6SV%64i0*x zWkC; zW*&69<|bzRlhXA!xB4Z`+`|X`&s;X*Hs?+LmzOIv^R9W!J+{At^+9_*LxZ@W`srM?1ATz&Y}8Bpb&lsq zqixm>ufVrYVKTnhjsDe|-S}PTUGhsC>b`L405GCYbG&;^L%08ttN9Cm%FBbkZ&`XD z-j)2Jz5LF=H8$2VwZAp^!PR^D6rZCG1Ye(-=JM1(EYExx{vDi@mGGmd-gmui?>_fC z9KkcPEqTuN;<(O5deS~E`PA@=AJpgb=0oQfUgH3d)eq|@RlBDC5}Rgj&*$cy`;;oH z9uI#I9*K9I=TmcY!0X*0CiTEgaReHqLx}s0PDY#Ugfm`rAfOg<3Q=iF;=!*Awo@dU<7=tt%`PBUOzxAoJ z;+J+*{EJ@RC$=uWiBHxNI2Pbs9DVhgFUJ0ReRk{9KI8vEtNd|~tacwCU_ZcdJ?%f^ zaGvMk`o!=+I;258^FF>Sc+0!sU3NFoF1gea-h}pDlXi zwd@ogENveBE?rSy`hDpl&%`x&F0O;G(jpG>CFQI)`djs=ALJL`$av9~V_&uRX7JZM zrtr&qVZ{FPetw=^!3NLTL$02JOO?a^jp6p%#~8lByh*8JnZNBTV^8LDyvhE6-vF$0 zzJXKc{n2h@4UltycM)IL9szhNuFbP0FBi`S9*y4;7v(?KS@|d3;j90bcnKeVt8XL8 zSMiNL%>Ng?+W2T|7d)g)Y(x0U&33=yv2pLWWb^S-uo+C}Z8G@IsvDDfqS=`d#%oz@^VMYx;#9_|_?o;?eOB`h`A-Pw~qu<;CAV zPTAeO40aq@Sg*LLYo#N_lOYpgf*rz=r^a;LZ$}U0L-RuTZU0rbJR0;x9>H6@QfF+g z;sVN;xB1X|U1i5}af?4GJjIW9;%e&k>e_D0Hg+lf;emCxEU_8spnUl_%0~}s9*lgE zr?wlstk)F%v8?yPPkm|sqmu{HpwI7mEtfoj8+g%`ycdRjT2t`{c}C~74f7dqF2=~f zUtU>m&MW`RE8ArAHqZ2VOFowDrO|w}xqbuGK0wU@@|_;n|J7M8@&!Dq-3JF@$%pEf z*k151_9_m+rLN$cK6hvi50~<2xs`qupKXiOxt2lblJ=sXI^qv1_O?vX+O#FVILWi( zzvEdY59?}QU|&caDlUNr>x_+ACUW-KLZ_vbzHe-OVfW#|pg;T%zWxtilxukJedIT% zPZ}EkWm_Aa)`mi3*^_i4qiP$`_4ap-bNd5vj=WkP>aJ&XIEN;!b6Gg95Lwke-QrQp zsQa~perT@#*r2Cq1((8)tN4qvKA?CDf73onvowTH{+B0S!;kPx9wo0jk?~^TtAo-V zd`mB>PakDGRD2#Z`YtJUAU*|0Jyzy3`BnWT2lP%H2AnEe`q6l4tCllGu7MTa7F_sM zeJ|9QF`wrMquah0c67(G-it@A&lzx9&%4glQMlR< zD7uXUfCJ2x&#|_YINLdatPQjt>s7WTkHHI{)tMHBN8{rA(6!{1K1dp*sdcpUl)BB+ zI@|AxFUUik2|tA?pXc#W(P8~F*&QGQ-bLPBFL+`5dn#^{r_uek``B0Y1ByQLfK%yQ z_s8Xb@f>>9)A(lju+MQc@;5&zH}2>B4qxUvZocHref!wACyn? zPhCwPDY5sW9R`m1GPlQTG$F@NwXTj5#8%cN^b!xG1$U!Dvc zQfK4eyt179(bwd@^oKH$nfaw9$>-r$!Gs6Z9$63XlxNDOOws8$J-S)zXRE!n&)a;I zw%jXY@8{zK;hXTPPJM_rK5+3`&xJo8oInT{)2bJ|M|?k>dYcf@u%fhbQj$A z<832tFU6bD4yaSrR@MLZS?W>gQpvCVZ^aDaH7jRXX%8OW%iG3RJ#3x!dG3RE#c1VE zq+h)Bi7l&wLmBzFl;hyL@@Ia#&Lo1q&}H5lj~s2UaX|S3`6Q3jFULdpAAUzpbNf%7 z%EJ4S5oOf7@Fe&C53L2a>S}u|JzbP(AL_Rt!wdN#ujYQyXTFc!3nMz{y*%3fdDdK# zFTTROdFT7SZ+#Va`6unI5AwI`Sl~N!Xs&mbOW9nDXTS-*DK9Rj@FDULp3g#Wc6f-g(k zPTiKb+@d~k%RBM0th};+B>wtTb*X)P!*8AT`4Yb6EHB)^*2h?1V$r3=G%(bCG(~|_9rawgR}S+kAV|8)_>;^ zDv4jiFS{S-fYtW^M*FWXkT>F1`~<#wmiQ-i28V{HxhmYraEPs%hz{B zA3TVzq@L)S|4aWyeR-e!>33B7H{eL0{c`=R?PvdQyE>qLZIjr&-wzySbj|ry6V2^<@+9_X+=-HmU~Hvl~U!!TfJ0=d@hW_-MR;^^?bbdK6aEc(rr>+y+*R@O1sr`x2~8^9TaxTdzDwNfoR{< zzHH$esZ-p$ooL?F7?W)Y{MYZQ7uMZ6)4obvlf*$jhu5JYd|3KKeE;a5)m#z2K|HD) zFcLTU-{&PKuA#NYE09s~Zj>`$zA>deRy(1O1fFRz_F=u$m*>hXJgArzy{NWo_B{1h z8R{wd!Q-+^*8`2Q$KYSGuwL-!dG@70RyOj{JmFjELfZ0CukU{jv{^0th5Ba6~S z{!4SGzOQ_>76RWq&H&znKiH?vSf2by;5*AJtHPE4xv%Hrz4EL6 zfIeX8jaPkN)jx2}+Ml8s8of%Be!~1!M!f{CveySG-|UkVx9~4|ZQaqM18`Mu3nsDw z|0Hcc`C1PehxP^X8s3n<{l2i5JYVrUQG*|r&76DcS3ku^8!X#}etBRj`t&pM1iU(4 z>bf$U(f0{97hVxZs~P0bKPJ;+XoRf38af z!}rYdciYguGW_s;)d^4KzxTz%!4KrDvwv$XkZnKv zTGVHKqB6t|;y-;>W51*Rzzu%BPqJLuz|a|edfIXGNQW|X4m>;+PIx42--ID>c=egrJPWO%r(^QA zk$PuaC{5^dhP+*cZ@eP{)v+^-6dC z=RPuiFIy6);eWKjZu?tS z>Xr1*@eYn&rM>l{Wnq5vdLBl_0rsiKv*@tAF8`~X^*h#S%xqjSF6 zi~3}uZ1A%Vd79*NW0yM5ocqvQJfM!82VCm{`L7R%PP9%DznKrdCuUJrk=qjAwvj5c z&?)U+pCg{j_tC|w$1zCq3omese=`_qZ>7`NQ+d>xxyXLMbA{lid>3^qmqDxa7R|u# zwgDRkwlE4O@+EKT4Zgn5y*MWM%<`odJeMAIt;gM#Wz@^It0nz`8Mt0e!WPZ~Fa9TQ z@#k)J1_$$`%p8`_=kH2}@M~3Eft@m;Dc6(>{N!`(tbO^M2O_=2N9#d`#e>4R`~ThV z?=$m4pMFPKC{KA^I_kaUkXgpG^Uk~reV0e3L04$C?)E{-r|YhDkgSI^1}U7`gDn1@ zW#40y;z3?*cj!v$%r(!&QTz43`WZh}`3COtNPJwc;Qe0hh5XL3)p>i+SG1~w*2gvb zMk?kg`){4~edlTU%Nzabf?i+;$Hf&o#4BaJmd?ZDJj)oK|C{f;qutd0%Lhm!`v6@Z zWL?$PQ@8m0EfVpNHm`FY)OZ5x_9{lbkph0XKgmsk14v&MZ{SHT{5SSEN&hwp@0 z?P1xLxDB5seU>A|wnI~{mW$u;nfZk2eXiCQoKmOn4)A&6Q1a<(%9MQ|;~Lwoc)95g z4Z<;5HqU~a&y|^H0K}emdj1c|J%Qq0{E}{P+>U=(VO+W1)p^E4Z^$Q1+Yj}!ZAx42 zWWAxwd-DdDJP(hH&&bvH^}PWbocT_ zZ4vyU^L?HoZM8n3#*I3^Ixep)YpS*bUd(x$cfxDi>AVGh@FaS&W7hWjY+knUZT?kX z33&2TzRL3@pE@Q>yOw%{CCqAjyvmm<+s`YXb&luqtlJv>Pupn6p4!q(x07F3-1nS) z^E`YsfAPk=Ll$21O!#v==l>VaiG3?>2S(B16?q6JDR`y)qAYpkmH&;QJ@02b_N}ee z_wIZS4#f}hmak!tqTfCBefhNkBm9XyDQoXTkMGSN{-><4i}&(wB=PKe;e~w;^r?Ij zbygeiK1tFp@La(U=qJkldB2PQp5Z189k3nN7LzJw=D9f9H}JVOTYX5bvEc=; z#8Vp7MfxHAOi`$2AmzwNF1qdgeUS$p7_7| zFZO4^vtkSJEu6_C{PBOue|-Y{pl_i47k@u{2H|nfB1NXr7nfKxpc#((ulj?gz z!X_E_@8BGi`mU0GsO+DsJm2ho@XoIB2vr|@BB`_aefk!J_j z_tplVivQe;kM$;g(f${{O}KVbi`RTxCNN`5{`cN~X|+k>0VDub>PXz>Rh;8j zYcH(6Kt2F3^>~%H^=(V;L$5m3bPxIm9!q%(pLc_=@f(&&9aX;ikdAjcPcI*v&*EP+ zjPjXNPmG&A!2dg0l60_&Z^hj{-0C zayhPsFQ)9APtrJ-S^Iu@Aa3X#IBciS85wB*mX}A(i>1u#zxWM)E$|iBAs=nuM zL(wRl_RIR_@|ogCQcl~MZ>zJp#Rt0ORq<=^v1lH62G{ Date: Wed, 25 Jun 2025 14:37:30 +0200 Subject: [PATCH 22/42] chore: update dependencies --- pyproject.toml | 4 +- uv.lock | 161 +++++++++++++++++++++++++------------------------ 2 files changed, 83 insertions(+), 82 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2a03c91..75c0a88 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,12 +44,12 @@ replace = "{new_version}" regex = false ignore_missing_version = false ignore_missing_files = false -tag = false +tag = true sign_tags = false tag_name = "v{new_version}" tag_message = "Bump version: {current_version} → {new_version}" allow_dirty = false -commit = false +commit = true message = "Bump version: {current_version} → {new_version}" moveable_tags = [] commit_args = "" diff --git a/uv.lock b/uv.lock index a58dca0..f7f6efc 100644 --- a/uv.lock +++ b/uv.lock @@ -74,15 +74,16 @@ wheels = [ [[package]] name = "backrefs" -version = "5.8" +version = "5.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/46/caba1eb32fa5784428ab401a5487f73db4104590ecd939ed9daaf18b47e0/backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd", size = 6773994 } +sdist = { url = "https://files.pythonhosted.org/packages/eb/a7/312f673df6a79003279e1f55619abbe7daebbb87c17c976ddc0345c04c7b/backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59", size = 5765857 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/cb/d019ab87fe70e0fe3946196d50d6a4428623dc0c38a6669c8cae0320fbf3/backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d", size = 380337 }, - { url = "https://files.pythonhosted.org/packages/a9/86/abd17f50ee21b2248075cb6924c6e7f9d23b4925ca64ec660e869c2633f1/backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b", size = 392142 }, - { url = "https://files.pythonhosted.org/packages/b3/04/7b415bd75c8ab3268cc138c76fa648c19495fcc7d155508a0e62f3f82308/backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486", size = 398021 }, - { url = "https://files.pythonhosted.org/packages/04/b8/60dcfb90eb03a06e883a92abbc2ab95c71f0d8c9dd0af76ab1d5ce0b1402/backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585", size = 399915 }, - { url = "https://files.pythonhosted.org/packages/0c/37/fb6973edeb700f6e3d6ff222400602ab1830446c25c7b4676d8de93e65b8/backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc", size = 380336 }, + { url = "https://files.pythonhosted.org/packages/19/4d/798dc1f30468134906575156c089c492cf79b5a5fd373f07fe26c4d046bf/backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f", size = 380267 }, + { url = "https://files.pythonhosted.org/packages/55/07/f0b3375bf0d06014e9787797e6b7cc02b38ac9ff9726ccfe834d94e9991e/backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf", size = 392072 }, + { url = "https://files.pythonhosted.org/packages/9d/12/4f345407259dd60a0997107758ba3f221cf89a9b5a0f8ed5b961aef97253/backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa", size = 397947 }, + { url = "https://files.pythonhosted.org/packages/10/bf/fa31834dc27a7f05e5290eae47c82690edc3a7b37d58f7fb35a1bdbf355b/backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b", size = 399843 }, + { url = "https://files.pythonhosted.org/packages/fc/24/b29af34b2c9c41645a9f4ff117bae860291780d73880f449e0b5d948c070/backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9", size = 411762 }, + { url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60", size = 380265 }, ] [[package]] @@ -109,11 +110,11 @@ sdist = { url = "https://files.pythonhosted.org/packages/92/8d/e296c7af03757debd [[package]] name = "bracex" -version = "2.5.post1" +version = "2.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/6c/57418c4404cd22fe6275b8301ca2b46a8cdaa8157938017a9ae0b3edf363/bracex-2.5.post1.tar.gz", hash = "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6", size = 26641 } +sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/02/8db98cdc1a58e0abd6716d5e63244658e6e63513c65f469f34b6f1053fd0/bracex-2.5.post1-py3-none-any.whl", hash = "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6", size = 11558 }, + { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508 }, ] [[package]] @@ -138,11 +139,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650 }, ] [[package]] @@ -528,11 +529,11 @@ wheels = [ [[package]] name = "markdown" -version = "3.8" +version = "3.8.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071 } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210 }, + { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827 }, ] [[package]] @@ -683,53 +684,53 @@ wheels = [ [[package]] name = "nuitka" -version = "2.7.7" +version = "2.7.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ordered-set" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/3f/3294699ad8ebca820127b5606c77e52bb1bd5d7eea97e9dd5a6228884e80/Nuitka-2.7.7.tar.gz", hash = "sha256:327e697e1a3eea2608ca7dce228c2d7686d65e38af9907c98646695ba5df9edf", size = 3886105 } +sdist = { url = "https://files.pythonhosted.org/packages/07/23/01d9537320d170abff79d9f61bd1e9500b464a503433c0fa0a76375f3fd2/Nuitka-2.7.10.tar.gz", hash = "sha256:c510c44027bcd9890deb00586ea87b00dc787addace0722095f041b1414395fa", size = 3887512 } [[package]] name = "numpy" -version = "2.3.0" +version = "2.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/db/8e12381333aea300890829a0a36bfa738cac95475d88982d538725143fd9/numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6", size = 20382813 } +sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372 } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/59/9df493df81ac6f76e9f05cdbe013cdb0c9a37b434f6e594f5bd25e278908/numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba", size = 20897025 }, - { url = "https://files.pythonhosted.org/packages/2f/86/4ff04335901d6cf3a6bb9c748b0097546ae5af35e455ae9b962ebff4ecd7/numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e", size = 14129882 }, - { url = "https://files.pythonhosted.org/packages/71/8d/a942cd4f959de7f08a79ab0c7e6cecb7431d5403dce78959a726f0f57aa1/numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2", size = 5110181 }, - { url = "https://files.pythonhosted.org/packages/86/5d/45850982efc7b2c839c5626fb67fbbc520d5b0d7c1ba1ae3651f2f74c296/numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459", size = 6647581 }, - { url = "https://files.pythonhosted.org/packages/1a/c0/c871d4a83f93b00373d3eebe4b01525eee8ef10b623a335ec262b58f4dc1/numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a", size = 14262317 }, - { url = "https://files.pythonhosted.org/packages/b7/f6/bc47f5fa666d5ff4145254f9e618d56e6a4ef9b874654ca74c19113bb538/numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a", size = 16633919 }, - { url = "https://files.pythonhosted.org/packages/f5/b4/65f48009ca0c9b76df5f404fccdea5a985a1bb2e34e97f21a17d9ad1a4ba/numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67", size = 15567651 }, - { url = "https://files.pythonhosted.org/packages/f1/62/5367855a2018578e9334ed08252ef67cc302e53edc869666f71641cad40b/numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc", size = 18361723 }, - { url = "https://files.pythonhosted.org/packages/d4/75/5baed8cd867eabee8aad1e74d7197d73971d6a3d40c821f1848b8fab8b84/numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570", size = 6318285 }, - { url = "https://files.pythonhosted.org/packages/bc/49/d5781eaa1a15acb3b3a3f49dc9e2ff18d92d0ce5c2976f4ab5c0a7360250/numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd", size = 12732594 }, - { url = "https://files.pythonhosted.org/packages/c2/1c/6d343e030815c7c97a1f9fbad00211b47717c7fe446834c224bd5311e6f1/numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea", size = 9891498 }, - { url = "https://files.pythonhosted.org/packages/73/fc/1d67f751fd4dbafc5780244fe699bc4084268bad44b7c5deb0492473127b/numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a", size = 20889633 }, - { url = "https://files.pythonhosted.org/packages/e8/95/73ffdb69e5c3f19ec4530f8924c4386e7ba097efc94b9c0aff607178ad94/numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959", size = 14151683 }, - { url = "https://files.pythonhosted.org/packages/64/d5/06d4bb31bb65a1d9c419eb5676173a2f90fd8da3c59f816cc54c640ce265/numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe", size = 5102683 }, - { url = "https://files.pythonhosted.org/packages/12/8b/6c2cef44f8ccdc231f6b56013dff1d71138c48124334aded36b1a1b30c5a/numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb", size = 6640253 }, - { url = "https://files.pythonhosted.org/packages/62/aa/fca4bf8de3396ddb59544df9b75ffe5b73096174de97a9492d426f5cd4aa/numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0", size = 14258658 }, - { url = "https://files.pythonhosted.org/packages/1c/12/734dce1087eed1875f2297f687e671cfe53a091b6f2f55f0c7241aad041b/numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f", size = 16628765 }, - { url = "https://files.pythonhosted.org/packages/48/03/ffa41ade0e825cbcd5606a5669962419528212a16082763fc051a7247d76/numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8", size = 15564335 }, - { url = "https://files.pythonhosted.org/packages/07/58/869398a11863310aee0ff85a3e13b4c12f20d032b90c4b3ee93c3b728393/numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270", size = 18360608 }, - { url = "https://files.pythonhosted.org/packages/2f/8a/5756935752ad278c17e8a061eb2127c9a3edf4ba2c31779548b336f23c8d/numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f", size = 6310005 }, - { url = "https://files.pythonhosted.org/packages/08/60/61d60cf0dfc0bf15381eaef46366ebc0c1a787856d1db0c80b006092af84/numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5", size = 12729093 }, - { url = "https://files.pythonhosted.org/packages/66/31/2f2f2d2b3e3c32d5753d01437240feaa32220b73258c9eef2e42a0832866/numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e", size = 9885689 }, - { url = "https://files.pythonhosted.org/packages/f1/89/c7828f23cc50f607ceb912774bb4cff225ccae7131c431398ad8400e2c98/numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8", size = 20986612 }, - { url = "https://files.pythonhosted.org/packages/dd/46/79ecf47da34c4c50eedec7511e53d57ffdfd31c742c00be7dc1d5ffdb917/numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3", size = 14298953 }, - { url = "https://files.pythonhosted.org/packages/59/44/f6caf50713d6ff4480640bccb2a534ce1d8e6e0960c8f864947439f0ee95/numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f", size = 5225806 }, - { url = "https://files.pythonhosted.org/packages/a6/43/e1fd1aca7c97e234dd05e66de4ab7a5be54548257efcdd1bc33637e72102/numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808", size = 6735169 }, - { url = "https://files.pythonhosted.org/packages/84/89/f76f93b06a03177c0faa7ca94d0856c4e5c4bcaf3c5f77640c9ed0303e1c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8", size = 14330701 }, - { url = "https://files.pythonhosted.org/packages/aa/f5/4858c3e9ff7a7d64561b20580cf7cc5d085794bd465a19604945d6501f6c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad", size = 16692983 }, - { url = "https://files.pythonhosted.org/packages/08/17/0e3b4182e691a10e9483bcc62b4bb8693dbf9ea5dc9ba0b77a60435074bb/numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b", size = 15641435 }, - { url = "https://files.pythonhosted.org/packages/4e/d5/463279fda028d3c1efa74e7e8d507605ae87f33dbd0543cf4c4527c8b882/numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555", size = 18433798 }, - { url = "https://files.pythonhosted.org/packages/0e/1e/7a9d98c886d4c39a2b4d3a7c026bffcf8fbcaf518782132d12a301cfc47a/numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61", size = 6438632 }, - { url = "https://files.pythonhosted.org/packages/fe/ab/66fc909931d5eb230107d016861824f335ae2c0533f422e654e5ff556784/numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb", size = 12868491 }, - { url = "https://files.pythonhosted.org/packages/ee/e8/2c8a1c9e34d6f6d600c83d5ce5b71646c32a13f34ca5c518cc060639841c/numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944", size = 9935345 }, + { url = "https://files.pythonhosted.org/packages/c6/56/71ad5022e2f63cfe0ca93559403d0edef14aea70a841d640bd13cdba578e/numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", size = 20896664 }, + { url = "https://files.pythonhosted.org/packages/25/65/2db52ba049813670f7f987cc5db6dac9be7cd95e923cc6832b3d32d87cef/numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", size = 14131078 }, + { url = "https://files.pythonhosted.org/packages/57/dd/28fa3c17b0e751047ac928c1e1b6990238faad76e9b147e585b573d9d1bd/numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", size = 5112554 }, + { url = "https://files.pythonhosted.org/packages/c9/fc/84ea0cba8e760c4644b708b6819d91784c290288c27aca916115e3311d17/numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", size = 6646560 }, + { url = "https://files.pythonhosted.org/packages/61/b2/512b0c2ddec985ad1e496b0bd853eeb572315c0f07cd6997473ced8f15e2/numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", size = 14260638 }, + { url = "https://files.pythonhosted.org/packages/6e/45/c51cb248e679a6c6ab14b7a8e3ead3f4a3fe7425fc7a6f98b3f147bec532/numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", size = 16632729 }, + { url = "https://files.pythonhosted.org/packages/e4/ff/feb4be2e5c09a3da161b412019caf47183099cbea1132fd98061808c2df2/numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", size = 15565330 }, + { url = "https://files.pythonhosted.org/packages/bc/6d/ceafe87587101e9ab0d370e4f6e5f3f3a85b9a697f2318738e5e7e176ce3/numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77", size = 18361734 }, + { url = "https://files.pythonhosted.org/packages/2b/19/0fb49a3ea088be691f040c9bf1817e4669a339d6e98579f91859b902c636/numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", size = 6320411 }, + { url = "https://files.pythonhosted.org/packages/b1/3e/e28f4c1dd9e042eb57a3eb652f200225e311b608632bc727ae378623d4f8/numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", size = 12734973 }, + { url = "https://files.pythonhosted.org/packages/04/a8/8a5e9079dc722acf53522b8f8842e79541ea81835e9b5483388701421073/numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", size = 10191491 }, + { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381 }, + { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726 }, + { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145 }, + { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409 }, + { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630 }, + { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546 }, + { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538 }, + { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327 }, + { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330 }, + { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565 }, + { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262 }, + { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593 }, + { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523 }, + { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993 }, + { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652 }, + { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561 }, + { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349 }, + { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053 }, + { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184 }, + { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678 }, + { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697 }, + { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376 }, ] [[package]] @@ -747,7 +748,7 @@ wheels = [ [[package]] name = "openai" -version = "1.86.0" +version = "1.91.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -759,9 +760,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/7a/9ad4a61f1502f0e59d8c27fb629e28a63259a44d8d31cd2314e1534a2d9f/openai-1.86.0.tar.gz", hash = "sha256:c64d5b788359a8fdf69bd605ae804ce41c1ce2e78b8dd93e2542e0ee267f1e4b", size = 468272 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/e2/a22f2973b729eff3f1f429017bdf717930c5de0fbf9e14017bae330e4e7a/openai-1.91.0.tar.gz", hash = "sha256:d6b07730d2f7c6745d0991997c16f85cddfc90ddcde8d569c862c30716b9fc90", size = 472529 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/c1/dfb16b3432810fc9758564f9d1a4dbce6b93b7fb763ba57530c7fc48316d/openai-1.86.0-py3-none-any.whl", hash = "sha256:c8889c39410621fe955c230cc4c21bfe36ec887f4e60a957de05f507d7e1f349", size = 730296 }, + { url = "https://files.pythonhosted.org/packages/7a/d2/f99bdd6fc737d6b3cf0df895508d621fc9a386b375a1230ee81d46c5436e/openai-1.91.0-py3-none-any.whl", hash = "sha256:207f87aa3bc49365e014fac2f7e291b99929f4fe126c4654143440e0ad446a5f", size = 735837 }, ] [[package]] @@ -885,7 +886,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.5" +version = "2.11.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -893,9 +894,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102 } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229 }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, ] [[package]] @@ -942,16 +943,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.9.1" +version = "2.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234 } +sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 }, + { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 }, ] [[package]] @@ -968,24 +969,24 @@ wheels = [ [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] [[package]] name = "pymdown-extensions" -version = "10.15" +version = "10.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/92/a7296491dbf5585b3a987f3f3fc87af0e632121ff3e490c14b5f2d2b4eb5/pymdown_extensions-10.15.tar.gz", hash = "sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7", size = 852320 } +sdist = { url = "https://files.pythonhosted.org/packages/1a/0a/c06b542ac108bfc73200677309cd9188a3a01b127a63f20cadc18d873d88/pymdown_extensions-10.16.tar.gz", hash = "sha256:71dac4fca63fabeffd3eb9038b756161a33ec6e8d230853d3cecf562155ab3de", size = 853197 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/d1/c54e608505776ce4e7966d03358ae635cfd51dff1da6ee421c090dbc797b/pymdown_extensions-10.15-py3-none-any.whl", hash = "sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f", size = 265845 }, + { url = "https://files.pythonhosted.org/packages/98/d4/10bb14004d3c792811e05e21b5e5dcae805aacb739bd12a0540967b99592/pymdown_extensions-10.16-py3-none-any.whl", hash = "sha256:f5dd064a4db588cb2d95229fc4ee63a1b16cc8b4d0e6145c0899ed8723da1df2", size = 266143 }, ] [[package]] @@ -1059,24 +1060,24 @@ wheels = [ [[package]] name = "python-docx" -version = "1.1.2" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lxml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/e4/386c514c53684772885009c12b67a7edd526c15157778ac1b138bc75063e/python_docx-1.1.2.tar.gz", hash = "sha256:0cf1f22e95b9002addca7948e16f2cd7acdfd498047f1941ca5d293db7762efd", size = 5656581 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/eddfe33871520adab45aaa1a71f0402a2252050c14c7e3009446c8f4701c/python_docx-1.2.0.tar.gz", hash = "sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce", size = 5723256 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/3d/330d9efbdb816d3f60bf2ad92f05e1708e4a1b9abe80461ac3444c83f749/python_docx-1.1.2-py3-none-any.whl", hash = "sha256:08c20d6058916fb19853fcf080f7f42b6270d89eac9fa5f8c15f691c0017fabe", size = 244315 }, + { url = "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl", hash = "sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7", size = 252987 }, ] [[package]] name = "python-dotenv" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, ] [[package]] @@ -1379,11 +1380,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, ] [[package]] @@ -1412,14 +1413,14 @@ wheels = [ [[package]] name = "wcmatch" -version = "10.0" +version = "10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bracex" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/ab/b3a52228538ccb983653c446c1656eddf1d5303b9cb8b9aef6a91299f862/wcmatch-10.0.tar.gz", hash = "sha256:e72f0de09bba6a04e0de70937b0cf06e55f36f37b3deb422dfaf854b867b840a", size = 115578 } +sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e47680bd5803a07cb3dfd17de84cde92dd217dcb9e05253/wcmatch-10.1.tar.gz", hash = "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af", size = 117421 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/df/4ee467ab39cc1de4b852c212c1ed3becfec2e486a51ac1ce0091f85f38d7/wcmatch-10.0-py3-none-any.whl", hash = "sha256:0dd927072d03c0a6527a20d2e6ad5ba8d0380e60870c383bc533b71744df7b7a", size = 39347 }, + { url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854 }, ] [[package]] From edd57011e06e2e967a0ad8957bba450be1a48681 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 25 Jun 2025 14:37:41 +0200 Subject: [PATCH 23/42] feat: add build script --- build.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 build.py diff --git a/build.py b/build.py new file mode 100644 index 0000000..231e221 --- /dev/null +++ b/build.py @@ -0,0 +1,21 @@ +import os + + +with open(".version", "r") as version_file: + version = version_file.read().strip() + +print("Building the project...") +build = input("Include console in build? (y/n): ").strip().lower() + + +command = "uv run python -m nuitka --standalone --output-dir=dist --include-data-dir=./config=config --include-data-dir=./docs=docs --include-data-dir=./icons=icons --enable-plugin=pyside6" +executable = "main.py" + + +if build == 'y': + + os.system(f"{command} {executable}") +else: + command += " --windows-console-mode=disable" + os.system(f"{command} {executable}") + From 7e0e26619f2acadce70d4526fd90f7b580185372 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 2 Jul 2025 08:29:44 +0200 Subject: [PATCH 24/42] revert version --- src/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__init__.py b/src/__init__.py index daeaf7a..1259fd9 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.2.1" +__version__ = "0.1.0" __author__ = "Alexander Kirchner" __all__ = ["__version__", "__author__", "Icon", "settings"] From bc061dcbc66435d22cac2de32249286ee6d55acd Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 2 Jul 2025 08:29:58 +0200 Subject: [PATCH 25/42] add .version file --- .version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .version diff --git a/.version b/.version new file mode 100644 index 0000000..6c6aa7c --- /dev/null +++ b/.version @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file From 5eccbebef7f4efb4e441c3415f2cd5e253c1e771 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 2 Jul 2025 08:30:19 +0200 Subject: [PATCH 26/42] add pyramid dependency for documentation hosting --- pyproject.toml | 1 + uv.lock | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 75c0a88..778ce38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ dependencies = [ "openai>=1.79.0", "pandas>=2.2.3", "playwright>=1.49.1", + "pyramid>=2.0.2", "pyside6>=6.9.1", "python-docx>=1.1.2", "pyzotero>=1.6.4", diff --git a/uv.lock b/uv.lock index f7f6efc..9fb517d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,9 @@ version = 1 requires-python = ">=3.12" +resolution-markers = [ + "python_full_version < '3.13'", + "python_full_version >= '3.13'", +] [[package]] name = "annotated-types" @@ -388,6 +392,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] +[[package]] +name = "hupper" +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/e6/bb064537288eee2be97f3e0fcad8e7242bc5bbe9664ae57c7d29b3fa18c2/hupper-1.12.1.tar.gz", hash = "sha256:06bf54170ff4ecf4c84ad5f188dee3901173ab449c2608ad05b9bfd6b13e32eb", size = 43231 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/7d/3888833e4f5ea56af4a9935066ec09a83228e533d7b8877f65889d706ee4/hupper-1.12.1-py3-none-any.whl", hash = "sha256:e872b959f09d90be5fb615bd2e62de89a0b57efc037bdf9637fb09cdf8552b19", size = 22830 }, +] + [[package]] name = "icecream" version = "2.1.4" @@ -472,6 +485,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, ] +[[package]] +name = "legacy-cgi" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/ed/300cabc9693209d5a03e2ebc5eb5c4171b51607c08ed84a2b71c9015e0f3/legacy_cgi-2.6.3.tar.gz", hash = "sha256:4c119d6cb8e9d8b6ad7cc0ddad880552c62df4029622835d06dfd18f438a8154", size = 24401 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/33/68c6c38193684537757e0d50a7ccb4f4656e5c2f7cd2be737a9d4a1bff71/legacy_cgi-2.6.3-py3-none-any.whl", hash = "sha256:6df2ea5ae14c71ef6f097f8b6372b44f6685283dc018535a75c924564183cdab", size = 19851 }, +] + [[package]] name = "loguru" version = "0.7.3" @@ -826,6 +848,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/c2/646d2e93e0af70f4e5359d870a63584dacbc324b54d73e6b3267920ff117/pandas-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bb3be958022198531eb7ec2008cfc78c5b1eed51af8600c6c5d9160d89d8d249", size = 13231847 }, ] +[[package]] +name = "pastedeploy" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/97/0c4a613ec96a54d21daa7e089178263915554320402e89b4e319436a63cb/PasteDeploy-3.1.0.tar.gz", hash = "sha256:9ddbaf152f8095438a9fe81f82c78a6714b92ae8e066bed418b6a7ff6a095a95", size = 37841 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/30/cdddd9a88969683a59222a6d61cd6dce923977f2e9f9ffba38e1324149cd/PasteDeploy-3.1.0-py3-none-any.whl", hash = "sha256:76388ad53a661448d436df28c798063108f70e994ddc749540d733cdbd1b38cf", size = 16943 }, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -835,6 +866,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, ] +[[package]] +name = "plaster" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/93/66df0f87f1442d8afea8531ae8a4a9eca656006a54eac2b4489427e92c10/plaster-1.1.2.tar.gz", hash = "sha256:f8befc54bf8c1147c10ab40297ec84c2676fa2d4ea5d6f524d9436a80074ef98", size = 33232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/8b/3f98db1448e3b4d2d142716874a7e02f6101685fdaa0f55a8668e9ffa048/plaster-1.1.2-py2.py3-none-any.whl", hash = "sha256:42992ab1f4865f1278e2ad740e8ad145683bb4022e03534265528f0c23c0df2d", size = 11554 }, +] + +[[package]] +name = "plaster-pastedeploy" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pastedeploy" }, + { name = "plaster" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/af/01a22f73ce97c6375c88d7ceaf6f5f4f345e940da93c94f98833d898a449/plaster_pastedeploy-1.0.1.tar.gz", hash = "sha256:be262e6d2e41a7264875daa2fe2850cbb0615728bcdc92828fdc72736e381412", size = 20915 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/30/2d4cf89035c22a89bf0e34dbc50fdc07c42c9bdc90fd972d495257ad2b6e/plaster_pastedeploy-1.0.1-py2.py3-none-any.whl", hash = "sha256:ad3550cc744648969ed3b810f33c9344f515ee8d8a8cec18e8f2c4a643c2181f", size = 7823 }, +] + [[package]] name = "platformdirs" version = "4.3.8" @@ -998,6 +1051,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120 }, ] +[[package]] +name = "pyramid" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hupper" }, + { name = "plaster" }, + { name = "plaster-pastedeploy" }, + { name = "setuptools" }, + { name = "translationstring" }, + { name = "venusian" }, + { name = "webob" }, + { name = "zope-deprecation" }, + { name = "zope-interface" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/c3/5d5736e692fc7ff052577f03136b5edfdf1e2e177eff2f4b91206acae11d/pyramid-2.0.2.tar.gz", hash = "sha256:372138a738e4216535cc76dcce6eddd5a1aaca95130f2354fb834264c06f18de", size = 2637533 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/41/a2114b8dd2187ae007e022a2baabdc7937cc78211cefc0c01fc5452193af/pyramid-2.0.2-py3-none-any.whl", hash = "sha256:2e6585ac55c147f0a51bc00dadf72075b3bdd9a871b332ff9e5e04117ccd76fa", size = 247277 }, +] + [[package]] name = "pyside6" version = "6.9.1" @@ -1236,6 +1309,7 @@ dependencies = [ { name = "openai" }, { name = "pandas" }, { name = "playwright" }, + { name = "pyramid" }, { name = "pyside6" }, { name = "python-docx" }, { name = "pyzotero" }, @@ -1268,6 +1342,7 @@ requires-dist = [ { name = "openai", specifier = ">=1.79.0" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "playwright", specifier = ">=1.49.1" }, + { name = "pyramid", specifier = ">=2.0.2" }, { name = "pyside6", specifier = ">=6.9.1" }, { name = "python-docx", specifier = ">=1.1.2" }, { name = "pyzotero", specifier = ">=1.6.4" }, @@ -1282,6 +1357,15 @@ dev = [ { name = "nuitka", specifier = ">=2.5.9" }, ] +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486 }, +] + [[package]] name = "sgmllib3k" version = "1.0.0" @@ -1348,6 +1432,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, ] +[[package]] +name = "translationstring" +version = "1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/39/32325add93da9439775d7fe4b4887eb7986dbc1d5675b0431f4531f560e5/translationstring-1.4.tar.gz", hash = "sha256:bf947538d76e69ba12ab17283b10355a9ecfbc078e6123443f43f2107f6376f3", size = 24199 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/98/36187601a15e3d37e9bfcf0e0e1055532b39d044353b06861c3a519737a9/translationstring-1.4-py2.py3-none-any.whl", hash = "sha256:5f4dc4d939573db851c8d840551e1a0fb27b946afe3b95aafc22577eed2d6262", size = 15028 }, +] + [[package]] name = "typing-extensions" version = "4.14.0" @@ -1387,6 +1480,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, ] +[[package]] +name = "venusian" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/4c/eefa68085c555dc11e6744b9c6fbe5966b1c9378c47267776a448923e9a5/venusian-3.1.1.tar.gz", hash = "sha256:534fb3b355669283eb3954581931e5d1d071fce61d029d58f3219a5e3a6f0c41", size = 39269 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/4b/34d926eba40db81b204066a60b4efdc5d8867a8efcbfe44d69b634b1c907/venusian-3.1.1-py3-none-any.whl", hash = "sha256:0845808a985976acbceaa1fbb871c7fac4fb28ae75453232970e9c2c2866dbf4", size = 14026 }, +] + [[package]] name = "watchdog" version = "6.0.0" @@ -1432,6 +1534,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, ] +[[package]] +name = "webob" +version = "1.8.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "legacy-cgi", marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/0b/1732085540b01f65e4e7999e15864fe14cd18b12a95731a43fd6fd11b26a/webob-1.8.9.tar.gz", hash = "sha256:ad6078e2edb6766d1334ec3dee072ac6a7f95b1e32ce10def8ff7f0f02d56589", size = 279775 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/bd/c336448be43d40be28e71f2e0f3caf7ccb28e2755c58f4c02c065bfe3e8e/WebOb-1.8.9-py2.py3-none-any.whl", hash = "sha256:45e34c58ed0c7e2ecd238ffd34432487ff13d9ad459ddfd77895e67abba7c1f9", size = 115364 }, +] + [[package]] name = "win32-setctime" version = "1.2.0" @@ -1441,6 +1555,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 }, ] +[[package]] +name = "zope-deprecation" +version = "5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/22/0e621e31b5826b2ff121fea5b1ea91173c88f86e182181f012abcc84a51f/zope_deprecation-5.1.tar.gz", hash = "sha256:46bed4611fb53edc731aadeb64b28308bcb848f4cc150c60c948d078f7108721", size = 24453 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/88/5fc32633682a452260f50417da3d4be26137dd220ef617bbd8ed52f0cfa9/zope.deprecation-5.1-py3-none-any.whl", hash = "sha256:60f957b964d8f947a4a592c647d51ce0f4f844d1f041657956ddde0d9fa9a76a", size = 10020 }, +] + +[[package]] +name = "zope-interface" +version = "7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/93/9210e7606be57a2dfc6277ac97dcc864fd8d39f142ca194fdc186d596fda/zope.interface-7.2.tar.gz", hash = "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe", size = 252960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/0b/c7516bc3bad144c2496f355e35bd699443b82e9437aa02d9867653203b4a/zope.interface-7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7", size = 208959 }, + { url = "https://files.pythonhosted.org/packages/a2/e9/1463036df1f78ff8c45a02642a7bf6931ae4a38a4acd6a8e07c128e387a7/zope.interface-7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465", size = 209357 }, + { url = "https://files.pythonhosted.org/packages/07/a8/106ca4c2add440728e382f1b16c7d886563602487bdd90004788d45eb310/zope.interface-7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89", size = 264235 }, + { url = "https://files.pythonhosted.org/packages/fc/ca/57286866285f4b8a4634c12ca1957c24bdac06eae28fd4a3a578e30cf906/zope.interface-7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54", size = 259253 }, + { url = "https://files.pythonhosted.org/packages/96/08/2103587ebc989b455cf05e858e7fbdfeedfc3373358320e9c513428290b1/zope.interface-7.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d", size = 264702 }, + { url = "https://files.pythonhosted.org/packages/5f/c7/3c67562e03b3752ba4ab6b23355f15a58ac2d023a6ef763caaca430f91f2/zope.interface-7.2-cp312-cp312-win_amd64.whl", hash = "sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5", size = 212466 }, + { url = "https://files.pythonhosted.org/packages/c6/3b/e309d731712c1a1866d61b5356a069dd44e5b01e394b6cb49848fa2efbff/zope.interface-7.2-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98", size = 208961 }, + { url = "https://files.pythonhosted.org/packages/49/65/78e7cebca6be07c8fc4032bfbb123e500d60efdf7b86727bb8a071992108/zope.interface-7.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d", size = 209356 }, + { url = "https://files.pythonhosted.org/packages/11/b1/627384b745310d082d29e3695db5f5a9188186676912c14b61a78bbc6afe/zope.interface-7.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c", size = 264196 }, + { url = "https://files.pythonhosted.org/packages/b8/f6/54548df6dc73e30ac6c8a7ff1da73ac9007ba38f866397091d5a82237bd3/zope.interface-7.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398", size = 259237 }, + { url = "https://files.pythonhosted.org/packages/b6/66/ac05b741c2129fdf668b85631d2268421c5cd1a9ff99be1674371139d665/zope.interface-7.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b", size = 264696 }, + { url = "https://files.pythonhosted.org/packages/0a/2f/1bccc6f4cc882662162a1158cda1a7f616add2ffe322b28c99cb031b4ffc/zope.interface-7.2-cp313-cp313-win_amd64.whl", hash = "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd", size = 212472 }, +] + [[package]] name = "zstandard" version = "0.23.0" From cd255696f0fcfa74a85ba532e0a8356703ce011d Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 2 Jul 2025 08:30:56 +0200 Subject: [PATCH 27/42] rework documentation launch --- src/utils/documentation.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/utils/documentation.py b/src/utils/documentation.py index 2e41721..fa70640 100644 --- a/src/utils/documentation.py +++ b/src/utils/documentation.py @@ -1,5 +1,28 @@ import os import sys +from pyramid.config import Configurator +from wsgiref.simple_server import make_server + +docport = 8000 + + +def website(): + config = Configurator() + + # Set up static file serving from the 'site/' directory + config.add_static_view( + name="/", path=os.path.join(os.getcwd(), "site"), cache_max_age=3600 + ) + + app = config.make_wsgi_app() + return app + + +def launch_documentation(): + app = website() + server = make_server("localhost", docport, app) + print("Serving MkDocs documentation on http://0.0.0.0:{}".format(docport)) + server.serve_forever() def run_mkdocs(): @@ -9,7 +32,9 @@ def run_mkdocs(): sys.stdout = devnull sys.stderr = devnull try: - os.system("mkdocs serve -q") + launch_documentation() + except Exception as e: + print("Error occurred while launching documentation:", e) finally: sys.stdout = old_stdout sys.stderr = old_stderr From 30228fd26770087915ba543eec5e4220e9b1b31d Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Wed, 2 Jul 2025 11:00:56 +0200 Subject: [PATCH 28/42] update build command --- build.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index 231e221..ca74144 100644 --- a/build.py +++ b/build.py @@ -8,7 +8,7 @@ print("Building the project...") build = input("Include console in build? (y/n): ").strip().lower() -command = "uv run python -m nuitka --standalone --output-dir=dist --include-data-dir=./config=config --include-data-dir=./docs=docs --include-data-dir=./icons=icons --enable-plugin=pyside6" +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 --enable-plugin=pyside6 --product-name=SemesterApparatsManager --product-version={version} --output-filename=SAM.exe --windows-icon-from-ico=icons/logo.ico" executable = "main.py" @@ -19,3 +19,7 @@ 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.") \ No newline at end of file From a6d9498b394365899ba172d041b2116dc069c41a Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 07:20:57 +0200 Subject: [PATCH 29/42] rework documentation launch, use generated files instead of pure mkdocs, add quiet handler for use with logging in terminal --- src/backend/documentation_thread.py | 18 +- src/ui/semesterapparat_ui.ui | 21 +- src/ui/semesterapparat_ui_ui.py | 1744 ++++++++++++++------------- src/ui/userInterface.py | 25 +- src/utils/documentation.py | 12 +- 5 files changed, 920 insertions(+), 900 deletions(-) diff --git a/src/backend/documentation_thread.py b/src/backend/documentation_thread.py index e411ec3..57cffcb 100644 --- a/src/backend/documentation_thread.py +++ b/src/backend/documentation_thread.py @@ -1,11 +1,23 @@ -from PySide6.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() \ No newline at end of file diff --git a/src/ui/semesterapparat_ui.ui b/src/ui/semesterapparat_ui.ui index d5a9c70..9831acd 100644 --- a/src/ui/semesterapparat_ui.ui +++ b/src/ui/semesterapparat_ui.ui @@ -1923,8 +1923,8 @@ Einige Angaben müssen ggf angepasst werden Help - + @@ -1956,17 +1956,6 @@ Einige Angaben müssen ggf angepasst werden true - - - Dokumentation (online) - - - F1 - - - Qt::ApplicationShortcut - - About @@ -1975,9 +1964,9 @@ Einige Angaben müssen ggf angepasst werden QAction::AboutRole - + - Dokumentation (lokal) + Dokumentation F1 @@ -2009,8 +1998,6 @@ Einige Angaben müssen ggf angepasst werden automation_add_selected_books saveandcreate - - - + diff --git a/src/ui/semesterapparat_ui_ui.py b/src/ui/semesterapparat_ui_ui.py index 077fb02..0b27766 100644 --- a/src/ui/semesterapparat_ui_ui.py +++ b/src/ui/semesterapparat_ui_ui.py @@ -1,845 +1,785 @@ -# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\semesterapparat_ui.ui' -# -# 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. +# -*- coding: utf-8 -*- +################################################################################ +## Form generated from reading UI file 'semesterapparat_ui.ui' +## +## Created by: Qt User Interface Compiler version 6.9.1 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ -from PySide6 import QtCore, QtGui, QtWidgets - +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient, + QCursor, QFont, QFontDatabase, QGradient, + QIcon, QImage, QKeySequence, QLinearGradient, + QPainter, QPalette, QPixmap, QRadialGradient, + QTransform) +from PySide6.QtWidgets import (QAbstractItemView, QAbstractScrollArea, QApplication, QCheckBox, + QComboBox, QFormLayout, QFrame, QGridLayout, + QGroupBox, QHBoxLayout, QHeaderView, QLabel, + QLineEdit, QMainWindow, QMenu, QMenuBar, + QPushButton, QSizePolicy, QSpacerItem, QStatusBar, + QTabWidget, QTableWidget, QTableWidgetItem, QToolButton, + QVBoxLayout, QWidget) class Ui_MainWindow(object): def setupUi(self, MainWindow): - MainWindow.setObjectName("MainWindow") - MainWindow.setWindowModality(QtCore.Qt.WindowModality.WindowModal) + if not MainWindow.objectName(): + MainWindow.setObjectName(u"MainWindow") + MainWindow.setWindowModality(Qt.WindowModal) MainWindow.setEnabled(True) MainWindow.resize(1590, 800) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed) + sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) - MainWindow.setMinimumSize(QtCore.QSize(1278, 800)) - MainWindow.setMaximumSize(QtCore.QSize(1590, 800)) - MainWindow.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap("c:\\Users\\aky547\\GitHub\\SemesterapparatsManager\\src\\ui\\../../../../../../icons/logo.ico"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + MainWindow.setMinimumSize(QSize(1278, 800)) + MainWindow.setMaximumSize(QSize(1590, 800)) + MainWindow.setContextMenuPolicy(Qt.NoContextMenu) + icon = QIcon() + icon.addFile(u"../../../../../../icons/logo.ico", QSize(), QIcon.Mode.Normal, QIcon.State.Off) MainWindow.setWindowIcon(icon) - MainWindow.setStatusTip("") - self.centralwidget = QtWidgets.QWidget(parent=MainWindow) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) + self.actionBeenden = QAction(MainWindow) + self.actionBeenden.setObjectName(u"actionBeenden") + self.actionBeenden.setMenuRole(QAction.QuitRole) + self.actionBeenden.setShortcutVisibleInContextMenu(True) + self.actionEinstellungen = QAction(MainWindow) + self.actionEinstellungen.setObjectName(u"actionEinstellungen") + self.actionEinstellungen.setShortcutVisibleInContextMenu(True) + self.actionAbout = QAction(MainWindow) + self.actionAbout.setObjectName(u"actionAbout") + self.actionAbout.setMenuRole(QAction.AboutRole) + self.actionDokumentation = QAction(MainWindow) + self.actionDokumentation.setObjectName(u"actionDokumentation") + self.centralwidget = QWidget(MainWindow) + self.centralwidget.setObjectName(u"centralwidget") sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) self.centralwidget.setSizePolicy(sizePolicy) - self.centralwidget.setObjectName("centralwidget") - self.verticalLayoutWidget = QtWidgets.QWidget(parent=self.centralwidget) - self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 1271, 751)) - self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") - self.mainLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) + self.verticalLayoutWidget = QWidget(self.centralwidget) + self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget") + self.verticalLayoutWidget.setGeometry(QRect(0, 0, 1271, 751)) + self.mainLayout = QVBoxLayout(self.verticalLayoutWidget) + self.mainLayout.setObjectName(u"mainLayout") self.mainLayout.setContentsMargins(0, 0, 0, 0) - self.mainLayout.setObjectName("mainLayout") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.gridLayout = QtWidgets.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.tabWidget = QtWidgets.QTabWidget(parent=self.verticalLayoutWidget) - self.tabWidget.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.tabWidget.setObjectName("tabWidget") - self.createApparat = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.createApparat.sizePolicy().hasHeightForWidth()) - self.createApparat.setSizePolicy(sizePolicy) - self.createApparat.setObjectName("createApparat") - self.horizontalLayoutWidget_2 = QtWidgets.QWidget(parent=self.createApparat) - self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(0, 0, 1261, 163)) - self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2") - self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2) + self.horizontalLayout = QHBoxLayout() + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.gridLayout = QGridLayout() + self.gridLayout.setObjectName(u"gridLayout") + self.tabWidget = QTabWidget(self.verticalLayoutWidget) + self.tabWidget.setObjectName(u"tabWidget") + self.tabWidget.setFocusPolicy(Qt.NoFocus) + self.createApparat = QWidget() + self.createApparat.setObjectName(u"createApparat") + sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) + sizePolicy1.setHorizontalStretch(0) + sizePolicy1.setVerticalStretch(0) + sizePolicy1.setHeightForWidth(self.createApparat.sizePolicy().hasHeightForWidth()) + self.createApparat.setSizePolicy(sizePolicy1) + self.horizontalLayoutWidget_2 = QWidget(self.createApparat) + self.horizontalLayoutWidget_2.setObjectName(u"horizontalLayoutWidget_2") + self.horizontalLayoutWidget_2.setGeometry(QRect(0, 0, 1261, 163)) + self.horizontalLayout_2 = QHBoxLayout(self.horizontalLayoutWidget_2) + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.formLayout = QtWidgets.QFormLayout() - self.formLayout.setObjectName("formLayout") - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_2.addItem(spacerItem) - self.create_document = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_2) - self.create_document.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.create_document.setObjectName("create_document") + self.formLayout = QFormLayout() + self.formLayout.setObjectName(u"formLayout") + self.verticalLayout_2 = QVBoxLayout() + self.verticalLayout_2.setObjectName(u"verticalLayout_2") + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_2.addItem(self.verticalSpacer) + + self.create_document = QPushButton(self.horizontalLayoutWidget_2) + self.create_document.setObjectName(u"create_document") + self.create_document.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_2.addWidget(self.create_document) - self.create_new_app = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_2) - self.create_new_app.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.create_new_app.setObjectName("create_new_app") + + self.create_new_app = QPushButton(self.horizontalLayoutWidget_2) + self.create_new_app.setObjectName(u"create_new_app") + self.create_new_app.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_2.addWidget(self.create_new_app) - self.cancel_active_selection = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_2) + + self.cancel_active_selection = QPushButton(self.horizontalLayoutWidget_2) + self.cancel_active_selection.setObjectName(u"cancel_active_selection") self.cancel_active_selection.setEnabled(False) - self.cancel_active_selection.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.cancel_active_selection.setObjectName("cancel_active_selection") + self.cancel_active_selection.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_2.addWidget(self.cancel_active_selection) - spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_2.addItem(spacerItem1) - self.formLayout.setLayout(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.verticalLayout_2) - self.tableWidget_apparate = QtWidgets.QTableWidget(parent=self.horizontalLayoutWidget_2) - self.tableWidget_apparate.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.tableWidget_apparate.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) - self.tableWidget_apparate.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + + self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_2.addItem(self.verticalSpacer_2) + + + self.formLayout.setLayout(1, QFormLayout.ItemRole.LabelRole, self.verticalLayout_2) + + self.tableWidget_apparate = QTableWidget(self.horizontalLayoutWidget_2) + if (self.tableWidget_apparate.columnCount() < 6): + self.tableWidget_apparate.setColumnCount(6) + __qtablewidgetitem = QTableWidgetItem() + self.tableWidget_apparate.setHorizontalHeaderItem(0, __qtablewidgetitem) + __qtablewidgetitem1 = QTableWidgetItem() + self.tableWidget_apparate.setHorizontalHeaderItem(1, __qtablewidgetitem1) + __qtablewidgetitem2 = QTableWidgetItem() + self.tableWidget_apparate.setHorizontalHeaderItem(2, __qtablewidgetitem2) + __qtablewidgetitem3 = QTableWidgetItem() + self.tableWidget_apparate.setHorizontalHeaderItem(3, __qtablewidgetitem3) + __qtablewidgetitem4 = QTableWidgetItem() + self.tableWidget_apparate.setHorizontalHeaderItem(4, __qtablewidgetitem4) + __qtablewidgetitem5 = QTableWidgetItem() + self.tableWidget_apparate.setHorizontalHeaderItem(5, __qtablewidgetitem5) + self.tableWidget_apparate.setObjectName(u"tableWidget_apparate") + self.tableWidget_apparate.setFocusPolicy(Qt.NoFocus) + self.tableWidget_apparate.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) + self.tableWidget_apparate.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableWidget_apparate.setAlternatingRowColors(True) - self.tableWidget_apparate.setTextElideMode(QtCore.Qt.TextElideMode.ElideMiddle) - self.tableWidget_apparate.setObjectName("tableWidget_apparate") - self.tableWidget_apparate.setColumnCount(6) - self.tableWidget_apparate.setRowCount(0) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparate.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparate.setHorizontalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparate.setHorizontalHeaderItem(2, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparate.setHorizontalHeaderItem(3, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparate.setHorizontalHeaderItem(4, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparate.setHorizontalHeaderItem(5, item) + self.tableWidget_apparate.setTextElideMode(Qt.ElideMiddle) + self.tableWidget_apparate.setSortingEnabled(False) self.tableWidget_apparate.horizontalHeader().setCascadingSectionResizes(True) - self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.tableWidget_apparate) + + self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.tableWidget_apparate) + + self.horizontalLayout_2.addLayout(self.formLayout) - self.line = QtWidgets.QFrame(parent=self.createApparat) - self.line.setGeometry(QtCore.QRect(0, 160, 1261, 21)) - self.line.setFrameShape(QtWidgets.QFrame.Shape.HLine) - self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) - self.line.setObjectName("line") - self.gridLayoutWidget_2 = QtWidgets.QWidget(parent=self.createApparat) + + self.line = QFrame(self.createApparat) + self.line.setObjectName(u"line") + self.line.setGeometry(QRect(0, 160, 1261, 21)) + self.line.setFrameShape(QFrame.Shape.HLine) + self.line.setFrameShadow(QFrame.Shadow.Sunken) + self.gridLayoutWidget_2 = QWidget(self.createApparat) + self.gridLayoutWidget_2.setObjectName(u"gridLayoutWidget_2") self.gridLayoutWidget_2.setEnabled(True) - self.gridLayoutWidget_2.setGeometry(QtCore.QRect(0, 180, 1261, 511)) - self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2") - self.gridLayout_2 = QtWidgets.QGridLayout(self.gridLayoutWidget_2) + self.gridLayoutWidget_2.setGeometry(QRect(0, 180, 1261, 511)) + self.gridLayout_2 = QGridLayout(self.gridLayoutWidget_2) + self.gridLayout_2.setObjectName(u"gridLayout_2") self.gridLayout_2.setContentsMargins(0, 0, 0, 0) - self.gridLayout_2.setObjectName("gridLayout_2") - self.horizontalLayout_5 = QtWidgets.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - spacerItem2 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_5.addItem(spacerItem2) - self.chkbx_show_del_media = QtWidgets.QCheckBox(parent=self.gridLayoutWidget_2) - self.chkbx_show_del_media.setObjectName("chkbx_show_del_media") + self.horizontalLayout_5 = QHBoxLayout() + self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") + self.horizontalSpacer = QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_5.addItem(self.horizontalSpacer) + + self.chkbx_show_del_media = QCheckBox(self.gridLayoutWidget_2) + self.chkbx_show_del_media.setObjectName(u"chkbx_show_del_media") + self.horizontalLayout_5.addWidget(self.chkbx_show_del_media) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_5.addItem(spacerItem3) - self.btn_reserve = QtWidgets.QPushButton(parent=self.gridLayoutWidget_2) - self.btn_reserve.setObjectName("btn_reserve") + + self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_5.addItem(self.horizontalSpacer_3) + + self.btn_reserve = QPushButton(self.gridLayoutWidget_2) + self.btn_reserve.setObjectName(u"btn_reserve") + self.horizontalLayout_5.addWidget(self.btn_reserve) - self.add_layout = QtWidgets.QHBoxLayout() - self.add_layout.setObjectName("add_layout") - self.label_info = QtWidgets.QLabel(parent=self.gridLayoutWidget_2) - self.label_info.setObjectName("label_info") + + self.add_layout = QHBoxLayout() + self.add_layout.setObjectName(u"add_layout") + self.label_info = QLabel(self.gridLayoutWidget_2) + self.label_info.setObjectName(u"label_info") + self.add_layout.addWidget(self.label_info) - self.line_2 = QtWidgets.QFrame(parent=self.gridLayoutWidget_2) - self.line_2.setFrameShape(QtWidgets.QFrame.Shape.VLine) - self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) - self.line_2.setObjectName("line_2") + + self.line_2 = QFrame(self.gridLayoutWidget_2) + self.line_2.setObjectName(u"line_2") + self.line_2.setFrameShape(QFrame.Shape.VLine) + self.line_2.setFrameShadow(QFrame.Shadow.Sunken) + self.add_layout.addWidget(self.line_2) - self.progress_label = QtWidgets.QLabel(parent=self.gridLayoutWidget_2) - self.progress_label.setObjectName("progress_label") + + self.progress_label = QLabel(self.gridLayoutWidget_2) + self.progress_label.setObjectName(u"progress_label") + self.add_layout.addWidget(self.progress_label) + + self.horizontalLayout_5.addLayout(self.add_layout) - spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_5.addItem(spacerItem4) - self.avail_layout = QtWidgets.QHBoxLayout() - self.avail_layout.setObjectName("avail_layout") + + self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_5.addItem(self.horizontalSpacer_4) + + self.avail_layout = QHBoxLayout() + self.avail_layout.setObjectName(u"avail_layout") + self.horizontalLayout_5.addLayout(self.avail_layout) - self.label_20 = QtWidgets.QLabel(parent=self.gridLayoutWidget_2) - self.label_20.setObjectName("label_20") + + self.label_20 = QLabel(self.gridLayoutWidget_2) + self.label_20.setObjectName(u"label_20") + self.horizontalLayout_5.addWidget(self.label_20) - self.line_3 = QtWidgets.QFrame(parent=self.gridLayoutWidget_2) - self.line_3.setFrameShape(QtWidgets.QFrame.Shape.VLine) - self.line_3.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) - self.line_3.setObjectName("line_3") + + self.line_3 = QFrame(self.gridLayoutWidget_2) + self.line_3.setObjectName(u"line_3") + self.line_3.setFrameShape(QFrame.Shape.VLine) + self.line_3.setFrameShadow(QFrame.Shadow.Sunken) + self.horizontalLayout_5.addWidget(self.line_3) - self.avail_status = QtWidgets.QLabel(parent=self.gridLayoutWidget_2) - self.avail_status.setObjectName("avail_status") + + self.avail_status = QLabel(self.gridLayoutWidget_2) + self.avail_status.setObjectName(u"avail_status") + self.horizontalLayout_5.addWidget(self.avail_status) - self.automation_add_selected_books = QtWidgets.QPushButton(parent=self.gridLayoutWidget_2) - self.automation_add_selected_books.setObjectName("automation_add_selected_books") + + self.automation_add_selected_books = QPushButton(self.gridLayoutWidget_2) + self.automation_add_selected_books.setObjectName(u"automation_add_selected_books") + self.horizontalLayout_5.addWidget(self.automation_add_selected_books) - spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_5.addItem(spacerItem5) + + self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_5.addItem(self.horizontalSpacer_2) + + self.gridLayout_2.addLayout(self.horizontalLayout_5, 4, 0, 1, 1) - self.tableWidget_apparat_media = QtWidgets.QTableWidget(parent=self.gridLayoutWidget_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tableWidget_apparat_media.sizePolicy().hasHeightForWidth()) - self.tableWidget_apparat_media.setSizePolicy(sizePolicy) - self.tableWidget_apparat_media.setMinimumSize(QtCore.QSize(1259, 0)) - self.tableWidget_apparat_media.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.tableWidget_apparat_media.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu) - self.tableWidget_apparat_media.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) - self.tableWidget_apparat_media.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + + self.tableWidget_apparat_media = QTableWidget(self.gridLayoutWidget_2) + if (self.tableWidget_apparat_media.columnCount() < 7): + self.tableWidget_apparat_media.setColumnCount(7) + __qtablewidgetitem6 = QTableWidgetItem() + self.tableWidget_apparat_media.setHorizontalHeaderItem(0, __qtablewidgetitem6) + __qtablewidgetitem7 = QTableWidgetItem() + self.tableWidget_apparat_media.setHorizontalHeaderItem(1, __qtablewidgetitem7) + __qtablewidgetitem8 = QTableWidgetItem() + self.tableWidget_apparat_media.setHorizontalHeaderItem(2, __qtablewidgetitem8) + __qtablewidgetitem9 = QTableWidgetItem() + self.tableWidget_apparat_media.setHorizontalHeaderItem(3, __qtablewidgetitem9) + __qtablewidgetitem10 = QTableWidgetItem() + self.tableWidget_apparat_media.setHorizontalHeaderItem(4, __qtablewidgetitem10) + __qtablewidgetitem11 = QTableWidgetItem() + self.tableWidget_apparat_media.setHorizontalHeaderItem(5, __qtablewidgetitem11) + __qtablewidgetitem12 = QTableWidgetItem() + self.tableWidget_apparat_media.setHorizontalHeaderItem(6, __qtablewidgetitem12) + self.tableWidget_apparat_media.setObjectName(u"tableWidget_apparat_media") + sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding) + sizePolicy2.setHorizontalStretch(0) + sizePolicy2.setVerticalStretch(0) + sizePolicy2.setHeightForWidth(self.tableWidget_apparat_media.sizePolicy().hasHeightForWidth()) + self.tableWidget_apparat_media.setSizePolicy(sizePolicy2) + self.tableWidget_apparat_media.setMinimumSize(QSize(1259, 0)) + self.tableWidget_apparat_media.setFocusPolicy(Qt.NoFocus) + self.tableWidget_apparat_media.setContextMenuPolicy(Qt.CustomContextMenu) + self.tableWidget_apparat_media.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) + self.tableWidget_apparat_media.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableWidget_apparat_media.setAlternatingRowColors(True) - self.tableWidget_apparat_media.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) - self.tableWidget_apparat_media.setObjectName("tableWidget_apparat_media") - self.tableWidget_apparat_media.setColumnCount(7) - self.tableWidget_apparat_media.setRowCount(0) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparat_media.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparat_media.setHorizontalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparat_media.setHorizontalHeaderItem(2, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparat_media.setHorizontalHeaderItem(3, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparat_media.setHorizontalHeaderItem(4, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparat_media.setHorizontalHeaderItem(5, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget_apparat_media.setHorizontalHeaderItem(6, item) + self.tableWidget_apparat_media.setSelectionBehavior(QAbstractItemView.SelectRows) + self.tableWidget_apparat_media.setSortingEnabled(True) self.tableWidget_apparat_media.horizontalHeader().setCascadingSectionResizes(True) + self.gridLayout_2.addWidget(self.tableWidget_apparat_media, 9, 0, 1, 1) - self.label = QtWidgets.QLabel(parent=self.gridLayoutWidget_2) - font = QtGui.QFont() + + self.label = QLabel(self.gridLayoutWidget_2) + self.label.setObjectName(u"label") + font = QFont() font.setPointSize(11) font.setBold(True) self.label.setFont(font) - self.label.setObjectName("label") + self.gridLayout_2.addWidget(self.label, 2, 0, 1, 1) - self.app_group_box = QtWidgets.QGroupBox(parent=self.gridLayoutWidget_2) + + self.app_group_box = QGroupBox(self.gridLayoutWidget_2) + self.app_group_box.setObjectName(u"app_group_box") self.app_group_box.setEnabled(True) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.app_group_box.sizePolicy().hasHeightForWidth()) - self.app_group_box.setSizePolicy(sizePolicy) - self.app_group_box.setMinimumSize(QtCore.QSize(0, 210)) - font = QtGui.QFont() - font.setPointSize(12) - font.setBold(True) - self.app_group_box.setFont(font) - self.app_group_box.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.app_group_box.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignVCenter) + sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) + sizePolicy3.setHorizontalStretch(0) + sizePolicy3.setVerticalStretch(0) + sizePolicy3.setHeightForWidth(self.app_group_box.sizePolicy().hasHeightForWidth()) + self.app_group_box.setSizePolicy(sizePolicy3) + self.app_group_box.setMinimumSize(QSize(0, 210)) + font1 = QFont() + font1.setPointSize(12) + font1.setBold(True) + self.app_group_box.setFont(font1) + self.app_group_box.setFocusPolicy(Qt.NoFocus) + self.app_group_box.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter) self.app_group_box.setCheckable(False) - self.app_group_box.setObjectName("app_group_box") - self.document_list = QtWidgets.QTableWidget(parent=self.app_group_box) - self.document_list.setGeometry(QtCore.QRect(780, 20, 321, 181)) - font = QtGui.QFont() - font.setPointSize(10) - font.setBold(False) - font.setKerning(False) - self.document_list.setFont(font) - self.document_list.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + self.document_list = QTableWidget(self.app_group_box) + if (self.document_list.columnCount() < 4): + self.document_list.setColumnCount(4) + font2 = QFont() + font2.setFamilies([u"Arial"]) + font2.setPointSize(8) + __qtablewidgetitem13 = QTableWidgetItem() + __qtablewidgetitem13.setFont(font2); + self.document_list.setHorizontalHeaderItem(0, __qtablewidgetitem13) + __qtablewidgetitem14 = QTableWidgetItem() + __qtablewidgetitem14.setFont(font2); + self.document_list.setHorizontalHeaderItem(1, __qtablewidgetitem14) + __qtablewidgetitem15 = QTableWidgetItem() + __qtablewidgetitem15.setFont(font2); + self.document_list.setHorizontalHeaderItem(2, __qtablewidgetitem15) + __qtablewidgetitem16 = QTableWidgetItem() + self.document_list.setHorizontalHeaderItem(3, __qtablewidgetitem16) + self.document_list.setObjectName(u"document_list") + self.document_list.setGeometry(QRect(780, 20, 321, 181)) + font3 = QFont() + font3.setPointSize(10) + font3.setBold(False) + font3.setKerning(False) + self.document_list.setFont(font3) + self.document_list.setFocusPolicy(Qt.NoFocus) self.document_list.setAcceptDrops(True) - self.document_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) - self.document_list.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) + self.document_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.document_list.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) self.document_list.setDragEnabled(True) - self.document_list.setDragDropMode(QtWidgets.QAbstractItemView.DragDropMode.DragOnly) - self.document_list.setDefaultDropAction(QtCore.Qt.DropAction.LinkAction) - self.document_list.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.SingleSelection) - self.document_list.setObjectName("document_list") - self.document_list.setColumnCount(4) - self.document_list.setRowCount(0) - item = QtWidgets.QTableWidgetItem() - font = QtGui.QFont() - font.setFamily("Arial") - font.setPointSize(8) - item.setFont(font) - self.document_list.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - font = QtGui.QFont() - font.setFamily("Arial") - font.setPointSize(8) - item.setFont(font) - self.document_list.setHorizontalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - font = QtGui.QFont() - font.setFamily("Arial") - font.setPointSize(8) - item.setFont(font) - self.document_list.setHorizontalHeaderItem(2, item) - item = QtWidgets.QTableWidgetItem() - self.document_list.setHorizontalHeaderItem(3, item) + self.document_list.setDragDropMode(QAbstractItemView.DragOnly) + self.document_list.setDefaultDropAction(Qt.LinkAction) + self.document_list.setSelectionMode(QAbstractItemView.SingleSelection) self.document_list.horizontalHeader().setDefaultSectionSize(107) - self.appname_mand = QtWidgets.QLabel(parent=self.app_group_box) - self.appname_mand.setGeometry(QtCore.QRect(330, 50, 16, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.appname_mand.setFont(font) - self.appname_mand.setObjectName("appname_mand") - self.profname_mand = QtWidgets.QLabel(parent=self.app_group_box) - self.profname_mand.setGeometry(QtCore.QRect(110, 110, 16, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.profname_mand.setFont(font) - self.profname_mand.setObjectName("profname_mand") - self.prof_title = QtWidgets.QLineEdit(parent=self.app_group_box) - self.prof_title.setGeometry(QtCore.QRect(120, 80, 71, 20)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.prof_title.setFont(font) - self.prof_title.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus) - self.prof_title.setObjectName("prof_title") - self.fach_mand = QtWidgets.QLabel(parent=self.app_group_box) - self.fach_mand.setGeometry(QtCore.QRect(510, 50, 47, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.fach_mand.setFont(font) - self.fach_mand.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.fach_mand.setObjectName("fach_mand") - self.btn_apparat_apply = QtWidgets.QPushButton(parent=self.app_group_box) - self.btn_apparat_apply.setGeometry(QtCore.QRect(360, 150, 75, 23)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.btn_apparat_apply.setFont(font) - self.btn_apparat_apply.setObjectName("btn_apparat_apply") - self.label_9 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_9.setGeometry(QtCore.QRect(20, 160, 71, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_9.setFont(font) - self.label_9.setObjectName("label_9") - self.gridLayoutWidget_5 = QtWidgets.QWidget(parent=self.app_group_box) - self.gridLayoutWidget_5.setGeometry(QtCore.QRect(520, 30, 241, 61)) - self.gridLayoutWidget_5.setObjectName("gridLayoutWidget_5") - self.gridLayout_6 = QtWidgets.QGridLayout(self.gridLayoutWidget_5) + self.appname_mand = QLabel(self.app_group_box) + self.appname_mand.setObjectName(u"appname_mand") + self.appname_mand.setGeometry(QRect(330, 50, 16, 21)) + font4 = QFont() + font4.setPointSize(9) + font4.setBold(False) + self.appname_mand.setFont(font4) + self.profname_mand = QLabel(self.app_group_box) + self.profname_mand.setObjectName(u"profname_mand") + self.profname_mand.setGeometry(QRect(110, 110, 16, 21)) + self.profname_mand.setFont(font4) + self.prof_title = QLineEdit(self.app_group_box) + self.prof_title.setObjectName(u"prof_title") + self.prof_title.setGeometry(QRect(120, 80, 71, 20)) + self.prof_title.setFont(font4) + self.prof_title.setFocusPolicy(Qt.ClickFocus) + self.fach_mand = QLabel(self.app_group_box) + self.fach_mand.setObjectName(u"fach_mand") + self.fach_mand.setGeometry(QRect(510, 50, 47, 21)) + self.fach_mand.setFont(font4) + self.fach_mand.setFocusPolicy(Qt.NoFocus) + self.btn_apparat_apply = QPushButton(self.app_group_box) + self.btn_apparat_apply.setObjectName(u"btn_apparat_apply") + self.btn_apparat_apply.setGeometry(QRect(360, 150, 75, 23)) + self.btn_apparat_apply.setFont(font4) + self.label_9 = QLabel(self.app_group_box) + self.label_9.setObjectName(u"label_9") + self.label_9.setGeometry(QRect(20, 160, 71, 21)) + self.label_9.setFont(font4) + self.gridLayoutWidget_5 = QWidget(self.app_group_box) + self.gridLayoutWidget_5.setObjectName(u"gridLayoutWidget_5") + self.gridLayoutWidget_5.setGeometry(QRect(520, 30, 241, 61)) + self.gridLayout_6 = QGridLayout(self.gridLayoutWidget_5) + self.gridLayout_6.setObjectName(u"gridLayout_6") self.gridLayout_6.setContentsMargins(0, 0, 0, 0) - self.gridLayout_6.setObjectName("gridLayout_6") - self.app_fach = QtWidgets.QComboBox(parent=self.gridLayoutWidget_5) - self.app_fach.setMaximumSize(QtCore.QSize(16777215, 25)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.app_fach.setFont(font) + self.app_fach = QComboBox(self.gridLayoutWidget_5) + self.app_fach.setObjectName(u"app_fach") + self.app_fach.setMaximumSize(QSize(16777215, 25)) + self.app_fach.setFont(font4) self.app_fach.setEditable(True) - self.app_fach.setObjectName("app_fach") + self.gridLayout_6.addWidget(self.app_fach, 0, 1, 1, 1) - spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.gridLayout_6.addItem(spacerItem6, 0, 3, 1, 1) - self.valid_check_app_fach = QtWidgets.QToolButton(parent=self.gridLayoutWidget_5) - self.valid_check_app_fach.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.valid_check_app_fach.setText("") + + self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.gridLayout_6.addItem(self.horizontalSpacer_7, 0, 3, 1, 1) + + self.valid_check_app_fach = QToolButton(self.gridLayoutWidget_5) + self.valid_check_app_fach.setObjectName(u"valid_check_app_fach") + self.valid_check_app_fach.setFocusPolicy(Qt.NoFocus) self.valid_check_app_fach.setAutoRaise(True) - self.valid_check_app_fach.setArrowType(QtCore.Qt.ArrowType.NoArrow) - self.valid_check_app_fach.setObjectName("valid_check_app_fach") + self.valid_check_app_fach.setArrowType(Qt.NoArrow) + self.gridLayout_6.addWidget(self.valid_check_app_fach, 0, 2, 1, 1) - self._mand = QtWidgets.QLabel(parent=self.app_group_box) - self._mand.setGeometry(QtCore.QRect(330, 90, 16, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self._mand.setFont(font) - self._mand.setObjectName("_mand") - self.prof_tel_nr = QtWidgets.QLineEdit(parent=self.app_group_box) - self.prof_tel_nr.setGeometry(QtCore.QRect(120, 160, 121, 20)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.prof_tel_nr.setFont(font) - self.prof_tel_nr.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhNone) - self.prof_tel_nr.setPlaceholderText("") - self.prof_tel_nr.setObjectName("prof_tel_nr") - self.check_eternal_app = QtWidgets.QCheckBox(parent=self.app_group_box) + + self._mand = QLabel(self.app_group_box) + self._mand.setObjectName(u"_mand") + self._mand.setGeometry(QRect(330, 90, 16, 21)) + self._mand.setFont(font4) + self.prof_tel_nr = QLineEdit(self.app_group_box) + self.prof_tel_nr.setObjectName(u"prof_tel_nr") + self.prof_tel_nr.setGeometry(QRect(120, 160, 121, 20)) + self.prof_tel_nr.setFont(font4) + self.prof_tel_nr.setInputMethodHints(Qt.ImhNone) + self.check_eternal_app = QCheckBox(self.app_group_box) + self.check_eternal_app.setObjectName(u"check_eternal_app") self.check_eternal_app.setEnabled(False) - self.check_eternal_app.setGeometry(QtCore.QRect(340, 120, 101, 17)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.check_eternal_app.setFont(font) - self.check_eternal_app.setObjectName("check_eternal_app") - self.sem_sommer = QtWidgets.QCheckBox(parent=self.app_group_box) - self.sem_sommer.setGeometry(QtCore.QRect(340, 100, 82, 17)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.sem_sommer.setFont(font) - self.sem_sommer.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) - self.sem_sommer.setObjectName("sem_sommer") - self.drpdwn_prof_name = QtWidgets.QComboBox(parent=self.app_group_box) - self.drpdwn_prof_name.setGeometry(QtCore.QRect(120, 110, 121, 22)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.drpdwn_prof_name.setFont(font) - self.drpdwn_prof_name.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) - self.drpdwn_prof_name.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhNone) + self.check_eternal_app.setGeometry(QRect(340, 120, 101, 17)) + self.check_eternal_app.setFont(font4) + self.sem_sommer = QCheckBox(self.app_group_box) + self.sem_sommer.setObjectName(u"sem_sommer") + self.sem_sommer.setGeometry(QRect(340, 100, 82, 17)) + self.sem_sommer.setFont(font4) + self.sem_sommer.setFocusPolicy(Qt.StrongFocus) + self.drpdwn_prof_name = QComboBox(self.app_group_box) + self.drpdwn_prof_name.setObjectName(u"drpdwn_prof_name") + self.drpdwn_prof_name.setGeometry(QRect(120, 110, 121, 22)) + self.drpdwn_prof_name.setFont(font4) + self.drpdwn_prof_name.setFocusPolicy(Qt.StrongFocus) + self.drpdwn_prof_name.setInputMethodHints(Qt.ImhNone) self.drpdwn_prof_name.setEditable(True) - self.drpdwn_prof_name.setInsertPolicy(QtWidgets.QComboBox.InsertPolicy.InsertAlphabetically) - self.drpdwn_prof_name.setPlaceholderText("") + self.drpdwn_prof_name.setInsertPolicy(QComboBox.InsertAlphabetically) self.drpdwn_prof_name.setFrame(True) - self.drpdwn_prof_name.setObjectName("drpdwn_prof_name") - self.mail_mand = QtWidgets.QLabel(parent=self.app_group_box) - self.mail_mand.setGeometry(QtCore.QRect(110, 140, 47, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.mail_mand.setFont(font) - self.mail_mand.setObjectName("mail_mand") - self.label_3 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_3.setGeometry(QtCore.QRect(20, 80, 61, 20)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_3.setFont(font) - self.label_3.setObjectName("label_3") - self.label_2 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_2.setGeometry(QtCore.QRect(20, 50, 101, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_2.setFont(font) - self.label_2.setObjectName("label_2") - self.label_8 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_8.setGeometry(QtCore.QRect(20, 140, 71, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_8.setFont(font) - self.label_8.setObjectName("label_8") - self.label_10 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_10.setGeometry(QtCore.QRect(480, 50, 51, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_10.setFont(font) - self.label_10.setObjectName("label_10") - self.prof_mail = QtWidgets.QLineEdit(parent=self.app_group_box) - self.prof_mail.setGeometry(QtCore.QRect(120, 140, 121, 20)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.prof_mail.setFont(font) - self.prof_mail.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhEmailCharactersOnly) + self.mail_mand = QLabel(self.app_group_box) + self.mail_mand.setObjectName(u"mail_mand") + self.mail_mand.setGeometry(QRect(110, 140, 47, 21)) + self.mail_mand.setFont(font4) + self.label_3 = QLabel(self.app_group_box) + self.label_3.setObjectName(u"label_3") + self.label_3.setGeometry(QRect(20, 80, 61, 20)) + self.label_3.setFont(font4) + self.label_2 = QLabel(self.app_group_box) + self.label_2.setObjectName(u"label_2") + self.label_2.setGeometry(QRect(20, 50, 101, 21)) + self.label_2.setFont(font4) + self.label_8 = QLabel(self.app_group_box) + self.label_8.setObjectName(u"label_8") + self.label_8.setGeometry(QRect(20, 140, 71, 21)) + self.label_8.setFont(font4) + self.label_10 = QLabel(self.app_group_box) + self.label_10.setObjectName(u"label_10") + self.label_10.setGeometry(QRect(480, 50, 51, 21)) + self.label_10.setFont(font4) + self.prof_mail = QLineEdit(self.app_group_box) + self.prof_mail.setObjectName(u"prof_mail") + self.prof_mail.setGeometry(QRect(120, 140, 121, 20)) + self.prof_mail.setFont(font4) + self.prof_mail.setInputMethodHints(Qt.ImhEmailCharactersOnly) self.prof_mail.setMaxLength(200) - self.prof_mail.setPlaceholderText("") - self.prof_mail.setObjectName("prof_mail") - self.formLayoutWidget_2 = QtWidgets.QWidget(parent=self.app_group_box) - self.formLayoutWidget_2.setGeometry(QtCore.QRect(560, 100, 211, 99)) - self.formLayoutWidget_2.setObjectName("formLayoutWidget_2") - self.formLayout_3 = QtWidgets.QFormLayout(self.formLayoutWidget_2) + self.formLayoutWidget_2 = QWidget(self.app_group_box) + self.formLayoutWidget_2.setObjectName(u"formLayoutWidget_2") + self.formLayoutWidget_2.setGeometry(QRect(560, 100, 211, 99)) + self.formLayout_3 = QFormLayout(self.formLayoutWidget_2) + self.formLayout_3.setObjectName(u"formLayout_3") self.formLayout_3.setContentsMargins(0, 0, 0, 0) - self.formLayout_3.setObjectName("formLayout_3") - self.label_12 = QtWidgets.QLabel(parent=self.formLayoutWidget_2) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_12.setFont(font) - self.label_12.setObjectName("label_12") - self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_12) - self.prof_id_adis = QtWidgets.QLineEdit(parent=self.formLayoutWidget_2) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.prof_id_adis.setFont(font) - self.prof_id_adis.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhPreferNumbers) - self.prof_id_adis.setText("") - self.prof_id_adis.setObjectName("prof_id_adis") - self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.prof_id_adis) - self.label_13 = QtWidgets.QLabel(parent=self.formLayoutWidget_2) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_13.setFont(font) - self.label_13.setObjectName("label_13") - self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_13) - self.apparat_id_adis = QtWidgets.QLineEdit(parent=self.formLayoutWidget_2) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.apparat_id_adis.setFont(font) - self.apparat_id_adis.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhPreferNumbers) - self.apparat_id_adis.setObjectName("apparat_id_adis") - self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.apparat_id_adis) - self.sem_year = QtWidgets.QLineEdit(parent=self.app_group_box) - self.sem_year.setGeometry(QtCore.QRect(410, 90, 113, 20)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.sem_year.setFont(font) - self.sem_year.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) + self.label_12 = QLabel(self.formLayoutWidget_2) + self.label_12.setObjectName(u"label_12") + self.label_12.setFont(font4) + + self.formLayout_3.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_12) + + self.prof_id_adis = QLineEdit(self.formLayoutWidget_2) + self.prof_id_adis.setObjectName(u"prof_id_adis") + self.prof_id_adis.setFont(font4) + self.prof_id_adis.setInputMethodHints(Qt.ImhPreferNumbers) + + self.formLayout_3.setWidget(0, QFormLayout.ItemRole.FieldRole, self.prof_id_adis) + + self.label_13 = QLabel(self.formLayoutWidget_2) + self.label_13.setObjectName(u"label_13") + self.label_13.setFont(font4) + + self.formLayout_3.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_13) + + self.apparat_id_adis = QLineEdit(self.formLayoutWidget_2) + self.apparat_id_adis.setObjectName(u"apparat_id_adis") + self.apparat_id_adis.setFont(font4) + self.apparat_id_adis.setInputMethodHints(Qt.ImhPreferNumbers) + + self.formLayout_3.setWidget(1, QFormLayout.ItemRole.FieldRole, self.apparat_id_adis) + + self.sem_year = QLineEdit(self.app_group_box) + self.sem_year.setObjectName(u"sem_year") + self.sem_year.setGeometry(QRect(410, 90, 113, 20)) + self.sem_year.setFont(font4) + self.sem_year.setFocusPolicy(Qt.StrongFocus) self.sem_year.setMaxLength(5) - self.sem_year.setObjectName("sem_year") - self.check_send_mail = QtWidgets.QCheckBox(parent=self.app_group_box) - self.check_send_mail.setGeometry(QtCore.QRect(450, 150, 91, 24)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.check_send_mail.setFont(font) - self.check_send_mail.setObjectName("check_send_mail") - self.sem_winter = QtWidgets.QCheckBox(parent=self.app_group_box) - self.sem_winter.setGeometry(QtCore.QRect(340, 80, 82, 17)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.sem_winter.setFont(font) - self.sem_winter.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) - self.sem_winter.setObjectName("sem_winter") - self.label_4 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_4.setGeometry(QtCore.QRect(20, 110, 71, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_4.setFont(font) - self.label_4.setObjectName("label_4") - self.telnr_mand = QtWidgets.QLabel(parent=self.app_group_box) - self.telnr_mand.setGeometry(QtCore.QRect(110, 160, 47, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.telnr_mand.setFont(font) - self.telnr_mand.setObjectName("telnr_mand") - self.btn_apparat_save = QtWidgets.QPushButton(parent=self.app_group_box) - self.btn_apparat_save.setGeometry(QtCore.QRect(270, 150, 75, 23)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.btn_apparat_save.setFont(font) - self.btn_apparat_save.setStatusTip("") - self.btn_apparat_save.setObjectName("btn_apparat_save") - self.label_5 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_5.setGeometry(QtCore.QRect(250, 50, 91, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_5.setFont(font) - self.label_5.setObjectName("label_5") - self.app_name = QtWidgets.QLineEdit(parent=self.app_group_box) - self.app_name.setGeometry(QtCore.QRect(340, 50, 113, 20)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.app_name.setFont(font) - self.app_name.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) - self.app_name.setObjectName("app_name") - self.drpdwn_app_nr = QtWidgets.QComboBox(parent=self.app_group_box) - self.drpdwn_app_nr.setGeometry(QtCore.QRect(120, 50, 69, 22)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.drpdwn_app_nr.setFont(font) - self.drpdwn_app_nr.setInputMethodHints(QtCore.Qt.InputMethodHint.ImhNone) + self.check_send_mail = QCheckBox(self.app_group_box) + self.check_send_mail.setObjectName(u"check_send_mail") + self.check_send_mail.setGeometry(QRect(450, 150, 91, 24)) + self.check_send_mail.setFont(font4) + self.sem_winter = QCheckBox(self.app_group_box) + self.sem_winter.setObjectName(u"sem_winter") + self.sem_winter.setGeometry(QRect(340, 80, 82, 17)) + self.sem_winter.setFont(font4) + self.sem_winter.setFocusPolicy(Qt.StrongFocus) + self.label_4 = QLabel(self.app_group_box) + self.label_4.setObjectName(u"label_4") + self.label_4.setGeometry(QRect(20, 110, 71, 21)) + self.label_4.setFont(font4) + self.telnr_mand = QLabel(self.app_group_box) + self.telnr_mand.setObjectName(u"telnr_mand") + self.telnr_mand.setGeometry(QRect(110, 160, 47, 21)) + self.telnr_mand.setFont(font4) + self.btn_apparat_save = QPushButton(self.app_group_box) + self.btn_apparat_save.setObjectName(u"btn_apparat_save") + self.btn_apparat_save.setGeometry(QRect(270, 150, 75, 23)) + self.btn_apparat_save.setFont(font4) + self.label_5 = QLabel(self.app_group_box) + self.label_5.setObjectName(u"label_5") + self.label_5.setGeometry(QRect(250, 50, 91, 21)) + self.label_5.setFont(font4) + self.app_name = QLineEdit(self.app_group_box) + self.app_name.setObjectName(u"app_name") + self.app_name.setGeometry(QRect(340, 50, 113, 20)) + self.app_name.setFont(font4) + self.app_name.setFocusPolicy(Qt.StrongFocus) + self.drpdwn_app_nr = QComboBox(self.app_group_box) + self.drpdwn_app_nr.setObjectName(u"drpdwn_app_nr") + self.drpdwn_app_nr.setGeometry(QRect(120, 50, 69, 22)) + self.drpdwn_app_nr.setFont(font4) + self.drpdwn_app_nr.setInputMethodHints(Qt.ImhNone) self.drpdwn_app_nr.setEditable(True) - self.drpdwn_app_nr.setObjectName("drpdwn_app_nr") - self.label_6 = QtWidgets.QLabel(parent=self.app_group_box) - self.label_6.setGeometry(QtCore.QRect(270, 90, 61, 21)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.label_6.setFont(font) - self.label_6.setObjectName("label_6") - self.valid_check_profname = QtWidgets.QToolButton(parent=self.app_group_box) - self.valid_check_profname.setGeometry(QtCore.QRect(240, 110, 23, 22)) - self.valid_check_profname.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.valid_check_profname.setText("") + self.label_6 = QLabel(self.app_group_box) + self.label_6.setObjectName(u"label_6") + self.label_6.setGeometry(QRect(270, 90, 61, 21)) + self.label_6.setFont(font4) + self.valid_check_profname = QToolButton(self.app_group_box) + self.valid_check_profname.setObjectName(u"valid_check_profname") + self.valid_check_profname.setGeometry(QRect(240, 110, 23, 22)) + self.valid_check_profname.setFocusPolicy(Qt.NoFocus) self.valid_check_profname.setAutoRaise(True) - self.valid_check_profname.setArrowType(QtCore.Qt.ArrowType.NoArrow) - self.valid_check_profname.setObjectName("valid_check_profname") - self.valid_check_appname = QtWidgets.QToolButton(parent=self.app_group_box) - self.valid_check_appname.setGeometry(QtCore.QRect(450, 50, 22, 22)) - self.valid_check_appname.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.valid_check_appname.setText("") + self.valid_check_profname.setArrowType(Qt.NoArrow) + self.valid_check_appname = QToolButton(self.app_group_box) + self.valid_check_appname.setObjectName(u"valid_check_appname") + self.valid_check_appname.setGeometry(QRect(450, 50, 22, 22)) + self.valid_check_appname.setFocusPolicy(Qt.NoFocus) self.valid_check_appname.setAutoRaise(True) - self.valid_check_appname.setObjectName("valid_check_appname") - self.valid_check_semester = QtWidgets.QToolButton(parent=self.app_group_box) - self.valid_check_semester.setGeometry(QtCore.QRect(520, 90, 22, 22)) - self.valid_check_semester.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.valid_check_semester.setText("") + self.valid_check_semester = QToolButton(self.app_group_box) + self.valid_check_semester.setObjectName(u"valid_check_semester") + self.valid_check_semester.setGeometry(QRect(520, 90, 22, 22)) + self.valid_check_semester.setFocusPolicy(Qt.NoFocus) self.valid_check_semester.setAutoRaise(True) - self.valid_check_semester.setObjectName("valid_check_semester") - self.valid_check_mail = QtWidgets.QToolButton(parent=self.app_group_box) - self.valid_check_mail.setGeometry(QtCore.QRect(240, 140, 22, 22)) - self.valid_check_mail.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.valid_check_mail.setText("") + self.valid_check_mail = QToolButton(self.app_group_box) + self.valid_check_mail.setObjectName(u"valid_check_mail") + self.valid_check_mail.setGeometry(QRect(240, 140, 22, 22)) + self.valid_check_mail.setFocusPolicy(Qt.NoFocus) self.valid_check_mail.setAutoRaise(True) - self.valid_check_mail.setObjectName("valid_check_mail") - self.valid_check_telnr = QtWidgets.QToolButton(parent=self.app_group_box) - self.valid_check_telnr.setGeometry(QtCore.QRect(240, 160, 22, 22)) - self.valid_check_telnr.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.valid_check_telnr.setText("") + self.valid_check_telnr = QToolButton(self.app_group_box) + self.valid_check_telnr.setObjectName(u"valid_check_telnr") + self.valid_check_telnr.setGeometry(QRect(240, 160, 22, 22)) + self.valid_check_telnr.setFocusPolicy(Qt.NoFocus) self.valid_check_telnr.setAutoRaise(True) - self.valid_check_telnr.setObjectName("valid_check_telnr") - self.saveandcreate = QtWidgets.QPushButton(parent=self.app_group_box) + self.saveandcreate = QPushButton(self.app_group_box) + self.saveandcreate.setObjectName(u"saveandcreate") self.saveandcreate.setEnabled(False) - self.saveandcreate.setGeometry(QtCore.QRect(270, 180, 161, 24)) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.saveandcreate.setFont(font) - self.saveandcreate.setObjectName("saveandcreate") - self.verticalLayoutWidget_3 = QtWidgets.QWidget(parent=self.app_group_box) - self.verticalLayoutWidget_3.setGeometry(QtCore.QRect(1110, 17, 131, 181)) - self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3") - self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3) + self.saveandcreate.setGeometry(QRect(270, 180, 161, 24)) + self.saveandcreate.setFont(font4) + self.verticalLayoutWidget_3 = QWidget(self.app_group_box) + self.verticalLayoutWidget_3.setObjectName(u"verticalLayoutWidget_3") + self.verticalLayoutWidget_3.setGeometry(QRect(1110, 17, 131, 181)) + self.verticalLayout_8 = QVBoxLayout(self.verticalLayoutWidget_3) + self.verticalLayout_8.setObjectName(u"verticalLayout_8") self.verticalLayout_8.setContentsMargins(0, 0, 0, 0) - self.verticalLayout_8.setObjectName("verticalLayout_8") - self.btn_add_document = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_3) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.btn_add_document.sizePolicy().hasHeightForWidth()) - self.btn_add_document.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.btn_add_document.setFont(font) - self.btn_add_document.setObjectName("btn_add_document") + self.btn_add_document = QPushButton(self.verticalLayoutWidget_3) + self.btn_add_document.setObjectName(u"btn_add_document") + sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.MinimumExpanding) + sizePolicy4.setHorizontalStretch(0) + sizePolicy4.setVerticalStretch(0) + sizePolicy4.setHeightForWidth(self.btn_add_document.sizePolicy().hasHeightForWidth()) + self.btn_add_document.setSizePolicy(sizePolicy4) + self.btn_add_document.setFont(font4) + self.verticalLayout_8.addWidget(self.btn_add_document) - self.btn_open_document = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_3) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.btn_open_document.sizePolicy().hasHeightForWidth()) - self.btn_open_document.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.btn_open_document.setFont(font) - self.btn_open_document.setObjectName("btn_open_document") + + self.btn_open_document = QPushButton(self.verticalLayoutWidget_3) + self.btn_open_document.setObjectName(u"btn_open_document") + sizePolicy4.setHeightForWidth(self.btn_open_document.sizePolicy().hasHeightForWidth()) + self.btn_open_document.setSizePolicy(sizePolicy4) + self.btn_open_document.setFont(font4) + self.verticalLayout_8.addWidget(self.btn_open_document) - self.check_file = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_3) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.check_file.sizePolicy().hasHeightForWidth()) - self.check_file.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.check_file.setFont(font) - self.check_file.setObjectName("check_file") + + self.check_file = QPushButton(self.verticalLayoutWidget_3) + self.check_file.setObjectName(u"check_file") + sizePolicy4.setHeightForWidth(self.check_file.sizePolicy().hasHeightForWidth()) + self.check_file.setSizePolicy(sizePolicy4) + self.check_file.setFont(font4) + self.verticalLayout_8.addWidget(self.check_file) - self.btn_extract_data_from_document = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_3) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.btn_extract_data_from_document.sizePolicy().hasHeightForWidth()) - self.btn_extract_data_from_document.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(9) - font.setBold(False) - self.btn_extract_data_from_document.setFont(font) - self.btn_extract_data_from_document.setObjectName("btn_extract_data_from_document") + + self.btn_extract_data_from_document = QPushButton(self.verticalLayoutWidget_3) + self.btn_extract_data_from_document.setObjectName(u"btn_extract_data_from_document") + sizePolicy4.setHeightForWidth(self.btn_extract_data_from_document.sizePolicy().hasHeightForWidth()) + self.btn_extract_data_from_document.setSizePolicy(sizePolicy4) + self.btn_extract_data_from_document.setFont(font4) + self.verticalLayout_8.addWidget(self.btn_extract_data_from_document) + self.verticalLayout_8.setStretch(0, 1) self.verticalLayout_8.setStretch(1, 1) self.verticalLayout_8.setStretch(2, 2) self.verticalLayout_8.setStretch(3, 2) + self.gridLayout_2.addWidget(self.app_group_box, 1, 0, 1, 1) - self.add_medium = QtWidgets.QPushButton(parent=self.createApparat) - self.add_medium.setGeometry(QtCore.QRect(3, 695, 121, 20)) - self.add_medium.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.add_medium.setObjectName("add_medium") + + self.add_medium = QPushButton(self.createApparat) + self.add_medium.setObjectName(u"add_medium") + self.add_medium.setGeometry(QRect(3, 695, 121, 20)) + self.add_medium.setFocusPolicy(Qt.NoFocus) self.tabWidget.addTab(self.createApparat, "") - self.search_statistics = QtWidgets.QWidget() - self.search_statistics.setObjectName("search_statistics") + self.search_statistics = QWidget() + self.search_statistics.setObjectName(u"search_statistics") self.tabWidget.addTab(self.search_statistics, "") - self.elsatab = QtWidgets.QWidget() - self.elsatab.setObjectName("elsatab") + self.elsatab = QWidget() + self.elsatab.setObjectName(u"elsatab") self.tabWidget.addTab(self.elsatab, "") - self.admin = QtWidgets.QWidget() - self.admin.setObjectName("admin") - self.label_21 = QtWidgets.QLabel(parent=self.admin) - self.label_21.setGeometry(QtCore.QRect(10, 30, 47, 22)) - self.label_21.setObjectName("label_21") - self.select_action_box = QtWidgets.QComboBox(parent=self.admin) - self.select_action_box.setGeometry(QtCore.QRect(60, 30, 181, 22)) - self.select_action_box.setObjectName("select_action_box") + self.admin = QWidget() + self.admin.setObjectName(u"admin") + self.label_21 = QLabel(self.admin) + self.label_21.setObjectName(u"label_21") + self.label_21.setGeometry(QRect(10, 30, 47, 22)) + self.select_action_box = QComboBox(self.admin) self.select_action_box.addItem("") self.select_action_box.addItem("") self.select_action_box.addItem("") - self.admin_action = QtWidgets.QGroupBox(parent=self.admin) - self.admin_action.setGeometry(QtCore.QRect(10, 70, 570, 291)) - font = QtGui.QFont() - font.setBold(False) - self.admin_action.setFont(font) + self.select_action_box.setObjectName(u"select_action_box") + self.select_action_box.setGeometry(QRect(60, 30, 181, 22)) + self.admin_action = QGroupBox(self.admin) + self.admin_action.setObjectName(u"admin_action") + self.admin_action.setGeometry(QRect(10, 70, 570, 291)) + font5 = QFont() + font5.setBold(False) + self.admin_action.setFont(font5) self.admin_action.setFlat(True) self.admin_action.setCheckable(False) - self.admin_action.setObjectName("admin_action") self.tabWidget.addTab(self.admin, "") + self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) + + self.horizontalLayout.addLayout(self.gridLayout) + + self.mainLayout.addLayout(self.horizontalLayout) - self.verticalLayoutWidget_2 = QtWidgets.QWidget(parent=self.centralwidget) - self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(1280, 0, 306, 751)) - self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2") - self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2) + + self.verticalLayoutWidget_2 = QWidget(self.centralwidget) + self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2") + self.verticalLayoutWidget_2.setGeometry(QRect(1280, 0, 306, 751)) + self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget_2) + self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setContentsMargins(0, 0, 0, 0) - self.verticalLayout.setObjectName("verticalLayout") - self.calendar_frame = QtWidgets.QFrame(parent=self.verticalLayoutWidget_2) - self.calendar_frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) - self.calendar_frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) - self.calendar_frame.setObjectName("calendar_frame") - self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.calendar_frame) - self.verticalLayout_7.setObjectName("verticalLayout_7") - self.calendarlayout = QtWidgets.QVBoxLayout() - self.calendarlayout.setObjectName("calendarlayout") + self.calendar_frame = QFrame(self.verticalLayoutWidget_2) + self.calendar_frame.setObjectName(u"calendar_frame") + self.calendar_frame.setFrameShape(QFrame.StyledPanel) + self.calendar_frame.setFrameShadow(QFrame.Raised) + self.verticalLayout_7 = QVBoxLayout(self.calendar_frame) + self.verticalLayout_7.setObjectName(u"verticalLayout_7") + self.calendarlayout = QVBoxLayout() + self.calendarlayout.setObjectName(u"calendarlayout") + self.verticalLayout_7.addLayout(self.calendarlayout) + + self.verticalLayout.addWidget(self.calendar_frame) - self.frame_creation_progress = QtWidgets.QFrame(parent=self.verticalLayoutWidget_2) - self.frame_creation_progress.setObjectName("frame_creation_progress") - self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame_creation_progress) + + self.frame_creation_progress = QFrame(self.verticalLayoutWidget_2) + self.frame_creation_progress.setObjectName(u"frame_creation_progress") + self.verticalLayout_4 = QVBoxLayout(self.frame_creation_progress) self.verticalLayout_4.setSpacing(6) - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.steps = QtWidgets.QFrame(parent=self.frame_creation_progress) - self.steps.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) - self.steps.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) - self.steps.setObjectName("steps") - self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.steps) + self.verticalLayout_4.setObjectName(u"verticalLayout_4") + self.steps = QFrame(self.frame_creation_progress) + self.steps.setObjectName(u"steps") + self.steps.setFrameShape(QFrame.StyledPanel) + self.steps.setFrameShadow(QFrame.Raised) + self.verticalLayout_3 = QVBoxLayout(self.steps) self.verticalLayout_3.setSpacing(0) - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.groupBox_2 = QtWidgets.QGroupBox(parent=self.steps) + self.verticalLayout_3.setObjectName(u"verticalLayout_3") + self.groupBox_2 = QGroupBox(self.steps) + self.groupBox_2.setObjectName(u"groupBox_2") self.groupBox_2.setEnabled(True) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth()) - self.groupBox_2.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(11) - font.setBold(True) + sizePolicy5 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + sizePolicy5.setHorizontalStretch(0) + sizePolicy5.setVerticalStretch(0) + sizePolicy5.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth()) + self.groupBox_2.setSizePolicy(sizePolicy5) self.groupBox_2.setFont(font) - self.groupBox_2.setObjectName("groupBox_2") - self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_2) - self.verticalLayout_6.setObjectName("verticalLayout_6") - self.appdata_check = QtWidgets.QCheckBox(parent=self.groupBox_2) - font = QtGui.QFont() - font.setPointSize(8) - font.setBold(False) - self.appdata_check.setFont(font) - self.appdata_check.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.appdata_check.setObjectName("appdata_check") + self.verticalLayout_6 = QVBoxLayout(self.groupBox_2) + self.verticalLayout_6.setObjectName(u"verticalLayout_6") + self.appdata_check = QCheckBox(self.groupBox_2) + self.appdata_check.setObjectName(u"appdata_check") + font6 = QFont() + font6.setPointSize(8) + font6.setBold(False) + self.appdata_check.setFont(font6) + self.appdata_check.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_6.addWidget(self.appdata_check) - self.media_check = QtWidgets.QCheckBox(parent=self.groupBox_2) - font = QtGui.QFont() - font.setPointSize(8) - font.setBold(False) - self.media_check.setFont(font) - self.media_check.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.media_check.setObjectName("media_check") + + self.media_check = QCheckBox(self.groupBox_2) + self.media_check.setObjectName(u"media_check") + self.media_check.setFont(font6) + self.media_check.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_6.addWidget(self.media_check) - self.ids_check = QtWidgets.QCheckBox(parent=self.groupBox_2) - font = QtGui.QFont() - font.setPointSize(8) - font.setBold(False) - self.ids_check.setFont(font) - self.ids_check.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.ids_check.setObjectName("ids_check") + + self.ids_check = QCheckBox(self.groupBox_2) + self.ids_check.setObjectName(u"ids_check") + self.ids_check.setFont(font6) + self.ids_check.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_6.addWidget(self.ids_check) + + self.verticalLayout_3.addWidget(self.groupBox_2) - self.groupBox = QtWidgets.QGroupBox(parent=self.steps) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth()) - self.groupBox.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(11) - font.setBold(True) + + self.groupBox = QGroupBox(self.steps) + self.groupBox.setObjectName(u"groupBox") + sizePolicy5.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth()) + self.groupBox.setSizePolicy(sizePolicy5) self.groupBox.setFont(font) - self.groupBox.setObjectName("groupBox") - self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox) - self.verticalLayout_5.setObjectName("verticalLayout_5") - self.media_checked = QtWidgets.QCheckBox(parent=self.groupBox) - font = QtGui.QFont() - font.setPointSize(8) - font.setBold(False) - font.setItalic(False) - font.setUnderline(False) - font.setKerning(True) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferDefault) - self.media_checked.setFont(font) - self.media_checked.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.media_checked.setObjectName("media_checked") + self.verticalLayout_5 = QVBoxLayout(self.groupBox) + self.verticalLayout_5.setObjectName(u"verticalLayout_5") + self.media_checked = QCheckBox(self.groupBox) + self.media_checked.setObjectName(u"media_checked") + font7 = QFont() + font7.setPointSize(8) + font7.setBold(False) + font7.setItalic(False) + font7.setUnderline(False) + font7.setKerning(True) + font7.setStyleStrategy(QFont.PreferDefault) + self.media_checked.setFont(font7) + self.media_checked.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_5.addWidget(self.media_checked) - self.media_edited_check = QtWidgets.QCheckBox(parent=self.groupBox) - font = QtGui.QFont() - font.setPointSize(8) - font.setBold(False) - font.setItalic(False) - font.setUnderline(False) - font.setKerning(True) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferDefault) - self.media_edited_check.setFont(font) - self.media_edited_check.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.media_edited_check.setObjectName("media_edited_check") + + self.media_edited_check = QCheckBox(self.groupBox) + self.media_edited_check.setObjectName(u"media_edited_check") + self.media_edited_check.setFont(font7) + self.media_edited_check.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_5.addWidget(self.media_edited_check) - self.app_created = QtWidgets.QCheckBox(parent=self.groupBox) - font = QtGui.QFont() - font.setPointSize(8) - font.setBold(False) - font.setItalic(False) - font.setUnderline(False) - font.setKerning(True) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferDefault) - self.app_created.setFont(font) - self.app_created.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.app_created.setObjectName("app_created") + + self.app_created = QCheckBox(self.groupBox) + self.app_created.setObjectName(u"app_created") + self.app_created.setFont(font7) + self.app_created.setFocusPolicy(Qt.NoFocus) + self.verticalLayout_5.addWidget(self.app_created) - self.btn_copy_adis_command = QtWidgets.QPushButton(parent=self.groupBox) - font = QtGui.QFont() - font.setPointSize(8) - font.setBold(False) - font.setItalic(False) - font.setUnderline(False) - font.setKerning(True) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferDefault) - self.btn_copy_adis_command.setFont(font) - self.btn_copy_adis_command.setStatusTip("") - self.btn_copy_adis_command.setWhatsThis("") - self.btn_copy_adis_command.setAccessibleDescription("") + + self.btn_copy_adis_command = QPushButton(self.groupBox) + self.btn_copy_adis_command.setObjectName(u"btn_copy_adis_command") + self.btn_copy_adis_command.setFont(font7) self.btn_copy_adis_command.setAutoFillBackground(False) - icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap("c:\\Users\\aky547\\GitHub\\SemesterapparatsManager\\src\\ui\\../../../../../../.designer/backup/icons/information.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon1 = QIcon() + icon1.addFile(u"../../../../../../.designer/backup/icons/information.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) self.btn_copy_adis_command.setIcon(icon1) self.btn_copy_adis_command.setCheckable(False) self.btn_copy_adis_command.setChecked(False) self.btn_copy_adis_command.setAutoDefault(False) - self.btn_copy_adis_command.setObjectName("btn_copy_adis_command") + self.verticalLayout_5.addWidget(self.btn_copy_adis_command) + + self.verticalLayout_3.addWidget(self.groupBox) + + self.verticalLayout_4.addWidget(self.steps) + + self.verticalLayout.addWidget(self.frame_creation_progress) + MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(parent=MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1590, 22)) - self.menubar.setObjectName("menubar") - self.menuDatei = QtWidgets.QMenu(parent=self.menubar) - self.menuDatei.setObjectName("menuDatei") - self.menuEinstellungen = QtWidgets.QMenu(parent=self.menubar) - self.menuEinstellungen.setObjectName("menuEinstellungen") - self.menuHelp = QtWidgets.QMenu(parent=self.menubar) - self.menuHelp.setObjectName("menuHelp") + self.menubar = QMenuBar(MainWindow) + self.menubar.setObjectName(u"menubar") + self.menubar.setGeometry(QRect(0, 0, 1590, 22)) + self.menuDatei = QMenu(self.menubar) + self.menuDatei.setObjectName(u"menuDatei") + self.menuEinstellungen = QMenu(self.menubar) + self.menuEinstellungen.setObjectName(u"menuEinstellungen") + self.menuHelp = QMenu(self.menubar) + self.menuHelp.setObjectName(u"menuHelp") MainWindow.setMenuBar(self.menubar) - self.statusBar = QtWidgets.QStatusBar(parent=MainWindow) - self.statusBar.setObjectName("statusBar") + self.statusBar = QStatusBar(MainWindow) + self.statusBar.setObjectName(u"statusBar") MainWindow.setStatusBar(self.statusBar) - self.actionBeenden = QtGui.QAction(parent=MainWindow) - self.actionBeenden.setMenuRole(QtGui.QAction.MenuRole.QuitRole) - self.actionBeenden.setShortcutVisibleInContextMenu(True) - self.actionBeenden.setObjectName("actionBeenden") - self.actionEinstellungen = QtGui.QAction(parent=MainWindow) - self.actionEinstellungen.setShortcutVisibleInContextMenu(True) - self.actionEinstellungen.setObjectName("actionEinstellungen") - self.actionDokumentation = QtGui.QAction(parent=MainWindow) - self.actionDokumentation.setShortcutContext(QtCore.Qt.ShortcutContext.ApplicationShortcut) - self.actionDokumentation.setObjectName("actionDokumentation") - self.actionAbout = QtGui.QAction(parent=MainWindow) - self.actionAbout.setMenuRole(QtGui.QAction.MenuRole.AboutRole) - self.actionAbout.setObjectName("actionAbout") - self.actionDokumentation_lokal = QtGui.QAction(parent=MainWindow) - self.actionDokumentation_lokal.setObjectName("actionDokumentation_lokal") - self.menuDatei.addAction(self.actionBeenden) - self.menuEinstellungen.addAction(self.actionEinstellungen) - self.menuHelp.addAction(self.actionDokumentation_lokal) - self.menuHelp.addAction(self.actionAbout) - self.menubar.addAction(self.menuDatei.menuAction()) - self.menubar.addAction(self.menuEinstellungen.menuAction()) - self.menubar.addAction(self.menuHelp.menuAction()) +#if QT_CONFIG(shortcut) self.label_9.setBuddy(self.prof_tel_nr) self.label_3.setBuddy(self.prof_title) self.label_2.setBuddy(self.drpdwn_app_nr) @@ -850,153 +790,215 @@ class Ui_MainWindow(object): self.label_4.setBuddy(self.drpdwn_prof_name) self.label_5.setBuddy(self.app_name) self.label_6.setBuddy(self.sem_year) +#endif // QT_CONFIG(shortcut) + QWidget.setTabOrder(self.drpdwn_app_nr, self.drpdwn_prof_name) + QWidget.setTabOrder(self.drpdwn_prof_name, self.prof_mail) + QWidget.setTabOrder(self.prof_mail, self.prof_tel_nr) + QWidget.setTabOrder(self.prof_tel_nr, self.app_name) + QWidget.setTabOrder(self.app_name, self.app_fach) + QWidget.setTabOrder(self.app_fach, self.sem_sommer) + QWidget.setTabOrder(self.sem_sommer, self.sem_winter) + QWidget.setTabOrder(self.sem_winter, self.sem_year) + QWidget.setTabOrder(self.sem_year, self.check_eternal_app) + QWidget.setTabOrder(self.check_eternal_app, self.btn_add_document) + QWidget.setTabOrder(self.btn_add_document, self.btn_open_document) + QWidget.setTabOrder(self.btn_open_document, self.check_file) + QWidget.setTabOrder(self.check_file, self.check_send_mail) + QWidget.setTabOrder(self.check_send_mail, self.btn_apparat_save) + QWidget.setTabOrder(self.btn_apparat_save, self.btn_apparat_apply) + QWidget.setTabOrder(self.btn_apparat_apply, self.chkbx_show_del_media) + QWidget.setTabOrder(self.chkbx_show_del_media, self.btn_reserve) + QWidget.setTabOrder(self.btn_reserve, self.select_action_box) + QWidget.setTabOrder(self.select_action_box, self.prof_id_adis) + QWidget.setTabOrder(self.prof_id_adis, self.apparat_id_adis) + QWidget.setTabOrder(self.apparat_id_adis, self.automation_add_selected_books) + QWidget.setTabOrder(self.automation_add_selected_books, self.saveandcreate) + + self.menubar.addAction(self.menuDatei.menuAction()) + self.menubar.addAction(self.menuEinstellungen.menuAction()) + self.menubar.addAction(self.menuHelp.menuAction()) + self.menuDatei.addAction(self.actionBeenden) + self.menuEinstellungen.addAction(self.actionEinstellungen) + self.menuHelp.addAction(self.actionAbout) + self.menuHelp.addAction(self.actionDokumentation) self.retranslateUi(MainWindow) + self.tabWidget.setCurrentIndex(0) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - MainWindow.setTabOrder(self.drpdwn_app_nr, self.drpdwn_prof_name) - MainWindow.setTabOrder(self.drpdwn_prof_name, self.prof_mail) - MainWindow.setTabOrder(self.prof_mail, self.prof_tel_nr) - MainWindow.setTabOrder(self.prof_tel_nr, self.app_name) - MainWindow.setTabOrder(self.app_name, self.app_fach) - MainWindow.setTabOrder(self.app_fach, self.sem_sommer) - MainWindow.setTabOrder(self.sem_sommer, self.sem_winter) - MainWindow.setTabOrder(self.sem_winter, self.sem_year) - MainWindow.setTabOrder(self.sem_year, self.check_eternal_app) - MainWindow.setTabOrder(self.check_eternal_app, self.btn_add_document) - MainWindow.setTabOrder(self.btn_add_document, self.btn_open_document) - MainWindow.setTabOrder(self.btn_open_document, self.check_file) - MainWindow.setTabOrder(self.check_file, self.check_send_mail) - MainWindow.setTabOrder(self.check_send_mail, self.btn_apparat_save) - MainWindow.setTabOrder(self.btn_apparat_save, self.btn_apparat_apply) - MainWindow.setTabOrder(self.btn_apparat_apply, self.chkbx_show_del_media) - MainWindow.setTabOrder(self.chkbx_show_del_media, self.btn_reserve) - MainWindow.setTabOrder(self.btn_reserve, self.select_action_box) - MainWindow.setTabOrder(self.select_action_box, self.prof_id_adis) - MainWindow.setTabOrder(self.prof_id_adis, self.apparat_id_adis) - MainWindow.setTabOrder(self.apparat_id_adis, self.automation_add_selected_books) - MainWindow.setTabOrder(self.automation_add_selected_books, self.saveandcreate) + + + QMetaObject.connectSlotsByName(MainWindow) + # setupUi def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "Semesterapparatsmanagement")) - self.create_document.setToolTip(_translate("MainWindow", "Erstellt die Übersicht, welche am Regal ausgehängt werden kann")) - self.create_document.setText(_translate("MainWindow", "Übersicht erstellen")) - self.create_new_app.setText(_translate("MainWindow", "neu. App anlegen")) - self.cancel_active_selection.setText(_translate("MainWindow", "Auswahl abbrechen")) - self.tableWidget_apparate.setSortingEnabled(False) - item = self.tableWidget_apparate.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "AppNr")) - item = self.tableWidget_apparate.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "App Name")) - item = self.tableWidget_apparate.horizontalHeaderItem(2) - item.setText(_translate("MainWindow", "Professor")) - item = self.tableWidget_apparate.horizontalHeaderItem(3) - item.setText(_translate("MainWindow", "gültig bis")) - item = self.tableWidget_apparate.horizontalHeaderItem(4) - item.setText(_translate("MainWindow", "Dauerapparat")) - item = self.tableWidget_apparate.horizontalHeaderItem(5) - item.setText(_translate("MainWindow", "KontoNr")) - self.chkbx_show_del_media.setText(_translate("MainWindow", "gel. Medien anzeigen")) - self.btn_reserve.setText(_translate("MainWindow", "im Apparat?")) - self.label_info.setText(_translate("MainWindow", "Medien werden hinzugefügt")) - self.progress_label.setText(_translate("MainWindow", "Medium x/y")) - self.label_20.setText(_translate("MainWindow", "Medien werden geprüft")) - self.avail_status.setText(_translate("MainWindow", "TextLabel")) - self.automation_add_selected_books.setText(_translate("MainWindow", "Ausgewählte als verfügbar markieren")) - self.tableWidget_apparat_media.setSortingEnabled(True) - item = self.tableWidget_apparat_media.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Buchtitel")) - item.setToolTip(_translate("MainWindow", "Es kann sein, dass der Buchtitel leer ist, dies kommt vor, wenn der Titel nicht passend formatiert ist")) - item = self.tableWidget_apparat_media.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "Signatur")) - item = self.tableWidget_apparat_media.horizontalHeaderItem(2) - item.setText(_translate("MainWindow", "Auflage")) - item = self.tableWidget_apparat_media.horizontalHeaderItem(3) - item.setText(_translate("MainWindow", "Autor")) - item = self.tableWidget_apparat_media.horizontalHeaderItem(4) - item.setText(_translate("MainWindow", "im Apparat?")) - item.setToolTip(_translate("MainWindow", "Diese Angabe ist nicht zuverlässig. Ist das ❌ vorhanden, kann das Medium im Apparat sein, aber aufgrund eines Bugs nicht gefunden worden")) - item = self.tableWidget_apparat_media.horizontalHeaderItem(5) - item.setText(_translate("MainWindow", "Vorgemerkt")) - item = self.tableWidget_apparat_media.horizontalHeaderItem(6) - item.setText(_translate("MainWindow", "Link")) - self.label.setText(_translate("MainWindow", " Medienliste")) - self.app_group_box.setTitle(_translate("MainWindow", "SemesterApparatsdetails")) - item = self.document_list.horizontalHeaderItem(0) - item.setText(_translate("MainWindow", "Dokumentname")) - item = self.document_list.horizontalHeaderItem(1) - item.setText(_translate("MainWindow", "Dateityp")) - item = self.document_list.horizontalHeaderItem(2) - item.setText(_translate("MainWindow", "Neu?")) - item = self.document_list.horizontalHeaderItem(3) - item.setText(_translate("MainWindow", "path")) - self.appname_mand.setText(_translate("MainWindow", "*")) - self.profname_mand.setText(_translate("MainWindow", "*")) - self.fach_mand.setText(_translate("MainWindow", "*")) - self.btn_apparat_apply.setText(_translate("MainWindow", "Aktualisieren")) - self.label_9.setText(_translate("MainWindow", "Tel")) - self._mand.setText(_translate("MainWindow", "*")) - self.check_eternal_app.setText(_translate("MainWindow", "Dauerapparat")) - self.sem_sommer.setText(_translate("MainWindow", "Sommer")) - self.drpdwn_prof_name.setToolTip(_translate("MainWindow", "Nachname, Vorname")) - self.mail_mand.setText(_translate("MainWindow", "*")) - self.label_3.setStatusTip(_translate("MainWindow", "sdvosdvsdv")) - self.label_3.setText(_translate("MainWindow", "Prof. Titel")) - self.label_2.setText(_translate("MainWindow", "Apparatsnummer")) - self.label_8.setText(_translate("MainWindow", "Mail")) - self.label_10.setText(_translate("MainWindow", "Fach")) - self.label_12.setText(_translate("MainWindow", "Prof-ID-aDIS")) - self.label_13.setText(_translate("MainWindow", "Apparat-ID-aDIS")) - self.sem_year.setPlaceholderText(_translate("MainWindow", "2023")) - self.check_send_mail.setText(_translate("MainWindow", "Mail senden")) - self.sem_winter.setText(_translate("MainWindow", "Winter")) - self.label_4.setText(_translate("MainWindow", "Prof. Name")) - self.telnr_mand.setText(_translate("MainWindow", "*")) - self.btn_apparat_save.setText(_translate("MainWindow", "Speichern")) - self.label_5.setText(_translate("MainWindow", "Apparatsname")) - self.label_6.setText(_translate("MainWindow", "Semester")) - self.valid_check_profname.setStatusTip(_translate("MainWindow", "Format: Nachname, Vorname")) - self.valid_check_mail.setStatusTip(_translate("MainWindow", "mail@irgendwas.wasanderes")) - self.saveandcreate.setText(_translate("MainWindow", "Speichern und anlegen")) - self.btn_add_document.setText(_translate("MainWindow", "Dokument hinzufügen")) - self.btn_open_document.setText(_translate("MainWindow", "Dokument öffnen")) - self.check_file.setToolTip(_translate("MainWindow", "Abhängig von der Anzahl der Medien kann die Suche sehr lange dauern")) - self.check_file.setText(_translate("MainWindow", "Medien aus Dokument\n" -" hinzufügen")) - self.btn_extract_data_from_document.setToolTip(_translate("MainWindow", "Die Apparatsdetails werden aus dem Dokument gelesen und eingetragen\n" -"Einige Angaben müssen ggf angepasst werden")) - self.btn_extract_data_from_document.setText(_translate("MainWindow", "Daten aus Dokument\n" -"übernehmen")) - self.add_medium.setText(_translate("MainWindow", "Medien hinzufügen")) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.createApparat), _translate("MainWindow", "Anlegen")) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.search_statistics), _translate("MainWindow", "Suchen / Statistik")) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.elsatab), _translate("MainWindow", "ELSA")) - self.label_21.setText(_translate("MainWindow", "Aktion:")) - self.select_action_box.setItemText(0, _translate("MainWindow", "Nutzer anlegen")) - self.select_action_box.setItemText(1, _translate("MainWindow", "Nutzer bearbeiten")) - self.select_action_box.setItemText(2, _translate("MainWindow", "Lehrperson bearbeiten")) - self.admin_action.setTitle(_translate("MainWindow", "GroupBox")) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.admin), _translate("MainWindow", "Admin")) - self.groupBox_2.setTitle(_translate("MainWindow", "Software")) - self.appdata_check.setText(_translate("MainWindow", "Apparatsdaten eingegeben")) - self.media_check.setText(_translate("MainWindow", "Medien hinzugefügt / importiert")) - self.ids_check.setText(_translate("MainWindow", "Prof-ID und Apparat-ID eingetragen")) - self.groupBox.setTitle(_translate("MainWindow", "aDIS")) - self.media_checked.setText(_translate("MainWindow", "Medien geprüft")) - self.media_edited_check.setText(_translate("MainWindow", "Medien bearbeitet")) - self.app_created.setText(_translate("MainWindow", "Apparat angelegt")) - self.btn_copy_adis_command.setToolTip(_translate("MainWindow", "Hier klicken, um die aDIS Abfrage in die Zwischenablage zu kopieren")) - self.btn_copy_adis_command.setText(_translate("MainWindow", " aDIS Abfrage in Zwischenablage kopieren")) - self.menuDatei.setTitle(_translate("MainWindow", "Datei")) - self.menuEinstellungen.setTitle(_translate("MainWindow", "Bearbeiten")) - self.menuHelp.setTitle(_translate("MainWindow", "Help")) - self.actionBeenden.setText(_translate("MainWindow", "Beenden")) - self.actionBeenden.setShortcut(_translate("MainWindow", "Ctrl+Q")) - self.actionEinstellungen.setText(_translate("MainWindow", "Einstellungen")) - self.actionEinstellungen.setShortcut(_translate("MainWindow", "Alt+S")) - self.actionDokumentation.setText(_translate("MainWindow", "Dokumentation (online)")) - self.actionDokumentation.setShortcut(_translate("MainWindow", "F1")) - self.actionAbout.setText(_translate("MainWindow", "About")) - self.actionDokumentation_lokal.setText(_translate("MainWindow", "Dokumentation (lokal)")) - self.actionDokumentation_lokal.setShortcut(_translate("MainWindow", "F1")) + MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Semesterapparatsmanagement", None)) +#if QT_CONFIG(statustip) + MainWindow.setStatusTip("") +#endif // QT_CONFIG(statustip) + self.actionBeenden.setText(QCoreApplication.translate("MainWindow", u"Beenden", None)) +#if QT_CONFIG(shortcut) + self.actionBeenden.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+Q", None)) +#endif // QT_CONFIG(shortcut) + self.actionEinstellungen.setText(QCoreApplication.translate("MainWindow", u"Einstellungen", None)) +#if QT_CONFIG(shortcut) + self.actionEinstellungen.setShortcut(QCoreApplication.translate("MainWindow", u"Alt+S", None)) +#endif // QT_CONFIG(shortcut) + self.actionAbout.setText(QCoreApplication.translate("MainWindow", u"About", None)) + self.actionDokumentation.setText(QCoreApplication.translate("MainWindow", u"Dokumentation", None)) +#if QT_CONFIG(shortcut) + self.actionDokumentation.setShortcut(QCoreApplication.translate("MainWindow", u"F1", None)) +#endif // QT_CONFIG(shortcut) +#if QT_CONFIG(tooltip) + self.create_document.setToolTip(QCoreApplication.translate("MainWindow", u"Erstellt die \u00dcbersicht, welche am Regal ausgeh\u00e4ngt werden kann", None)) +#endif // QT_CONFIG(tooltip) + self.create_document.setText(QCoreApplication.translate("MainWindow", u"\u00dcbersicht erstellen", None)) + self.create_new_app.setText(QCoreApplication.translate("MainWindow", u"neu. App anlegen", None)) + self.cancel_active_selection.setText(QCoreApplication.translate("MainWindow", u"Auswahl abbrechen", None)) + ___qtablewidgetitem = self.tableWidget_apparate.horizontalHeaderItem(0) + ___qtablewidgetitem.setText(QCoreApplication.translate("MainWindow", u"AppNr", None)); + ___qtablewidgetitem1 = self.tableWidget_apparate.horizontalHeaderItem(1) + ___qtablewidgetitem1.setText(QCoreApplication.translate("MainWindow", u"App Name", None)); + ___qtablewidgetitem2 = self.tableWidget_apparate.horizontalHeaderItem(2) + ___qtablewidgetitem2.setText(QCoreApplication.translate("MainWindow", u"Professor", None)); + ___qtablewidgetitem3 = self.tableWidget_apparate.horizontalHeaderItem(3) + ___qtablewidgetitem3.setText(QCoreApplication.translate("MainWindow", u"g\u00fcltig bis", None)); + ___qtablewidgetitem4 = self.tableWidget_apparate.horizontalHeaderItem(4) + ___qtablewidgetitem4.setText(QCoreApplication.translate("MainWindow", u"Dauerapparat", None)); + ___qtablewidgetitem5 = self.tableWidget_apparate.horizontalHeaderItem(5) + ___qtablewidgetitem5.setText(QCoreApplication.translate("MainWindow", u"KontoNr", None)); + self.chkbx_show_del_media.setText(QCoreApplication.translate("MainWindow", u"gel. Medien anzeigen", None)) + self.btn_reserve.setText(QCoreApplication.translate("MainWindow", u"im Apparat?", None)) + self.label_info.setText(QCoreApplication.translate("MainWindow", u"Medien werden hinzugef\u00fcgt", None)) + self.progress_label.setText(QCoreApplication.translate("MainWindow", u"Medium x/y", None)) + self.label_20.setText(QCoreApplication.translate("MainWindow", u"Medien werden gepr\u00fcft", None)) + self.avail_status.setText(QCoreApplication.translate("MainWindow", u"TextLabel", None)) + self.automation_add_selected_books.setText(QCoreApplication.translate("MainWindow", u"Ausgew\u00e4hlte als verf\u00fcgbar markieren", None)) + ___qtablewidgetitem6 = self.tableWidget_apparat_media.horizontalHeaderItem(0) + ___qtablewidgetitem6.setText(QCoreApplication.translate("MainWindow", u"Buchtitel", None)); +#if QT_CONFIG(tooltip) + ___qtablewidgetitem6.setToolTip(QCoreApplication.translate("MainWindow", u"Es kann sein, dass der Buchtitel leer ist, dies kommt vor, wenn der Titel nicht passend formatiert ist", None)); +#endif // QT_CONFIG(tooltip) + ___qtablewidgetitem7 = self.tableWidget_apparat_media.horizontalHeaderItem(1) + ___qtablewidgetitem7.setText(QCoreApplication.translate("MainWindow", u"Signatur", None)); + ___qtablewidgetitem8 = self.tableWidget_apparat_media.horizontalHeaderItem(2) + ___qtablewidgetitem8.setText(QCoreApplication.translate("MainWindow", u"Auflage", None)); + ___qtablewidgetitem9 = self.tableWidget_apparat_media.horizontalHeaderItem(3) + ___qtablewidgetitem9.setText(QCoreApplication.translate("MainWindow", u"Autor", None)); + ___qtablewidgetitem10 = self.tableWidget_apparat_media.horizontalHeaderItem(4) + ___qtablewidgetitem10.setText(QCoreApplication.translate("MainWindow", u"im Apparat?", None)); +#if QT_CONFIG(tooltip) + ___qtablewidgetitem10.setToolTip(QCoreApplication.translate("MainWindow", u"Diese Angabe ist nicht zuverl\u00e4ssig. Ist das \u274c vorhanden, kann das Medium im Apparat sein, aber aufgrund eines Bugs nicht gefunden worden", None)); +#endif // QT_CONFIG(tooltip) + ___qtablewidgetitem11 = self.tableWidget_apparat_media.horizontalHeaderItem(5) + ___qtablewidgetitem11.setText(QCoreApplication.translate("MainWindow", u"Vorgemerkt", None)); + ___qtablewidgetitem12 = self.tableWidget_apparat_media.horizontalHeaderItem(6) + ___qtablewidgetitem12.setText(QCoreApplication.translate("MainWindow", u"Link", None)); + self.label.setText(QCoreApplication.translate("MainWindow", u" Medienliste", None)) + self.app_group_box.setTitle(QCoreApplication.translate("MainWindow", u"SemesterApparatsdetails", None)) + ___qtablewidgetitem13 = self.document_list.horizontalHeaderItem(0) + ___qtablewidgetitem13.setText(QCoreApplication.translate("MainWindow", u"Dokumentname", None)); + ___qtablewidgetitem14 = self.document_list.horizontalHeaderItem(1) + ___qtablewidgetitem14.setText(QCoreApplication.translate("MainWindow", u"Dateityp", None)); + ___qtablewidgetitem15 = self.document_list.horizontalHeaderItem(2) + ___qtablewidgetitem15.setText(QCoreApplication.translate("MainWindow", u"Neu?", None)); + ___qtablewidgetitem16 = self.document_list.horizontalHeaderItem(3) + ___qtablewidgetitem16.setText(QCoreApplication.translate("MainWindow", u"path", None)); + self.appname_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) + self.profname_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) + self.fach_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) + self.btn_apparat_apply.setText(QCoreApplication.translate("MainWindow", u"Aktualisieren", None)) + self.label_9.setText(QCoreApplication.translate("MainWindow", u"Tel", None)) + self.valid_check_app_fach.setText("") + self._mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) + self.prof_tel_nr.setPlaceholderText("") + self.check_eternal_app.setText(QCoreApplication.translate("MainWindow", u"Dauerapparat", None)) + self.sem_sommer.setText(QCoreApplication.translate("MainWindow", u"Sommer", None)) +#if QT_CONFIG(tooltip) + self.drpdwn_prof_name.setToolTip(QCoreApplication.translate("MainWindow", u"Nachname, Vorname", None)) +#endif // QT_CONFIG(tooltip) + self.drpdwn_prof_name.setPlaceholderText("") + self.mail_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) +#if QT_CONFIG(statustip) + self.label_3.setStatusTip(QCoreApplication.translate("MainWindow", u"sdvosdvsdv", None)) +#endif // QT_CONFIG(statustip) + self.label_3.setText(QCoreApplication.translate("MainWindow", u"Prof. Titel", None)) + self.label_2.setText(QCoreApplication.translate("MainWindow", u"Apparatsnummer", None)) + self.label_8.setText(QCoreApplication.translate("MainWindow", u"Mail", None)) + self.label_10.setText(QCoreApplication.translate("MainWindow", u"Fach", None)) + self.prof_mail.setPlaceholderText("") + self.label_12.setText(QCoreApplication.translate("MainWindow", u"Prof-ID-aDIS", None)) + self.prof_id_adis.setText("") + self.label_13.setText(QCoreApplication.translate("MainWindow", u"Apparat-ID-aDIS", None)) + self.sem_year.setPlaceholderText(QCoreApplication.translate("MainWindow", u"2023", None)) + self.check_send_mail.setText(QCoreApplication.translate("MainWindow", u"Mail senden", None)) + self.sem_winter.setText(QCoreApplication.translate("MainWindow", u"Winter", None)) + self.label_4.setText(QCoreApplication.translate("MainWindow", u"Prof. Name", None)) + self.telnr_mand.setText(QCoreApplication.translate("MainWindow", u"*", None)) +#if QT_CONFIG(statustip) + self.btn_apparat_save.setStatusTip("") +#endif // QT_CONFIG(statustip) + self.btn_apparat_save.setText(QCoreApplication.translate("MainWindow", u"Speichern", None)) + self.label_5.setText(QCoreApplication.translate("MainWindow", u"Apparatsname", None)) + self.label_6.setText(QCoreApplication.translate("MainWindow", u"Semester", None)) +#if QT_CONFIG(statustip) + self.valid_check_profname.setStatusTip(QCoreApplication.translate("MainWindow", u"Format: Nachname, Vorname", None)) +#endif // QT_CONFIG(statustip) + self.valid_check_profname.setText("") + self.valid_check_appname.setText("") + self.valid_check_semester.setText("") +#if QT_CONFIG(statustip) + self.valid_check_mail.setStatusTip(QCoreApplication.translate("MainWindow", u"mail@irgendwas.wasanderes", None)) +#endif // QT_CONFIG(statustip) + self.valid_check_mail.setText("") + self.valid_check_telnr.setText("") + self.saveandcreate.setText(QCoreApplication.translate("MainWindow", u"Speichern und anlegen", None)) + self.btn_add_document.setText(QCoreApplication.translate("MainWindow", u"Dokument hinzuf\u00fcgen", None)) + self.btn_open_document.setText(QCoreApplication.translate("MainWindow", u"Dokument \u00f6ffnen", None)) +#if QT_CONFIG(tooltip) + self.check_file.setToolTip(QCoreApplication.translate("MainWindow", u"Abh\u00e4ngig von der Anzahl der Medien kann die Suche sehr lange dauern", None)) +#endif // QT_CONFIG(tooltip) + self.check_file.setText(QCoreApplication.translate("MainWindow", u"Medien aus Dokument\n" +" hinzuf\u00fcgen", None)) +#if QT_CONFIG(tooltip) + self.btn_extract_data_from_document.setToolTip(QCoreApplication.translate("MainWindow", u"Die Apparatsdetails werden aus dem Dokument gelesen und eingetragen\n" +"Einige Angaben m\u00fcssen ggf angepasst werden", None)) +#endif // QT_CONFIG(tooltip) + self.btn_extract_data_from_document.setText(QCoreApplication.translate("MainWindow", u"Daten aus Dokument\n" +"\u00fcbernehmen", None)) + self.add_medium.setText(QCoreApplication.translate("MainWindow", u"Medien hinzuf\u00fcgen", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.createApparat), QCoreApplication.translate("MainWindow", u"Anlegen", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.search_statistics), QCoreApplication.translate("MainWindow", u"Suchen / Statistik", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.elsatab), QCoreApplication.translate("MainWindow", u"ELSA", None)) + self.label_21.setText(QCoreApplication.translate("MainWindow", u"Aktion:", None)) + self.select_action_box.setItemText(0, QCoreApplication.translate("MainWindow", u"Nutzer anlegen", None)) + self.select_action_box.setItemText(1, QCoreApplication.translate("MainWindow", u"Nutzer bearbeiten", None)) + self.select_action_box.setItemText(2, QCoreApplication.translate("MainWindow", u"Lehrperson bearbeiten", None)) + + self.admin_action.setTitle(QCoreApplication.translate("MainWindow", u"GroupBox", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.admin), QCoreApplication.translate("MainWindow", u"Admin", None)) + self.groupBox_2.setTitle(QCoreApplication.translate("MainWindow", u"Software", None)) + self.appdata_check.setText(QCoreApplication.translate("MainWindow", u"Apparatsdaten eingegeben", None)) + self.media_check.setText(QCoreApplication.translate("MainWindow", u"Medien hinzugef\u00fcgt / importiert", None)) + self.ids_check.setText(QCoreApplication.translate("MainWindow", u"Prof-ID und Apparat-ID eingetragen", None)) + self.groupBox.setTitle(QCoreApplication.translate("MainWindow", u"aDIS", None)) + self.media_checked.setText(QCoreApplication.translate("MainWindow", u"Medien gepr\u00fcft", None)) + self.media_edited_check.setText(QCoreApplication.translate("MainWindow", u"Medien bearbeitet", None)) + self.app_created.setText(QCoreApplication.translate("MainWindow", u"Apparat angelegt", None)) +#if QT_CONFIG(tooltip) + self.btn_copy_adis_command.setToolTip(QCoreApplication.translate("MainWindow", u"Hier klicken, um die aDIS Abfrage in die Zwischenablage zu kopieren", None)) +#endif // QT_CONFIG(tooltip) +#if QT_CONFIG(statustip) + self.btn_copy_adis_command.setStatusTip("") +#endif // QT_CONFIG(statustip) +#if QT_CONFIG(whatsthis) + self.btn_copy_adis_command.setWhatsThis("") +#endif // QT_CONFIG(whatsthis) +#if QT_CONFIG(accessibility) + self.btn_copy_adis_command.setAccessibleDescription("") +#endif // QT_CONFIG(accessibility) + self.btn_copy_adis_command.setText(QCoreApplication.translate("MainWindow", u" aDIS Abfrage in Zwischenablage kopieren", None)) + self.menuDatei.setTitle(QCoreApplication.translate("MainWindow", u"Datei", None)) + self.menuEinstellungen.setTitle(QCoreApplication.translate("MainWindow", u"Bearbeiten", None)) + self.menuHelp.setTitle(QCoreApplication.translate("MainWindow", u"Help", None)) + # retranslateUi + diff --git a/src/ui/userInterface.py b/src/ui/userInterface.py index 81015d4..f36dfa8 100644 --- a/src/ui/userInterface.py +++ b/src/ui/userInterface.py @@ -12,7 +12,7 @@ from typing import Any, Union import loguru from natsort import natsorted from PySide6 import QtCore, QtGui, QtWidgets -from PySide6.QtCore import QThread +from PySide6.QtCore import QThread, Qt from PySide6.QtGui import QRegularExpressionValidator from src import LOG_DIR, Icon @@ -23,7 +23,6 @@ from src.backend.semester import Semester from src.logic import ( APP_NRS, Apparat, - # PROF_TITLES, ApparatData, BookData, Prof, @@ -118,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 @@ -204,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() @@ -329,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]) @@ -1832,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: diff --git a/src/utils/documentation.py b/src/utils/documentation.py index fa70640..1dc00c5 100644 --- a/src/utils/documentation.py +++ b/src/utils/documentation.py @@ -1,10 +1,18 @@ import os import sys from pyramid.config import Configurator -from wsgiref.simple_server import make_server +from wsgiref.simple_server import make_server, WSGIRequestHandler docport = 8000 +class QuietHandler(WSGIRequestHandler): + # suppress “GET /…” access log + def log_request(self, code="-", size="-"): + pass + + # suppress all other messages (errors, etc.) + def log_message(self, fmt, *args): + pass def website(): config = Configurator() @@ -20,7 +28,7 @@ def website(): def launch_documentation(): app = website() - server = make_server("localhost", docport, app) + server = make_server("localhost", docport, app, handler_class=QuietHandler) print("Serving MkDocs documentation on http://0.0.0.0:{}".format(docport)) server.serve_forever() From 3d1560953614b95edd199582a604358ae0496594 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 07:21:16 +0200 Subject: [PATCH 30/42] update build: remove folders from previous build --- build.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index ca74144..0313ef8 100644 --- a/build.py +++ b/build.py @@ -1,10 +1,14 @@ import os - +import shutil with open(".version", "r") as version_file: version = version_file.read().strip() print("Building the project...") +# clear dist directory +if os.path.exists("dist"): + shutil.rmtree("dist") + os.makedirs("dist") build = input("Include console in build? (y/n): ").strip().lower() From 981fee5d7fa108dd62411b76e2ee6f2a89613d8b Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 07:21:37 +0200 Subject: [PATCH 31/42] add warning widget --- src/ui/widgets/searchPage.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 4d10292..764008f 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -483,8 +483,12 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): parent_depth = args[2] apparat = args[1] if header == "deleted" and parent_depth == 2: - # TODO: warn message here - logger.warning("Semesterapparat wurde bereits gelöscht") + log.warning("Semesterapparat wurde bereits gelöscht") + QtWidgets.QMessageBox.information( + self, + "Information", + f"Der Semesterapparat wurde bereits gelöscht und kann nicht angezeigt werden.", + ) if parent_depth == 1: # person selected case - open all apparats from this person in the tableWidget self.tableWidget.setRowCount(0) From 9b0bf3663bc8a9ae8486cd66410934518fd49a12 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 07:22:06 +0200 Subject: [PATCH 32/42] rework quiet handler, use logging to log to file --- src/utils/documentation.py | 43 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/utils/documentation.py b/src/utils/documentation.py index 1dc00c5..88789e5 100644 --- a/src/utils/documentation.py +++ b/src/utils/documentation.py @@ -1,19 +1,36 @@ import os -import sys from pyramid.config import Configurator -from wsgiref.simple_server import make_server, WSGIRequestHandler +from wsgiref.simple_server import WSGIRequestHandler +from src import LOG_DIR +import logging + + +log_path = os.path.join(LOG_DIR, "web_documentation.log") + +# Replace the default StreamHandler with a FileHandler +logging.basicConfig( + level=logging.INFO, + handlers=[logging.FileHandler(log_path, encoding="utf-8")], + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) + +logger = logging.getLogger(__name__) # inherits the same file handler docport = 8000 + class QuietHandler(WSGIRequestHandler): # suppress “GET /…” access log def log_request(self, code="-", size="-"): + logger.info("Request: {} {}".format(self.requestline, code)) pass # suppress all other messages (errors, etc.) def log_message(self, fmt, *args): + logger.error("Error: {}, Args: {}".format(fmt, args)) pass + def website(): config = Configurator() @@ -24,25 +41,3 @@ def website(): app = config.make_wsgi_app() return app - - -def launch_documentation(): - app = website() - server = make_server("localhost", docport, app, handler_class=QuietHandler) - print("Serving MkDocs documentation on http://0.0.0.0:{}".format(docport)) - server.serve_forever() - - -def run_mkdocs(): - with open(os.devnull, "w") as devnull: - old_stdout = sys.stdout - old_stderr = sys.stderr - sys.stdout = devnull - sys.stderr = devnull - try: - launch_documentation() - except Exception as e: - print("Error occurred while launching documentation:", e) - finally: - sys.stdout = old_stdout - sys.stderr = old_stderr From 42dc945ab67238b789572b236decaebd34ef4d19 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 07:22:26 +0200 Subject: [PATCH 33/42] change critical to debug for database path log --- src/backend/admin_console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/admin_console.py b/src/backend/admin_console.py index 69f6006..38b45f5 100644 --- a/src/backend/admin_console.py +++ b/src/backend/admin_console.py @@ -24,7 +24,7 @@ class AdminCommands: else: self.db = Database(db_path=db_path) log.info("AdminCommands initialized with database connection.") - log.critical("location: {}", self.db.db_path) + 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. From 6d8051e4e6d5df66e37f858578a404e83abc9ed8 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 09:22:21 +0200 Subject: [PATCH 34/42] fix: fix display of apparatsdata in table on doubleclick, was broken due to old dict usage instead of apparat class --- src/ui/widgets/searchPage.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 764008f..91a58b3 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -500,10 +500,12 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): # insert new row self.tableWidget.insertRow(0) self.tableWidget.setItem(0, 0, QtWidgets.QTableWidgetItem("")) - self.tableWidget.setItem(0, 1, QtWidgets.QTableWidgetItem(app[1])) - self.tableWidget.setItem(0, 2, QtWidgets.QTableWidgetItem(str(app[4]))) - self.tableWidget.setItem(0, 3, QtWidgets.QTableWidgetItem(app[2])) - self.tableWidget.setItem(0, 4, QtWidgets.QTableWidgetItem(app[3])) + self.tableWidget.setItem(0, 1, QtWidgets.QTableWidgetItem(app.name)) + self.tableWidget.setItem( + 0, 2, QtWidgets.QTableWidgetItem(str(app.appnr)) + ) + self.tableWidget.setItem(0, 3, QtWidgets.QTableWidgetItem(name)) + self.tableWidget.setItem(0, 4, QtWidgets.QTableWidgetItem(app.subject)) # replace the 0 with a checkbox checkbox = QtWidgets.QCheckBox() checkbox.setChecked(False) From 290395d38d77ec4a85fb4779c6fb90c858ab0d88 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 09:22:43 +0200 Subject: [PATCH 35/42] add error checking for non-existent users trying to log in --- src/backend/database.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/backend/database.py b/src/backend/database.py index bce9241..eb5c31a 100644 --- a/src/backend/database.py +++ b/src/backend/database.py @@ -1217,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( From 2e3845b568b56c3eb3c769ec4e29aa68f52773fd Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 09:58:11 +0200 Subject: [PATCH 36/42] update search page to show no resutl found message and tell how to search --- src/ui/widgets/searchPage.py | 8 +- .../widget_sources/search_statistic_page.ui | 104 +-- .../search_statistic_page_ui.py | 605 +++++++++++------- 3 files changed, 430 insertions(+), 287 deletions(-) diff --git a/src/ui/widgets/searchPage.py b/src/ui/widgets/searchPage.py index 91a58b3..0b23fb1 100644 --- a/src/ui/widgets/searchPage.py +++ b/src/ui/widgets/searchPage.py @@ -143,6 +143,8 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): self.stackedWidget_4.setCurrentIndex(1) def search_book(self): + self.no_result.setText("") + self.book_search_result.setRowCount(0) signature = self.search_by_signature.text() title = self.search_by_title.text() @@ -151,9 +153,11 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog): "title": title if title != "" else None, } params = {key: value for key, value in params.items() if value is not None} - log.debug(params) + log.info(params) retdata = self.db.searchBook(params) - if retdata is None: + log.info(retdata) + if retdata == [] or retdata is None: + self.no_result.setText("Keine Ergebnisse gefunden") return for book in retdata: log.debug(book) diff --git a/src/ui/widgets/widget_sources/search_statistic_page.ui b/src/ui/widgets/widget_sources/search_statistic_page.ui index 5356983..8960cb8 100644 --- a/src/ui/widgets/widget_sources/search_statistic_page.ui +++ b/src/ui/widgets/widget_sources/search_statistic_page.ui @@ -210,49 +210,6 @@ - - - - Titel - - - search_by_title - - - - - - - Qt::ClickFocus - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Signatur - - - search_by_signature - - - @@ -266,6 +223,67 @@ + + + + Signatur + + + search_by_signature + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::ClickFocus + + + true + + + + + + + Titel + + + search_by_title + + + + + + + + + Suche mit Enter starten + + + + + + + + + + + + diff --git a/src/ui/widgets/widget_sources/search_statistic_page_ui.py b/src/ui/widgets/widget_sources/search_statistic_page_ui.py index 4cced76..31aa672 100644 --- a/src/ui/widgets/widget_sources/search_statistic_page_ui.py +++ b/src/ui/widgets/widget_sources/search_statistic_page_ui.py @@ -1,271 +1,379 @@ -# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\widgets\widget_sources\search_statistic_page.ui' -# -# 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. +# -*- coding: utf-8 -*- +################################################################################ +## Form generated from reading UI file 'search_statistic_page.ui' +## +## Created by: Qt User Interface Compiler version 6.9.1 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ -from PySide6 import QtCore, QtGui, QtWidgets - +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox, + QDialog, QFrame, QGridLayout, QHBoxLayout, + QHeaderView, QLabel, QLayout, QLineEdit, + QPushButton, QSizePolicy, QSpacerItem, QStackedWidget, + QTabWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, + QWidget) class Ui_Dialog(object): def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") + if not Dialog.objectName(): + Dialog.setObjectName(u"Dialog") Dialog.resize(1244, 767) - self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) - self.verticalLayout.setObjectName("verticalLayout") - self.tabWidget_2 = QtWidgets.QTabWidget(parent=Dialog) - self.tabWidget_2.setMaximumSize(QtCore.QSize(16777215, 250)) - self.tabWidget_2.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus) - self.tabWidget_2.setTabPosition(QtWidgets.QTabWidget.TabPosition.North) - self.tabWidget_2.setTabShape(QtWidgets.QTabWidget.TabShape.Rounded) - self.tabWidget_2.setObjectName("tabWidget_2") - self.tab_3 = QtWidgets.QWidget() - self.tab_3.setObjectName("tab_3") - self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tab_3) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.gridLayout_3 = QtWidgets.QGridLayout() - self.gridLayout_3.setObjectName("gridLayout_3") - self.box_semester = QtWidgets.QComboBox(parent=self.tab_3) + self.verticalLayout = QVBoxLayout(Dialog) + self.verticalLayout.setObjectName(u"verticalLayout") + self.tabWidget_2 = QTabWidget(Dialog) + self.tabWidget_2.setObjectName(u"tabWidget_2") + self.tabWidget_2.setMaximumSize(QSize(16777215, 250)) + self.tabWidget_2.setFocusPolicy(Qt.ClickFocus) + self.tabWidget_2.setTabPosition(QTabWidget.North) + self.tabWidget_2.setTabShape(QTabWidget.Rounded) + self.tab_3 = QWidget() + self.tab_3.setObjectName(u"tab_3") + self.horizontalLayout_2 = QHBoxLayout(self.tab_3) + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.gridLayout_3 = QGridLayout() + self.gridLayout_3.setObjectName(u"gridLayout_3") + self.box_semester = QComboBox(self.tab_3) + self.box_semester.setObjectName(u"box_semester") self.box_semester.setEditable(True) - self.box_semester.setObjectName("box_semester") + self.gridLayout_3.addWidget(self.box_semester, 0, 3, 1, 1) - self.label_18 = QtWidgets.QLabel(parent=self.tab_3) - self.label_18.setObjectName("label_18") + + self.label_18 = QLabel(self.tab_3) + self.label_18.setObjectName(u"label_18") + self.gridLayout_3.addWidget(self.label_18, 2, 2, 1, 1) - self.box_fach = QtWidgets.QComboBox(parent=self.tab_3) + + self.box_fach = QComboBox(self.tab_3) + self.box_fach.setObjectName(u"box_fach") self.box_fach.setEditable(True) - self.box_fach.setObjectName("box_fach") + self.gridLayout_3.addWidget(self.box_fach, 2, 1, 1, 1) - self.label_15 = QtWidgets.QLabel(parent=self.tab_3) - self.label_15.setObjectName("label_15") + + self.label_15 = QLabel(self.tab_3) + self.label_15.setObjectName(u"label_15") + self.gridLayout_3.addWidget(self.label_15, 3, 0, 1, 1) - self.label_11 = QtWidgets.QLabel(parent=self.tab_3) - self.label_11.setObjectName("label_11") + + self.label_11 = QLabel(self.tab_3) + self.label_11.setObjectName(u"label_11") + self.gridLayout_3.addWidget(self.label_11, 1, 0, 1, 1) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_3.addItem(spacerItem, 4, 0, 1, 1) - self.label_7 = QtWidgets.QLabel(parent=self.tab_3) - self.label_7.setObjectName("label_7") + + self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.gridLayout_3.addItem(self.verticalSpacer_3, 4, 0, 1, 1) + + self.label_7 = QLabel(self.tab_3) + self.label_7.setObjectName(u"label_7") + self.gridLayout_3.addWidget(self.label_7, 0, 0, 1, 1) - self.label_17 = QtWidgets.QLabel(parent=self.tab_3) - self.label_17.setObjectName("label_17") + + self.label_17 = QLabel(self.tab_3) + self.label_17.setObjectName(u"label_17") + self.gridLayout_3.addWidget(self.label_17, 0, 2, 1, 1) - self.box_appnrs = QtWidgets.QComboBox(parent=self.tab_3) + + self.box_appnrs = QComboBox(self.tab_3) + self.box_appnrs.setObjectName(u"box_appnrs") self.box_appnrs.setEditable(True) - self.box_appnrs.setObjectName("box_appnrs") + self.gridLayout_3.addWidget(self.box_appnrs, 0, 1, 1, 1) - self.box_dauerapp = QtWidgets.QComboBox(parent=self.tab_3) - self.box_dauerapp.setObjectName("box_dauerapp") + + self.box_dauerapp = QComboBox(self.tab_3) + self.box_dauerapp.setObjectName(u"box_dauerapp") + self.gridLayout_3.addWidget(self.box_dauerapp, 2, 3, 1, 1) - self.box_person = QtWidgets.QComboBox(parent=self.tab_3) + + self.box_person = QComboBox(self.tab_3) + self.box_person.setObjectName(u"box_person") self.box_person.setEditable(True) - self.box_person.setObjectName("box_person") + self.gridLayout_3.addWidget(self.box_person, 1, 1, 1, 1) - self.box_erstellsemester = QtWidgets.QComboBox(parent=self.tab_3) + + self.box_erstellsemester = QComboBox(self.tab_3) + self.box_erstellsemester.setObjectName(u"box_erstellsemester") self.box_erstellsemester.setEditable(True) - self.box_erstellsemester.setObjectName("box_erstellsemester") + self.gridLayout_3.addWidget(self.box_erstellsemester, 1, 3, 1, 1) - self.label_19 = QtWidgets.QLabel(parent=self.tab_3) - self.label_19.setObjectName("label_19") + + self.label_19 = QLabel(self.tab_3) + self.label_19.setObjectName(u"label_19") + self.gridLayout_3.addWidget(self.label_19, 1, 2, 1, 1) - self.label_16 = QtWidgets.QLabel(parent=self.tab_3) - self.label_16.setObjectName("label_16") + + self.label_16 = QLabel(self.tab_3) + self.label_16.setObjectName(u"label_16") + self.gridLayout_3.addWidget(self.label_16, 2, 0, 1, 1) - self.check_deletable = QtWidgets.QCheckBox(parent=self.tab_3) - self.check_deletable.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) - self.check_deletable.setText("") - self.check_deletable.setObjectName("check_deletable") + + self.check_deletable = QCheckBox(self.tab_3) + self.check_deletable.setObjectName(u"check_deletable") + self.check_deletable.setFocusPolicy(Qt.StrongFocus) + self.gridLayout_3.addWidget(self.check_deletable, 3, 1, 1, 1) - self.btn_search = QtWidgets.QPushButton(parent=self.tab_3) - self.btn_search.setObjectName("btn_search") + + self.btn_search = QPushButton(self.tab_3) + self.btn_search.setObjectName(u"btn_search") + self.gridLayout_3.addWidget(self.btn_search, 5, 0, 1, 1) - self.db_err_message = QtWidgets.QLabel(parent=self.tab_3) - self.db_err_message.setText("") - self.db_err_message.setObjectName("db_err_message") + + self.db_err_message = QLabel(self.tab_3) + self.db_err_message.setObjectName(u"db_err_message") + self.gridLayout_3.addWidget(self.db_err_message, 5, 1, 1, 1) - self.gridLayout_3.setColumnMinimumWidth(0, 40) + self.gridLayout_3.setColumnStretch(0, 1) self.gridLayout_3.setColumnStretch(1, 1) self.gridLayout_3.setColumnStretch(2, 1) self.gridLayout_3.setColumnStretch(3, 1) + self.gridLayout_3.setColumnMinimumWidth(0, 40) + self.horizontalLayout_2.addLayout(self.gridLayout_3) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_2.addItem(spacerItem1) + + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_2.addItem(self.horizontalSpacer) + self.horizontalLayout_2.setStretch(0, 1) self.horizontalLayout_2.setStretch(1, 1) self.tabWidget_2.addTab(self.tab_3, "") - self.tab_4 = QtWidgets.QWidget() - self.tab_4.setObjectName("tab_4") - self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tab_4) - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.gridLayout = QtWidgets.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.label_26 = QtWidgets.QLabel(parent=self.tab_4) - self.label_26.setObjectName("label_26") - self.gridLayout.addWidget(self.label_26, 1, 0, 1, 1) - self.search_by_title = QtWidgets.QLineEdit(parent=self.tab_4) - self.search_by_title.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus) - self.search_by_title.setClearButtonEnabled(True) - self.search_by_title.setObjectName("search_by_title") - self.gridLayout.addWidget(self.search_by_title, 1, 1, 1, 1) - spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout.addItem(spacerItem2, 2, 0, 1, 1) - self.label_25 = QtWidgets.QLabel(parent=self.tab_4) - self.label_25.setObjectName("label_25") - self.gridLayout.addWidget(self.label_25, 0, 0, 1, 1) - self.search_by_signature = QtWidgets.QLineEdit(parent=self.tab_4) - self.search_by_signature.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus) + self.tab_4 = QWidget() + self.tab_4.setObjectName(u"tab_4") + self.horizontalLayout_3 = QHBoxLayout(self.tab_4) + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.gridLayout = QGridLayout() + self.gridLayout.setObjectName(u"gridLayout") + self.search_by_signature = QLineEdit(self.tab_4) + self.search_by_signature.setObjectName(u"search_by_signature") + self.search_by_signature.setFocusPolicy(Qt.ClickFocus) self.search_by_signature.setClearButtonEnabled(True) - self.search_by_signature.setObjectName("search_by_signature") + self.gridLayout.addWidget(self.search_by_signature, 0, 1, 1, 1) + + self.label_25 = QLabel(self.tab_4) + self.label_25.setObjectName(u"label_25") + + self.gridLayout.addWidget(self.label_25, 0, 0, 1, 1) + + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.gridLayout.addItem(self.verticalSpacer, 5, 0, 1, 1) + + self.search_by_title = QLineEdit(self.tab_4) + self.search_by_title.setObjectName(u"search_by_title") + self.search_by_title.setFocusPolicy(Qt.ClickFocus) + self.search_by_title.setClearButtonEnabled(True) + + self.gridLayout.addWidget(self.search_by_title, 1, 1, 1, 1) + + self.label_26 = QLabel(self.tab_4) + self.label_26.setObjectName(u"label_26") + + self.gridLayout.addWidget(self.label_26, 1, 0, 1, 1) + + self.horizontalLayout_4 = QHBoxLayout() + self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") + self.label = QLabel(self.tab_4) + self.label.setObjectName(u"label") + + self.horizontalLayout_4.addWidget(self.label) + + self.no_result = QLabel(self.tab_4) + self.no_result.setObjectName(u"no_result") + + self.horizontalLayout_4.addWidget(self.no_result) + + + self.gridLayout.addLayout(self.horizontalLayout_4, 3, 1, 1, 1) + + self.horizontalLayout_3.addLayout(self.gridLayout) - spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_3.addItem(spacerItem3) + + self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_3.addItem(self.horizontalSpacer_2) + self.horizontalLayout_3.setStretch(0, 30) self.horizontalLayout_3.setStretch(1, 70) self.tabWidget_2.addTab(self.tab_4, "") + self.verticalLayout.addWidget(self.tabWidget_2) - self.verticalLayout_3 = QtWidgets.QVBoxLayout() - self.verticalLayout_3.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint) - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.stackedWidget_4 = QtWidgets.QStackedWidget(parent=Dialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) + + self.verticalLayout_3 = QVBoxLayout() + self.verticalLayout_3.setObjectName(u"verticalLayout_3") + self.verticalLayout_3.setSizeConstraint(QLayout.SetDefaultConstraint) + self.stackedWidget_4 = QStackedWidget(Dialog) + self.stackedWidget_4.setObjectName(u"stackedWidget_4") + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.stackedWidget_4.sizePolicy().hasHeightForWidth()) self.stackedWidget_4.setSizePolicy(sizePolicy) - self.stackedWidget_4.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) - self.stackedWidget_4.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) - self.stackedWidget_4.setObjectName("stackedWidget_4") - self.apparatResult = QtWidgets.QWidget() - self.apparatResult.setObjectName("apparatResult") - self.horizontalLayout = QtWidgets.QHBoxLayout(self.apparatResult) - self.horizontalLayout.setObjectName("horizontalLayout") - self.app_results = QtWidgets.QWidget(parent=self.apparatResult) - self.app_results.setObjectName("app_results") - self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.app_results) - self.verticalLayout_6.setObjectName("verticalLayout_6") - self.verticalLayout_4 = QtWidgets.QVBoxLayout() - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.horizontalLayout_7 = QtWidgets.QHBoxLayout() - self.horizontalLayout_7.setObjectName("horizontalLayout_7") - self.verticalLayout_5 = QtWidgets.QVBoxLayout() - self.verticalLayout_5.setObjectName("verticalLayout_5") + self.stackedWidget_4.setFrameShape(QFrame.StyledPanel) + self.stackedWidget_4.setFrameShadow(QFrame.Raised) + self.apparatResult = QWidget() + self.apparatResult.setObjectName(u"apparatResult") + self.horizontalLayout = QHBoxLayout(self.apparatResult) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.app_results = QWidget(self.apparatResult) + self.app_results.setObjectName(u"app_results") + self.verticalLayout_6 = QVBoxLayout(self.app_results) + self.verticalLayout_6.setObjectName(u"verticalLayout_6") + self.verticalLayout_4 = QVBoxLayout() + self.verticalLayout_4.setObjectName(u"verticalLayout_4") + self.horizontalLayout_7 = QHBoxLayout() + self.horizontalLayout_7.setObjectName(u"horizontalLayout_7") + self.verticalLayout_5 = QVBoxLayout() + self.verticalLayout_5.setObjectName(u"verticalLayout_5") + self.horizontalLayout_7.addLayout(self.verticalLayout_5) - self.btn_del_select_apparats = QtWidgets.QPushButton(parent=self.app_results) - self.btn_del_select_apparats.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) - self.btn_del_select_apparats.setObjectName("btn_del_select_apparats") + + self.btn_del_select_apparats = QPushButton(self.app_results) + self.btn_del_select_apparats.setObjectName(u"btn_del_select_apparats") + self.btn_del_select_apparats.setFocusPolicy(Qt.StrongFocus) + self.horizontalLayout_7.addWidget(self.btn_del_select_apparats) - self.btn_notify_for_deletion = QtWidgets.QPushButton(parent=self.app_results) - self.btn_notify_for_deletion.setObjectName("btn_notify_for_deletion") + + self.btn_notify_for_deletion = QPushButton(self.app_results) + self.btn_notify_for_deletion.setObjectName(u"btn_notify_for_deletion") + self.horizontalLayout_7.addWidget(self.btn_notify_for_deletion) - spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_7.addItem(spacerItem4) + + self.horizontalSpacer_5 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_7.addItem(self.horizontalSpacer_5) + + self.verticalLayout_4.addLayout(self.horizontalLayout_7) - self.tableWidget = QtWidgets.QTableWidget(parent=self.app_results) - self.tableWidget.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.tableWidget.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu) - self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) - self.tableWidget.setGridStyle(QtCore.Qt.PenStyle.NoPen) - self.tableWidget.setObjectName("tableWidget") - self.tableWidget.setColumnCount(5) - self.tableWidget.setRowCount(0) - item = QtWidgets.QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(2, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(3, item) - item = QtWidgets.QTableWidgetItem() - self.tableWidget.setHorizontalHeaderItem(4, item) + + self.tableWidget = QTableWidget(self.app_results) + if (self.tableWidget.columnCount() < 5): + self.tableWidget.setColumnCount(5) + __qtablewidgetitem = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem) + __qtablewidgetitem1 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(1, __qtablewidgetitem1) + __qtablewidgetitem2 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(2, __qtablewidgetitem2) + __qtablewidgetitem3 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3) + __qtablewidgetitem4 = QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(4, __qtablewidgetitem4) + self.tableWidget.setObjectName(u"tableWidget") + self.tableWidget.setFocusPolicy(Qt.NoFocus) + self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu) + self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.tableWidget.setGridStyle(Qt.NoPen) + self.tableWidget.setSortingEnabled(True) self.tableWidget.horizontalHeader().setStretchLastSection(True) - self.tableWidget.verticalHeader().setSortIndicatorShown(True) + self.tableWidget.verticalHeader().setProperty(u"showSortIndicator", True) + self.verticalLayout_4.addWidget(self.tableWidget) + + self.verticalLayout_6.addLayout(self.verticalLayout_4) + + self.horizontalLayout.addWidget(self.app_results) - self.stats = QtWidgets.QFrame(parent=self.apparatResult) - self.stats.setObjectName("stats") - self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.stats) - self.verticalLayout_8.setObjectName("verticalLayout_8") - self.tabWidget_3 = QtWidgets.QTabWidget(parent=self.stats) - self.tabWidget_3.setObjectName("tabWidget_3") - self.statistic_table = QtWidgets.QWidget() - self.statistic_table.setObjectName("statistic_table") - self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.statistic_table) - self.verticalLayout_7.setObjectName("verticalLayout_7") - self.statistics_table = QtWidgets.QTableWidget(parent=self.statistic_table) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) + + self.stats = QFrame(self.apparatResult) + self.stats.setObjectName(u"stats") + self.verticalLayout_8 = QVBoxLayout(self.stats) + self.verticalLayout_8.setObjectName(u"verticalLayout_8") + self.tabWidget_3 = QTabWidget(self.stats) + self.tabWidget_3.setObjectName(u"tabWidget_3") + self.statistic_table = QWidget() + self.statistic_table.setObjectName(u"statistic_table") + self.verticalLayout_7 = QVBoxLayout(self.statistic_table) + self.verticalLayout_7.setObjectName(u"verticalLayout_7") + self.statistics_table = QTableWidget(self.statistic_table) + if (self.statistics_table.columnCount() < 3): + self.statistics_table.setColumnCount(3) + __qtablewidgetitem5 = QTableWidgetItem() + self.statistics_table.setHorizontalHeaderItem(0, __qtablewidgetitem5) + __qtablewidgetitem6 = QTableWidgetItem() + self.statistics_table.setHorizontalHeaderItem(1, __qtablewidgetitem6) + __qtablewidgetitem7 = QTableWidgetItem() + self.statistics_table.setHorizontalHeaderItem(2, __qtablewidgetitem7) + self.statistics_table.setObjectName(u"statistics_table") sizePolicy.setHeightForWidth(self.statistics_table.sizePolicy().hasHeightForWidth()) self.statistics_table.setSizePolicy(sizePolicy) - self.statistics_table.setMaximumSize(QtCore.QSize(16777215, 16777215)) - self.statistics_table.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) - self.statistics_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) - self.statistics_table.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.statistics_table.setMaximumSize(QSize(16777215, 16777215)) + self.statistics_table.setFocusPolicy(Qt.NoFocus) + self.statistics_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.statistics_table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.statistics_table.setAlternatingRowColors(True) - self.statistics_table.setObjectName("statistics_table") - self.statistics_table.setColumnCount(3) - self.statistics_table.setRowCount(0) - item = QtWidgets.QTableWidgetItem() - self.statistics_table.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - self.statistics_table.setHorizontalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - self.statistics_table.setHorizontalHeaderItem(2, item) self.statistics_table.horizontalHeader().setCascadingSectionResizes(True) - self.statistics_table.horizontalHeader().setDefaultSectionSize(80) self.statistics_table.horizontalHeader().setMinimumSectionSize(40) - self.statistics_table.horizontalHeader().setSortIndicatorShown(True) + self.statistics_table.horizontalHeader().setDefaultSectionSize(80) + self.statistics_table.horizontalHeader().setProperty(u"showSortIndicator", True) self.statistics_table.horizontalHeader().setStretchLastSection(False) self.statistics_table.verticalHeader().setStretchLastSection(True) + self.verticalLayout_7.addWidget(self.statistics_table) - self.dataLayout = QtWidgets.QHBoxLayout() - self.dataLayout.setObjectName("dataLayout") + + self.dataLayout = QHBoxLayout() + self.dataLayout.setObjectName(u"dataLayout") + self.verticalLayout_7.addLayout(self.dataLayout) + self.tabWidget_3.addTab(self.statistic_table, "") - self.graph_table = QtWidgets.QWidget() - self.graph_table.setObjectName("graph_table") + self.graph_table = QWidget() + self.graph_table.setObjectName(u"graph_table") self.tabWidget_3.addTab(self.graph_table, "") + self.verticalLayout_8.addWidget(self.tabWidget_3) + + self.horizontalLayout.addWidget(self.stats) + self.stackedWidget_4.addWidget(self.apparatResult) - self.bookresult = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) + self.bookresult = QWidget() + self.bookresult.setObjectName(u"bookresult") sizePolicy.setHeightForWidth(self.bookresult.sizePolicy().hasHeightForWidth()) self.bookresult.setSizePolicy(sizePolicy) - self.bookresult.setObjectName("bookresult") - self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.bookresult) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.book_search_result = QtWidgets.QTableWidget(parent=self.bookresult) - self.book_search_result.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) - self.book_search_result.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) + self.verticalLayout_2 = QVBoxLayout(self.bookresult) + self.verticalLayout_2.setObjectName(u"verticalLayout_2") + self.book_search_result = QTableWidget(self.bookresult) + if (self.book_search_result.columnCount() < 3): + self.book_search_result.setColumnCount(3) + __qtablewidgetitem8 = QTableWidgetItem() + self.book_search_result.setHorizontalHeaderItem(0, __qtablewidgetitem8) + __qtablewidgetitem9 = QTableWidgetItem() + self.book_search_result.setHorizontalHeaderItem(1, __qtablewidgetitem9) + __qtablewidgetitem10 = QTableWidgetItem() + self.book_search_result.setHorizontalHeaderItem(2, __qtablewidgetitem10) + self.book_search_result.setObjectName(u"book_search_result") + self.book_search_result.setFrameShadow(QFrame.Plain) + self.book_search_result.setEditTriggers(QAbstractItemView.NoEditTriggers) self.book_search_result.setAlternatingRowColors(True) self.book_search_result.setRowCount(0) - self.book_search_result.setObjectName("book_search_result") - self.book_search_result.setColumnCount(3) - item = QtWidgets.QTableWidgetItem() - self.book_search_result.setHorizontalHeaderItem(0, item) - item = QtWidgets.QTableWidgetItem() - self.book_search_result.setHorizontalHeaderItem(1, item) - item = QtWidgets.QTableWidgetItem() - self.book_search_result.setHorizontalHeaderItem(2, item) self.book_search_result.horizontalHeader().setCascadingSectionResizes(True) - self.book_search_result.horizontalHeader().setDefaultSectionSize(200) self.book_search_result.horizontalHeader().setMinimumSectionSize(100) - self.book_search_result.horizontalHeader().setSortIndicatorShown(True) + self.book_search_result.horizontalHeader().setDefaultSectionSize(200) + self.book_search_result.horizontalHeader().setProperty(u"showSortIndicator", True) self.book_search_result.horizontalHeader().setStretchLastSection(True) - self.book_search_result.verticalHeader().setSortIndicatorShown(False) + self.book_search_result.verticalHeader().setProperty(u"showSortIndicator", False) + self.verticalLayout_2.addWidget(self.book_search_result) + self.stackedWidget_4.addWidget(self.bookresult) + self.verticalLayout_3.addWidget(self.stackedWidget_4) + + self.verticalLayout.addLayout(self.verticalLayout_3) + +#if QT_CONFIG(shortcut) self.label_18.setBuddy(self.box_dauerapp) self.label_15.setBuddy(self.check_deletable) self.label_11.setBuddy(self.box_person) @@ -273,64 +381,77 @@ class Ui_Dialog(object): self.label_17.setBuddy(self.box_semester) self.label_19.setBuddy(self.box_erstellsemester) self.label_16.setBuddy(self.box_fach) - self.label_26.setBuddy(self.search_by_title) self.label_25.setBuddy(self.search_by_signature) + self.label_26.setBuddy(self.search_by_title) +#endif // QT_CONFIG(shortcut) + QWidget.setTabOrder(self.box_appnrs, self.box_person) + QWidget.setTabOrder(self.box_person, self.box_fach) + QWidget.setTabOrder(self.box_fach, self.check_deletable) + QWidget.setTabOrder(self.check_deletable, self.box_semester) + QWidget.setTabOrder(self.box_semester, self.box_erstellsemester) + QWidget.setTabOrder(self.box_erstellsemester, self.box_dauerapp) + QWidget.setTabOrder(self.box_dauerapp, self.btn_search) + QWidget.setTabOrder(self.btn_search, self.book_search_result) + QWidget.setTabOrder(self.book_search_result, self.search_by_signature) + QWidget.setTabOrder(self.search_by_signature, self.search_by_title) self.retranslateUi(Dialog) + self.tabWidget_2.setCurrentIndex(0) self.stackedWidget_4.setCurrentIndex(0) self.tabWidget_3.setCurrentIndex(0) - QtCore.QMetaObject.connectSlotsByName(Dialog) - Dialog.setTabOrder(self.box_appnrs, self.box_person) - Dialog.setTabOrder(self.box_person, self.box_fach) - Dialog.setTabOrder(self.box_fach, self.check_deletable) - Dialog.setTabOrder(self.check_deletable, self.box_semester) - Dialog.setTabOrder(self.box_semester, self.box_erstellsemester) - Dialog.setTabOrder(self.box_erstellsemester, self.box_dauerapp) - Dialog.setTabOrder(self.box_dauerapp, self.btn_search) - Dialog.setTabOrder(self.btn_search, self.book_search_result) - Dialog.setTabOrder(self.book_search_result, self.search_by_signature) - Dialog.setTabOrder(self.search_by_signature, self.search_by_title) + + + QMetaObject.connectSlotsByName(Dialog) + # setupUi def retranslateUi(self, Dialog): - _translate = QtCore.QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "Dialog")) - self.label_18.setText(_translate("Dialog", "Dauerapp:")) - self.label_15.setText(_translate("Dialog", "Löschbar")) - self.label_11.setText(_translate("Dialog", "Person:")) - self.label_7.setText(_translate("Dialog", "Appnr.:")) - self.label_17.setText(_translate("Dialog", "Endsemester:")) - self.label_19.setText(_translate("Dialog", "Erstellsemester:")) - self.label_16.setText(_translate("Dialog", "Fach:")) - self.btn_search.setText(_translate("Dialog", "Suchen")) - self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_3), _translate("Dialog", "Statistik")) - self.label_26.setText(_translate("Dialog", "Titel")) - self.label_25.setText(_translate("Dialog", "Signatur")) - self.search_by_signature.setStatusTip(_translate("Dialog", "Trunkierung mit * am Ende unterstützt")) - self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_4), _translate("Dialog", "Suchen")) - self.btn_del_select_apparats.setText(_translate("Dialog", "Ausgewählte Löschen")) - self.btn_notify_for_deletion.setStatusTip(_translate("Dialog", "Zeigt für jeden ausgewählten Apparat eine eMail-Vorlage an")) - self.btn_notify_for_deletion.setText(_translate("Dialog", "Ausgewählte Benachrichtigen")) - self.tableWidget.setSortingEnabled(True) - item = self.tableWidget.horizontalHeaderItem(1) - item.setText(_translate("Dialog", "Apparatsname")) - item = self.tableWidget.horizontalHeaderItem(2) - item.setText(_translate("Dialog", "Apparatsnummer")) - item = self.tableWidget.horizontalHeaderItem(3) - item.setText(_translate("Dialog", "Person")) - item = self.tableWidget.horizontalHeaderItem(4) - item.setText(_translate("Dialog", "Fach")) - item = self.statistics_table.horizontalHeaderItem(0) - item.setText(_translate("Dialog", "Semester")) - item = self.statistics_table.horizontalHeaderItem(1) - item.setText(_translate("Dialog", "Zugang")) - item = self.statistics_table.horizontalHeaderItem(2) - item.setText(_translate("Dialog", "Abgang")) - self.tabWidget_3.setTabText(self.tabWidget_3.indexOf(self.statistic_table), _translate("Dialog", "Tabelle")) - self.tabWidget_3.setTabText(self.tabWidget_3.indexOf(self.graph_table), _translate("Dialog", "Erstellte und gelöschte Semesterapparate")) - item = self.book_search_result.horizontalHeaderItem(0) - item.setText(_translate("Dialog", "Titel")) - item = self.book_search_result.horizontalHeaderItem(1) - item.setText(_translate("Dialog", "Signatur")) - item = self.book_search_result.horizontalHeaderItem(2) - item.setText(_translate("Dialog", "Apparat")) + Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None)) + self.label_18.setText(QCoreApplication.translate("Dialog", u"Dauerapp:", None)) + self.label_15.setText(QCoreApplication.translate("Dialog", u"L\u00f6schbar", None)) + self.label_11.setText(QCoreApplication.translate("Dialog", u"Person:", None)) + self.label_7.setText(QCoreApplication.translate("Dialog", u"Appnr.:", None)) + self.label_17.setText(QCoreApplication.translate("Dialog", u"Endsemester:", None)) + self.label_19.setText(QCoreApplication.translate("Dialog", u"Erstellsemester:", None)) + self.label_16.setText(QCoreApplication.translate("Dialog", u"Fach:", None)) + self.check_deletable.setText("") + self.btn_search.setText(QCoreApplication.translate("Dialog", u"Suchen", None)) + self.db_err_message.setText("") + self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_3), QCoreApplication.translate("Dialog", u"Statistik", None)) +#if QT_CONFIG(statustip) + self.search_by_signature.setStatusTip(QCoreApplication.translate("Dialog", u"Trunkierung mit * am Ende unterst\u00fctzt", None)) +#endif // QT_CONFIG(statustip) + self.label_25.setText(QCoreApplication.translate("Dialog", u"Signatur", None)) + self.label_26.setText(QCoreApplication.translate("Dialog", u"Titel", None)) + self.label.setText(QCoreApplication.translate("Dialog", u"Suche mit Enter starten", None)) + self.no_result.setText("") + self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_4), QCoreApplication.translate("Dialog", u"Suchen", None)) + self.btn_del_select_apparats.setText(QCoreApplication.translate("Dialog", u"Ausgew\u00e4hlte L\u00f6schen", None)) +#if QT_CONFIG(statustip) + self.btn_notify_for_deletion.setStatusTip(QCoreApplication.translate("Dialog", u"Zeigt f\u00fcr jeden ausgew\u00e4hlten Apparat eine eMail-Vorlage an", None)) +#endif // QT_CONFIG(statustip) + self.btn_notify_for_deletion.setText(QCoreApplication.translate("Dialog", u"Ausgew\u00e4hlte Benachrichtigen", None)) + ___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(1) + ___qtablewidgetitem.setText(QCoreApplication.translate("Dialog", u"Apparatsname", None)); + ___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(2) + ___qtablewidgetitem1.setText(QCoreApplication.translate("Dialog", u"Apparatsnummer", None)); + ___qtablewidgetitem2 = self.tableWidget.horizontalHeaderItem(3) + ___qtablewidgetitem2.setText(QCoreApplication.translate("Dialog", u"Person", None)); + ___qtablewidgetitem3 = self.tableWidget.horizontalHeaderItem(4) + ___qtablewidgetitem3.setText(QCoreApplication.translate("Dialog", u"Fach", None)); + ___qtablewidgetitem4 = self.statistics_table.horizontalHeaderItem(0) + ___qtablewidgetitem4.setText(QCoreApplication.translate("Dialog", u"Semester", None)); + ___qtablewidgetitem5 = self.statistics_table.horizontalHeaderItem(1) + ___qtablewidgetitem5.setText(QCoreApplication.translate("Dialog", u"Zugang", None)); + ___qtablewidgetitem6 = self.statistics_table.horizontalHeaderItem(2) + ___qtablewidgetitem6.setText(QCoreApplication.translate("Dialog", u"Abgang", None)); + self.tabWidget_3.setTabText(self.tabWidget_3.indexOf(self.statistic_table), QCoreApplication.translate("Dialog", u"Tabelle", None)) + self.tabWidget_3.setTabText(self.tabWidget_3.indexOf(self.graph_table), QCoreApplication.translate("Dialog", u"Erstellte und gel\u00f6schte Semesterapparate", None)) + ___qtablewidgetitem7 = self.book_search_result.horizontalHeaderItem(0) + ___qtablewidgetitem7.setText(QCoreApplication.translate("Dialog", u"Titel", None)); + ___qtablewidgetitem8 = self.book_search_result.horizontalHeaderItem(1) + ___qtablewidgetitem8.setText(QCoreApplication.translate("Dialog", u"Signatur", None)); + ___qtablewidgetitem9 = self.book_search_result.horizontalHeaderItem(2) + ___qtablewidgetitem9.setText(QCoreApplication.translate("Dialog", u"Apparat", None)); + # retranslateUi + From 5951081efa06fd7f703e4c265252567a39fa6a6b Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 10:37:49 +0200 Subject: [PATCH 37/42] update build script --- build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.py b/build.py index 0313ef8..60b295b 100644 --- a/build.py +++ b/build.py @@ -5,10 +5,12 @@ 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() From 7c756c2f218dcf4912a41cfa966e87a4bd0609eb Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 11:22:16 +0200 Subject: [PATCH 38/42] add mail folder to build command --- build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.py b/build.py index 60b295b..b80b545 100644 --- a/build.py +++ b/build.py @@ -14,7 +14,7 @@ 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 --enable-plugin=pyside6 --product-name=SemesterApparatsManager --product-version={version} --output-filename=SAM.exe --windows-icon-from-ico=icons/logo.ico" +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" From d4eae2b71ec8423061dd7233fb0263c8952a3961 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 11:22:38 +0200 Subject: [PATCH 39/42] remove unneeded mail file --- mail.py | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 mail.py diff --git a/mail.py b/mail.py deleted file mode 100644 index 1f19755..0000000 --- a/mail.py +++ /dev/null @@ -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, - ) From 88b6e8fc6a40f3f66b207e20b645fc385f04038d Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 12:10:50 +0200 Subject: [PATCH 40/42] update gitignore, add test.py to be ignored --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5e87da1..b9f6528 100644 --- a/.gitignore +++ b/.gitignore @@ -229,4 +229,5 @@ config.yaml logs/ *.pdf -*.docx \ No newline at end of file +*.docx +test.py From 83e6446b167665b2dc5fd7a7acbcd8e31ea5bd45 Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 12:13:18 +0200 Subject: [PATCH 41/42] allow dirty version bumps --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 778ce38..9ae1852 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ tag = true sign_tags = false tag_name = "v{new_version}" tag_message = "Bump version: {current_version} → {new_version}" -allow_dirty = false +allow_dirty = true commit = true message = "Bump version: {current_version} → {new_version}" moveable_tags = [] From 8d94abfb1aca90f8396c926467b8c193cf24edbb Mon Sep 17 00:00:00 2001 From: WorldTeacher Date: Thu, 3 Jul 2025 12:14:36 +0200 Subject: [PATCH 42/42] manually bump version back to current --- .version | 2 +- pyproject.toml | 4 ++-- src/__init__.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.version b/.version index 6c6aa7c..7dff5b8 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.1.0 \ No newline at end of file +0.2.1 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9ae1852..2a9433a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "semesterapparatsmanager" -version = "0.1.0" +version = "0.2.1" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" @@ -37,7 +37,7 @@ dev = [ ] [tool.bumpversion] -current_version = "0.1.0" +current_version = "0.2.1" parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" serialize = ["{major}.{minor}.{patch}"] search = "{current_version}" diff --git a/src/__init__.py b/src/__init__.py index 1259fd9..daeaf7a 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.1.0" +__version__ = "0.2.1" __author__ = "Alexander Kirchner" __all__ = ["__version__", "__author__", "Icon", "settings"]