From dc85e7c0c93670dff94c39f35d43f39f0efd27d9 Mon Sep 17 00:00:00 2001 From: Macbook Date: Sun, 30 Nov 2025 19:31:06 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=87=D0=B0=D1=81=D1=82=D1=8C=20=D0=B1=D0=B5=D0=BA=D0=B5=D0=BD?= =?UTF-8?q?=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/log.ini | 24 +- db/__init__.py | 32 +- db/handlers/__init__.py | 39 +++ db/handlers/access.py | 164 ++++++++-- db/handlers/categories.py | 92 ++++++ db/handlers/records.py | 118 +++++++ db/handlers/stock.py | 88 ++++++ db/handlers/toolbox.py | 120 +++++++- db/handlers/toolkit.py | 288 ++++++++++++++++++ db/handlers/user.py | 277 ++++++++++++----- db/schemas/__init__.py | 150 ++++++++- db/schemas/access.py | 3 +- db/schemas/categories.py | 2 +- db/schemas/records.py | 75 +++++ db/schemas/stock.py | 3 +- db/schemas/toolbox.py | 7 +- db/schemas/toolkit.py | 7 +- main.py | 19 +- pyproject.toml | 1 + static/images/tools/default.png | Bin 0 -> 70321 bytes static/images/users/default.png | Bin 0 -> 52120 bytes utils/__init__.py | 8 +- utils/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 261 bytes utils/__pycache__/for_DB.cpython-313.pyc | Bin 0 -> 946 bytes utils/__pycache__/image.cpython-313.pyc | Bin 0 -> 2288 bytes utils/__pycache__/loggers.cpython-313.pyc | Bin 0 -> 1934 bytes utils/__pycache__/my_loggers.cpython-313.pyc | Bin 0 -> 1253 bytes utils/__pycache__/password.cpython-313.pyc | Bin 0 -> 1013 bytes .../__pycache__/safe_filemane.cpython-313.pyc | Bin 0 -> 2457 bytes utils/image.py | 60 ++++ utils/loggers.py | 35 +++ utils/my_loggers.py | 9 - utils/password.py | 2 +- utils/safe_filemane.py | 39 +++ uv.lock | 60 ++++ 35 files changed, 1573 insertions(+), 149 deletions(-) create mode 100644 db/handlers/__init__.py create mode 100644 db/handlers/categories.py create mode 100644 db/handlers/records.py create mode 100644 db/handlers/stock.py create mode 100644 db/handlers/toolkit.py create mode 100644 db/schemas/records.py create mode 100644 static/images/tools/default.png create mode 100644 static/images/users/default.png create mode 100644 utils/__pycache__/__init__.cpython-313.pyc create mode 100644 utils/__pycache__/for_DB.cpython-313.pyc create mode 100644 utils/__pycache__/image.cpython-313.pyc create mode 100644 utils/__pycache__/loggers.cpython-313.pyc create mode 100644 utils/__pycache__/my_loggers.cpython-313.pyc create mode 100644 utils/__pycache__/password.cpython-313.pyc create mode 100644 utils/__pycache__/safe_filemane.cpython-313.pyc create mode 100644 utils/image.py create mode 100644 utils/loggers.py delete mode 100644 utils/my_loggers.py create mode 100644 utils/safe_filemane.py diff --git a/config/log.ini b/config/log.ini index 49b8c67..5faff9e 100644 --- a/config/log.ini +++ b/config/log.ini @@ -1,20 +1,38 @@ [loggers] -keys=root +keys=root, tools_app, tools_db, tools_ui [handlers] keys=logconsole [formatters] keys=formatter -encoding=utf-8 [logger_root] level=INFO handlers=logconsole +[logger_tools_app] +level=INFO +handlers=logconsole +qualname=tools.app +propagate=0 + +[logger_tools_db] +level=INFO +handlers=logconsole +qualname=tools.db +propagate=0 + +[logger_tools_ui] +level=INFO +handlers=logconsole +qualname=tools.ui +propagate=0 + + [formatter_formatter] class=colorlog.ColoredFormatter -format=%(log_color)s%(asctime)s: [%(levelname)s] %(message)s [%(module)s.%(funcName)s():%(lineno)d] +format=%(log_color)s%(asctime)s: [%(levelname)s] %(message)s [%(filename)s:%(lineno)d '%(funcName)s'] datefmt=%Y-%m-%d %H:%M:%S [handler_logconsole] diff --git a/db/__init__.py b/db/__init__.py index 7462397..d1cb303 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -4,7 +4,7 @@ from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker from sqlalchemy.exc import InvalidRequestError from sqlalchemy.pool import NullPool import config -from utils import loggerDB +from utils import logger DATABASE_URL = f"postgresql+asyncpg://{config.DB_USER}:{config.DB_PASS}@{config.DB_HOST}:{config.DB_PORT}/{config.DB_NAME}" @@ -22,43 +22,43 @@ class CRUD: is_lst = isinstance(db_data, list) async with SessionLocal() as db: if is_lst: - loggerDB.info(f"Создаю {len(db_data)} записей") + logger.info(f"Создаю {len(db_data)} записей") try: db.add_all(db_data) except InvalidRequestError: for data in db_data: await db.merge(data) else: - loggerDB.info("Создаю запись") + logger.info("Создаю запись") db.add(db_data) await db.commit() if refresh: if is_lst: - loggerDB.info(f"Обновляю {len(db_data)} записей") + logger.info(f"Обновляю {len(db_data)} записей") for data in db_data: await db.refresh(data) else: - loggerDB.info("Обновляю запись") + logger.info("Обновляю запись") await db.refresh(db_data) - loggerDB.info("Запись создана") + logger.info("Запись создана") return db_data if refresh else None except Exception as e: - loggerDB.error(f"Ошибка создания: {str(e)}", exc_info=True) + logger.error(f"Ошибка создания: {str(e)}", exc_info=True) return None async def read(query, all: bool = False): try: async with SessionLocal() as db: - loggerDB.info(f"Чтение записей. Все: {all}") + logger.info(f"Чтение записей. Все: {all}") results = await db.execute(query) - loggerDB.info(f"Чтение завершено") + logger.info(f"Чтение завершено") return ( results.unique().scalars().all() if all else results.unique().scalars().first() ) except Exception as e: - loggerDB.error(f"Ошибка чтения: {str(e)}", exc_info=True) + logger.error(f"Ошибка чтения: {str(e)}", exc_info=True) return None async def delete(db_data) -> bool: @@ -87,18 +87,18 @@ class CRUD: async with SessionLocal() as db: try: if isinstance(db_data, list): - loggerDB.info(f"Удаляю записей: {len(db_data)}") + logger.info(f"Удаляю записей: {len(db_data)}") for data in db_data: await deleteFromDB(data, db) else: - loggerDB.info("Удаляю запись") + logger.info("Удаляю запись") await deleteFromDB(db_data, db) await db.commit() - loggerDB.info("Запись удалена") + logger.info("Запись удалена") return True except Exception as e: await db.rollback() - loggerDB.error(f"Ошибка удаления: {str(e)}", exc_info=True) + logger.error(f"Ошибка удаления: {str(e)}", exc_info=True) return False async def update(db_data, id, **kwargs): @@ -107,9 +107,9 @@ class CRUD: query = update(db_data).where(db_data.id == id).values(**kwargs) item = await db.execute(query) await db.commit() - loggerDB.info("Запись обновлена") + logger.info("Запись обновлена") return item except Exception as e: await db.rollback() - loggerDB.error(f"Ошибка обновления: {str(e)}", exc_info=True) + logger.error(f"Ошибка обновления: {str(e)}", exc_info=True) return None diff --git a/db/handlers/__init__.py b/db/handlers/__init__.py new file mode 100644 index 0000000..82861c7 --- /dev/null +++ b/db/handlers/__init__.py @@ -0,0 +1,39 @@ +from .user import * +from .access import * +from .toolbox import * +from .categories import * +from .stock import * +from .toolkit import * +from .records import * + + +class InitializeDatabase: + def __init__(self): + self.userHandler = UserHandler() + self.accessHandler = AccessLevelHandler() + self.toolboxHandler = ToolboxHandler() + self.categoryHandler = CategoryHandler() + self.stockHandler = StockHandler() + self.toolkitHandler = ToolkitHandler() + self.stocksRecordHandler = StocksRecordsHandler() + self.servicesRecordHandler = ServiceRecordsHandler() + + async def initialize(self): + await self.accessHandler.initialize() + await self.userHandler.initialize() + await self.toolboxHandler.initialize() + await self.categoryHandler.initialize() + await self.toolkitHandler.initialize() + await self.stockHandler.initialize() + + +__all__ = [ + "UserHandler", + "AccessLevelHandler", + "ToolboxHandler", + "CategoryHandler", + "StockHandler", + "ToolkitHandler", + "StocksRecords", + "ServicesRecords", +] diff --git a/db/handlers/access.py b/db/handlers/access.py index 17345e1..3a1b70b 100644 --- a/db/handlers/access.py +++ b/db/handlers/access.py @@ -2,25 +2,153 @@ from sqlalchemy import select from utils import logger from db import CRUD from db.schemas import AccessLevel +from db.handlers import ServiceRecordsHandler -async def getAccessdata(accessId: int) -> dict: - query = select(AccessLevel).where(AccessLevel.id == accessId) - accessData = await CRUD.read(query) - if not accessData: - logger.error("Уровень доступа не найден") - return {} - return accessData.toDict() +class AccessLevelHandler: + async def add(**kwargs): + title = kwargs.get("title", None) + if not title: + logger.error("Не указано название уровня доступа") + return {} + exists = await CRUD.read(select(AccessLevel).where(AccessLevel.title == title)) + if exists: + logger.error("Уровень доступа с таким названием уже существует") + return {} + try: + logger.info(f"Создание уровня доступа {title}") + user_id = kwargs.pop("user_id", None) + accessData = await AccessLevel(**kwargs).save() + await ServiceRecordsHandler.add( + user_id, {"Добавлен уровень доступа": accessData.toDict()} + ) + except Exception as e: + logger.error(f"Ошибка создания уровня доступа: {str(e)}") + return {} + logger.info(f"Уровень доступа {accessData.title} успешно создан") + return accessData.toDict() + async def get(accessId: int) -> dict: + query = select(AccessLevel).where(AccessLevel.id == accessId) + accessData = await CRUD.read(query) + if not accessData: + logger.error("Уровень доступа не найден") + return {} + return accessData.toDict() -async def editAccessData(accessId: int, **kwargs): - query = select(AccessLevel).where(AccessLevel.id == accessId) - accessData = await CRUD.read(query) - if not accessData: - logger.error("Уровень доступа не найден") - return {} - editedAccessData = await accessData.edit(**kwargs) - logger.info( - f"Уровень доступа {editedAccessData.title} успешно обновлен, изменены данные: {kwargs.keys()}" - ) - return editedAccessData.toDict() + async def edit(accessId: int, **kwargs): + query = select(AccessLevel).where(AccessLevel.id == accessId) + accessData = await CRUD.read(query) + if not accessData: + logger.error("Уровень доступа не найден") + return {} + try: + user_id = kwargs.pop("user_id", None) + editedAccessData = await accessData.edit(**kwargs) + await ServiceRecordsHandler.add( + user_id, {"Обновлен уровень доступа": editedAccessData.toDict()} + ) + except Exception as e: + logger.error(f"Ошибка обновления уровня доступа: {str(e)}") + return {} + logger.info( + f"Уровень доступа {editedAccessData.title} успешно обновлен, изменены данные: {kwargs.keys()}" + ) + return editedAccessData.toDict() + + async def getAll() -> list: + query = select(AccessLevel) + accessLevels = await CRUD.read(query, True) + return ( + [accessLevel.toDict() for accessLevel in accessLevels] + if accessLevels + else [] + ) + + async def delete(accessId: int, user_id: int = None): + query = select(AccessLevel).where(AccessLevel.id == accessId) + accessData = await CRUD.read(query) + if not accessData: + logger.error("Уровень доступа не найден") + return False + try: + title = accessData.title + result = await CRUD.delete(accessData) + await ServiceRecordsHandler.add( + user_id, {"Удален уровень доступа": f"Название: {title}"} + ) + except Exception as e: + logger.error(f"Ошибка удаления уровня доступа: {str(e)}") + return False + logger.info( + f"Уровень доступа {accessData.title} {'успешно удален' if result else 'не удален'}" + ) + return result + + async def initialize(self): + baseAcessLevels = { + "admin": { + "title": "Администратор", + "description": "Администратор. Полный доступ", + "receiving_edit": True, + "refund_request_edit": True, + "refund_request_confirm": True, + "debit_request_edit": True, + "debit_request_confirm": True, + "tools_creation": True, + "tools_registration": True, + "tools_registration_edit": True, + "tools_edit": True, + "tools_delete": True, + "users_creation": True, + "users_edit": True, + "users_disabling": True, + "users_view": True, + "available_own_toolbox": False, + "view_all_toolboxes": True, + "view_requests": True, + "view_services": True, + "access_level_view": True, + "access_level_edit": True, + "manage_toolboxes": True, + }, + "manager": { + "title": "Менеджер", + "description": "Менеджер. Доступ к просмотру и редактированию рабочей информации", + "refund_request_confirm": True, + "debit_request_confirm": True, + "tools_creation": True, + "tools_registration": True, + "tools_edit": True, + "users_disabling": True, + "users_view": True, + "view_all_toolboxes": True, + "view_requests": True, + "view_services": True, + "access_level_view": True, + "manage_toolboxes": True, + }, + "storekeeper": { + "title": "Кладовщик", + "description": "Кладовщик. Доступ к управлению складом", + "refund_request_confirm": True, + "debit_request_confirm": True, + "tools_creation": True, + "tools_registration": True, + "tools_edit": True, + "users_view": True, + "view_requests": True, + "view_all_toolboxes": True, + }, + "employee": { + "title": "Сотрудник", + "description": "Сотрудник. Управление собственной рабочей информацией", + "available_own_toolbox": True, + }, + } + + for accessLevel in baseAcessLevels.values(): + await self.add(**accessLevel) + + logger.info("Уровни доступа успешно инициализированы") + return diff --git a/db/handlers/categories.py b/db/handlers/categories.py new file mode 100644 index 0000000..014adcf --- /dev/null +++ b/db/handlers/categories.py @@ -0,0 +1,92 @@ +from sqlalchemy import select +from utils import logger +from db import CRUD +from db.schemas import Category +from db.handlers import ServiceRecordsHandler + + +class CategoryHandler: + async def add(newCategoryData: dict, user_id: int = None): + title = newCategoryData.get("title", None) + if not title: + logger.error("Не указано название категории") + return {} + query = select(Category).where(Category.title == title) + category = await CRUD.read(query) + if category: + logger.error("Категория с таким названием уже существует") + return {} + try: + newCategory = await Category(**newCategoryData).save() + except Exception as e: + logger.error(f"Ошибка сохранения категории: {str(e)}") + return {} + if not newCategory: + logger.error("Категория не сохранена") + return {} + await ServiceRecordsHandler.add( + user_id, {"Добавлена категория": newCategory.toDict()} + ) + logger.info( + f"Категория {newCategory.title} успешно добавлена, id: {newCategory.id}" + ) + return newCategory.toDict() + + async def edit(categoryId: int, **kwargs): + query = select(Category).where(Category.id == categoryId) + category = await CRUD.read(query) + if not category: + logger.error("Категория не найдена") + return {} + try: + user_id = kwargs.get("user_id", None) + editedCategory = await category.edit(**kwargs) + except Exception as e: + logger.error(f"Ошибка обновления категории: {str(e)}") + return {} + if not editedCategory: + logger.error("Категория не обновлена") + return {} + await ServiceRecordsHandler.add( + user_id, {f"Обновлена категория {category.title}": editedCategory.toDict()} + ) + logger.info(f"Категория {editedCategory.title} успешно обновлена") + return editedCategory.toDict() + + async def getAll() -> list[dict]: + query = select(Category) + categories = await CRUD.read(query, True) + return [category.toDict() for category in categories] if categories else [] + + async def delete(categoryId: int, user_id: int = None): + query = select(Category).where(Category.id == categoryId) + category = await CRUD.read(query) + if not category: + logger.error("Категория не найдена") + return False + try: + categoryTitle = category.title + result = await CRUD.delete(category) + except Exception as e: + logger.error(f"Ошибка удаления категории: {str(e)}") + return False + await ServiceRecordsHandler.add( + user_id, {"Удалена категория": f"Название: {categoryTitle}"} + ) + logger.info( + f"Категория {categoryTitle} {'успешно удалена' if result else 'не удалена'}" + ) + return result + + async def initialize(): + baseCategories = [ + {"title": "Фрезеровка", "description": "Инструмент для фрезерного цеха"}, + {"title": "Токарка", "description": "Инструмент для токарного цеха"}, + {"title": "Слесарка", "description": "Инструмент для слесарного цеха"}, + ] + + for categoryData in baseCategories: + await CategoryHandler.add(categoryData) + + logger.info("Категории успешно созданы") + return diff --git a/db/handlers/records.py b/db/handlers/records.py new file mode 100644 index 0000000..2fad8d5 --- /dev/null +++ b/db/handlers/records.py @@ -0,0 +1,118 @@ +from datetime import datetime, timedelta + +from sqlalchemy import select +from db.schemas import StocksRecords, ServicesRecords +from utils import logger + + +class StocksRecordsHandler: + async def add( + action: str, + source_toolbox_id: int, + target_toolbox_id: int, + toolkit_id: int, + init_user_id: int, + reason: str, + quantity: int, + ): + recordData = { + "action": action, + "source_toolbox_id": source_toolbox_id, + "toolkit_id": toolkit_id, + "init_user_id": init_user_id, + "reason": reason, + "quantity": quantity, + } + if target_toolbox_id: + recordData["target_toolbox_id"] = target_toolbox_id + try: + logger.info(f"Создание записи: {action} от {init_user_id}") + logger.debug(recordData) + record = StocksRecords(**recordData) + await record.save() + logger.info(f"Запись успешно создана, id: {record.id}") + return True + except Exception as e: + logger.error(f"Ошибка создания записи: {str(e)}") + return False + + async def accept(record_id: int, accept_user_id: int): + try: + logger.info(f"Принятие записи {record_id} от {accept_user_id}") + record = await StocksRecords.get(id=record_id) + record.accept_user_id = accept_user_id + record.accepted_at = datetime.now() + await record.save() + logger.info( + f"Запись {record_id} успешно принята {accept_user_id} в {record.accepted_at.strftime('%Y-%m-%d %H:%M:%S')}" + ) + return True + except Exception as e: + logger.error(f"Ошибка принятия записи: {str(e)}") + return False + + async def edit(record_id: int, edit_user_id: int, **kwargs): + try: + logger.info(f"Обновление записи {record_id} от {edit_user_id}") + record = await StocksRecords.get(id=record_id) + record.edit_user_id = edit_user_id + record.edited_at = datetime.now() + edited = {} + for key, value in kwargs.items(): + originalValue = getattr(record, key) + setattr(record, key, value) + edited[key] = {"original": originalValue, "new": value} + record.edited = edited + await record.save() + logger.info( + f"Запись {record_id} успешно обновлена {edit_user_id} в {record.updated_at.strftime('%Y-%m-%d %H:%M:%S')}" + ) + logger.debug(edited) + return True + except Exception as e: + logger.error(f"Ошибка обновления записи: {str(e)}") + return False + + async def get(user_id: int = None, days: int = 30): + from db import CRUD + + try: + if user_id: + userInfo = f"пользователя {user_id} " + query = select(StocksRecords).where( + StocksRecords.init_user_id == user_id, + StocksRecords.created_at > datetime.now() - timedelta(days=days), + ) + else: + userInfo = "всех пользователей " + query = select(StocksRecords).where( + StocksRecords.created_at > datetime.now() - timedelta(days=days), + ) + logger.info(f"Получение всех записей {userInfo}за последние {days} дн.") + records = await CRUD.read(query, True) + logger.info( + f"{len(records)} записей {userInfo}за последние {days} дн. успешно получены" + ) + if len(records) == 0: + return [] + records.sort(key=lambda x: x.created_at, reverse=True) + recordsData = [record.toDict() for record in records] + logger.debug(recordsData) + return recordsData + except Exception as e: + logger.error(f"Ошибка получения записей: {str(e)}") + return False + + +class ServiceRecordsHandler: + async def add(user_id: int, details: dict): + try: + logger.info(f"Создание записи: {user_id}") + logger.debug(details) + record = ServicesRecords(user_id=user_id, details=details) + await record.save() + logger.info(f"Запись успешно создана, id: {record.id}") + return True + except Exception as e: + logger.error(f"Ошибка создания записи: {str(e)}") + return False diff --git a/db/handlers/stock.py b/db/handlers/stock.py new file mode 100644 index 0000000..fa014ca --- /dev/null +++ b/db/handlers/stock.py @@ -0,0 +1,88 @@ +from sqlalchemy import select +from db.schemas import Stock +from utils import logger + + +def filterQuantity(stocksData, filtered): + def filterStock(stock): + if stock.quantity > 0: + return stock + else: + return False + + if isinstance(stocksData, list): + if len(stocksData) == 0: + return [] + stocksData.sort(key=lambda stock: stock.created_at) + filteredStocks = ( + list(filter(filterStock, stocksData)) if filtered else stocksData + ) + return [stock.toDict() for stock in filteredStocks] if filteredStocks else [] + else: + stock = filterStock(stocksData) if filtered else stocksData + if stock: + return stock.toDict() + else: + return {} + + +class StockHandler: + async def add(**kwargs): + newStock = await Stock(**kwargs).save() + logger.info( + f"Новая запись об инструменте {newStock.toolkit_data.title} на складе {newStock.toolbox_data.title} успешно создана" + ) + return newStock.toDict() + + async def getAll(filtered: bool = True): + from db import CRUD + + stocks = await CRUD.read(select(Stock), all=True) + return filterQuantity(stocks, filtered) + + async def get(stockId: int, filtered: bool = True): + from db import CRUD + + stock = await CRUD.read(select(Stock).where(Stock.id == stockId)) + if not stock: + logger.error("Запись об остатках не найдена") + return {} + return filterQuantity(stock, filtered) + + async def getByToolboxId(toolboxId: int, filtered: bool = True): + from db import CRUD + + query = select(Stock).where(Stock.toolbox_id == toolboxId) + stocks = await CRUD.read(query, True) + return filterQuantity(stocks, filtered) + + async def getByToolkitId(toolkitId: int, filtered: bool = True): + from db import CRUD + + query = select(Stock).where(Stock.toolkit_id == toolkitId) + stocks = await CRUD.read(query, True) + return filterQuantity(stocks, filtered) + + async def edit(stockId: int, **kwargs): + from db import CRUD + + stock = await CRUD.read(select(Stock).where(Stock.id == stockId)) + if not stock: + logger.error("Запись об остатках не найдена") + return {} + try: + stockInfo = f"инструмента {stock.toolkit_data.title} на складе {stock.toolbox_data.title}" + editedStock = await stock.edit(**kwargs) + except Exception as e: + logger.error(f"Ошибка обновления записи об остатках: {str(e)}") + return {} + if not editedStock: + logger.error("Запись об остатках не обновлена") + return {} + logger.info( + f"Запись об остатках {stockInfo} успешно обновлена, изменены данные: {kwargs.keys()}" + ) + return editedStock.toDict() + + async def initialize(): + pass diff --git a/db/handlers/toolbox.py b/db/handlers/toolbox.py index f89d5a8..9acf859 100644 --- a/db/handlers/toolbox.py +++ b/db/handlers/toolbox.py @@ -2,18 +2,112 @@ from utils import logger from db import CRUD from db.schemas import Toolbox from sqlalchemy import select +from db.handlers import ServiceRecordsHandler -async def addNewToolbox(toolboxData: dict): - title = toolboxData.get("title", None) - if not title: - logger.error("Не указано Назавание тулбокса") - return {} - query = select(Toolbox).where(Toolbox.title == title) - toolbox = await CRUD.read(query) - if toolbox: - logger.error("Тулбокс с таким названием уже существует") - return {} - newToolbox = await Toolbox(**toolboxData).save() - logger.info(f"Тулбокс {newToolbox.title} успешно создан") - return newToolbox.toDict() +class ToolboxHandler: + async def add(toolboxData: dict, user_id: int = None): + title = toolboxData.get("title", None) + if not title: + logger.error("Не указано Назавание тулбокса") + return {} + query = select(Toolbox).where(Toolbox.title == title) + toolbox = await CRUD.read(query) + if toolbox: + logger.error("Тулбокс с таким названием уже существует") + return {} + + try: + logger.info(f"Создание тулбокса {title}") + newToolbox = await Toolbox(**toolboxData).save() + except Exception as e: + logger.error(f"Ошибка сохранения тулбокса: {str(e)}") + return {} + + if not newToolbox: + logger.error("Тулбокс не сохранен") + return {} + + await ServiceRecordsHandler.add( + user_id, {"Добавлен тулбокс": newToolbox.toDict()} + ) + logger.info(f"Тулбокс {newToolbox.title} успешно создан") + return newToolbox.toDict() + + async def edit(toolboxId: int, **kwargs): + query = select(Toolbox).where(Toolbox.id == toolboxId) + toolbox = await CRUD.read(query) + if not toolbox: + logger.error("Тулбокс не найден") + return {} + try: + user_id = kwargs.pop("user_id", None) + editedToolbox = await toolbox.edit(**kwargs) + except Exception as e: + logger.error(f"Ошибка обновления тулбокса: {str(e)}") + return {} + if not editedToolbox: + logger.error("Тулбокс не обновлен") + return {} + logger.info( + f"Тулбокс {editedToolbox.title} успешно обновлен, изменены данные: {kwargs.keys()}" + ) + await ServiceRecordsHandler.add( + user_id, {f"Обновлен тулбокс {toolbox.title}": editedToolbox.toDict()} + ) + return editedToolbox.toDict() + + async def getAll() -> list: + query = select(Toolbox) + toolboxes = await CRUD.read(query, True) + return [toolbox.toDict() for toolbox in toolboxes] if toolboxes else [] + + async def get(toolboxId: int) -> dict: + query = select(Toolbox).where(Toolbox.id == toolboxId) + toolbox = await CRUD.read(query) + if not toolbox: + logger.error("Тулбокс не найден") + return {} + return toolbox.toDict() + + async def delete(toolboxId: int, user_id: int = None): + query = select(Toolbox).where(Toolbox.id == toolboxId) + toolbox = await CRUD.read(query) + if not toolbox: + logger.error("Тулбокс не найден") + return False + try: + toolboxTitle = toolbox.title + result = await CRUD.delete(toolbox) + except Exception as e: + logger.error(f"Ошибка удаления тулбокса: {str(e)}") + return False + logger.info( + f"Тулбокс {toolboxTitle} {'успешно удален' if result else 'не удален'}" + ) + await ServiceRecordsHandler.add( + user_id, {"Удален тулбокс": f"Название: {toolboxTitle}"} + ) + return result + + async def initialize(): + baseToolsboxes = [ + { + "title": "Стеллаж", + "description": "Основной стеллаж с режущим инструментом", + "owner_id": None, + "monitoring": True, + }, + { + "title": "Шкаф", + "description": "Шкаф для хранения инструментов", + "owner_id": None, + "monitoring": True, + }, + ] + + for toolboxData in baseToolsboxes: + await ToolboxHandler.add(toolboxData) + + logger.info("Тулбоксы успешно созданы") + return diff --git a/db/handlers/toolkit.py b/db/handlers/toolkit.py new file mode 100644 index 0000000..ed8a193 --- /dev/null +++ b/db/handlers/toolkit.py @@ -0,0 +1,288 @@ +from utils import logger, saveImage, safeFilename, deleteImage +from db import CRUD +from db.schemas import Toolkit +from sqlalchemy import select +from db.handlers import ServiceRecordsHandler + + +def handleToolkitImage(imageData, title: str): + title = safeFilename(title) + fileName = f"tools/{title}.png" + if not saveImage(imageData, fileName): + return None + return fileName + + +class ToolkitHandler: + async def add(toolkitData: dict, user_id: int = None): + title = toolkitData.get("title", None) + if not title: + logger.error("Не указано название инструмента") + return {} + + query = select(Toolkit).where(Toolkit.title == title) + toolkit = await CRUD.read(query) + if toolkit: + logger.error("Инструмент с таким названием уже существует") + return {} + + try: + imageDict = {"main": "images/tools/default.png", "additional": []} + if "image" in toolkitData: + imageData = toolkitData.pop("image") + mainImage = imageData.get("main") + imageFileName = handleToolkitImage(mainImage, title) + if imageFileName: + imageDict["main"] = imageFileName + additionalImages = imageData.get("additional", []) + if len(additionalImages) > 0: + for image in additionalImages: + imageFileName = handleToolkitImage(image, title) + if imageFileName: + imageDict["additional"].append(imageFileName) + toolkitData["image"] = imageDict + newToolkit = await Toolkit(**toolkitData).save() + except Exception as e: + logger.error(f"Ошибка сохранения инструмента: {str(e)}") + return {} + + if not newToolkit: + logger.error("Инструмент не сохранен") + return {} + + logger.info(f"Инструмент {newToolkit.title} успешно создан") + await ServiceRecordsHandler.add( + user_id, {"Добавлен инструмент": toolkitData.toDict()} + ) + return newToolkit.toDict() + + async def edit(toolkitId: int, **kwargs): + query = select(Toolkit).where(Toolkit.id == toolkitId) + toolkit = await CRUD.read(query) + if not toolkit: + logger.error("Инструмент не найден") + return {} + try: + if "image" in kwargs: + title = kwargs.get("title", toolkit.title) + + imageData = kwargs.pop("image") + imageDict = {"main": "", "additional": []} + + existImagesList = [toolkit.image.get("main")] + existImagesList.extend(toolkit.image.get("additional")) + + newImagesList = [imageData.get("main")] + newImagesList.extend(imageData.get("additional")) + + for existImage in existImagesList: + if existImage not in newImagesList: + deleteImage(existImage) + + if toolkit.image.get("main") != imageData.get("main"): + if imageData.get("main") in existImagesList: + imageDict["main"] = imageData.get("main") + else: + imageFileName = handleToolkitImage(imageData.get("main"), title) + if imageFileName: + imageDict["main"] = imageFileName + else: + imageDict["main"] = "images/tools/default.png" + + imageDict["additional"].extend(imageData.get("additional")) + + uploadList = imageData.get("upload", []) + if len(uploadList) > 0: + for image in uploadList: + imageFileName = handleToolkitImage(image, title) + if imageFileName: + imageDict["additional"].append(imageFileName) + + kwargs["image"] = imageDict + user_id = kwargs.pop("user_id", None) + editedToolkit = await toolkit.edit(**kwargs) + except Exception as e: + logger.error(f"Ошибка обновления инструмента: {str(e)}") + return {} + + if not editedToolkit: + logger.error("Инструмент не обновлен") + return {} + + logger.info( + f"Инструмент {editedToolkit.title} успешно обновлен, изменены данные: {kwargs.keys()}" + ) + await ServiceRecordsHandler.add( + user_id, {f"Обновлен инструмент {toolkit.title}": editedToolkit.toDict()} + ) + return editedToolkit.toDict() + + async def getAll(): + query = select(Toolkit) + toolkits = await CRUD.read(query, True) + return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] + + async def get(toolkitId: int): + query = select(Toolkit).where(Toolkit.id == toolkitId) + toolkit = await CRUD.read(query) + if not toolkit: + logger.error("Инструмент не найден") + return {} + return toolkit.toDict() + + async def getSeveral(toolkitIds: list[int]) -> list[dict]: + query = select(Toolkit).where(Toolkit.id.in_(toolkitIds)) + toolkits = await CRUD.read(query, True) + return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] + + async def delete(toolkitId: int, user_id: int = None): + query = select(Toolkit).where(Toolkit.id == toolkitId) + toolkit = await CRUD.read(query) + if not toolkit: + logger.error("Инструмент не найден") + return False + try: + toolkitTitle = toolkit.title + result = await CRUD.delete(toolkit) + except Exception as e: + logger.error(f"Ошибка удаления инструмента: {str(e)}") + return False + logger.info( + f"Инструмент {toolkitTitle} {'успешно удален' if result else 'не удален'}" + ) + await ServiceRecordsHandler.add( + user_id, {"Удален инструмент": f"Название: {toolkitTitle}"} + ) + return result + + async def initialize(self): + from .categories import CategoryHandler + + categoriesList = await CategoryHandler.getAll() + categories = {category["title"]: category["id"] for category in categoriesList} + + baseToolkits = [ + { + "title": "Фреза №1", + "description": "Фреза такая сякая этакая #1", + "specifications": { + "Диаметр": "10", + "Длина": "20", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Фрезеровка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Фреза №2", + "description": "Фреза такая сякая этакая #2", + "specifications": { + "Диаметр": "10", + "Длина": "20", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Фрезеровка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Фреза №3", + "description": "Фреза такая сякая этакая #3", + "specifications": { + "Диаметр": "10", + "Длина": "20", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Фрезеровка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Псластина №1", + "description": "Пластина такая сякая этакая #1", + "specifications": { + "Размер": "10", + "Радиус": "0.4", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Токарка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Псластина №2", + "description": "Пластина такая сякая этакая #2", + "specifications": { + "Размер": "10", + "Радиус": "0.4", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Токарка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Псластина №3", + "description": "Пластина такая сякая этакая #3", + "specifications": { + "Размер": "10", + "Радиус": "0.4", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Токарка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Сверло №1", + "description": "Сверло такое сякое этакое #1", + "specifications": { + "Длина": "30", + "Диаметр": "5", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Слесарка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Сверло №2", + "description": "Сверло такое сякое этакое #2", + "specifications": { + "Длина": "30", + "Диаметр": "5", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Слесарка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + { + "title": "Сверло №3", + "description": "Сверло такое сякое этакое #3", + "specifications": { + "Длина": "30", + "Диаметр": "5", + "Ещё что-то": "Ещё столько-то", + }, + "category_id": categories["Слесарка"], + "quantity_min": 20, + "quantity_min_extra": 10, + "external_link": "https://nazv.ru", + }, + ] + + for toolkit in baseToolkits: + await self.add(toolkit) + + logger.info("Базовые инструменты успешно созданы") + return diff --git a/db/handlers/user.py b/db/handlers/user.py index 0a3717a..591a544 100644 --- a/db/handlers/user.py +++ b/db/handlers/user.py @@ -1,81 +1,222 @@ from sqlalchemy import or_, select from db import CRUD from db.handlers.toolbox import addNewToolbox +from utils import logger, pwd_hash, saveImage, safeFilename, deleteImage, pwd_verify from db.schemas import User -from utils import logger, pwd_hash +from db.handlers import ServiceRecordsHandler -async def addNewUser(userData: dict) -> dict: - login = userData.get("login", None) - if not login: - logger.error("Не указан логин") - return {} - userName = userData.get("username") - if not userName: - logger.error("Не указано имя пользователя") - return {} - query = select(User).where(or_(User.login == login, User.username == userName)) - user = await CRUD.read(query) - if user: - logger.error("Пользователь с таким логином или именем уже существует") - return {} - if "access_level_id" not in userData: - logger.error("Не указан уровень доступа") - return {} - if "password" not in userData: - logger.error("Не указан пароль") - return {} - userData["hashed_password"] = pwd_hash(userData.pop("password")) - newUser = await User(**userData).save() - if not newUser: - logger.error("Ошибка сохранения пользователя") - return {} - logger.info(f"Пользователь {newUser.username} успешно добавлен, id: {newUser.id}") - if newUser.available_own_toolbox: - newToolboxData = { - "title": f"Тулбокс {newUser.username}", - "description": f"Оборудование, полученное сотрудником {newUser.username}, под личную материальную ответственность", - "owner_id": newUser.id, - } - newToolbox = await addNewToolbox(newToolboxData) +def handleUserPhoto(imageData, login: str): + login = safeFilename(login) + fileName = f"users/{login}.png" + if not saveImage(imageData, fileName): + return None + return fileName + + +class UserHandler: + async def add(userData: dict, user_id: int = None) -> dict: + login = userData.get("login", None) + if not login: + logger.error("Не указан логин") + return {} + userName = userData.get("username", None) + if not userName: + logger.error("Не указано имя пользователя") + return {} + query = select(User).where(or_(User.login == login, User.username == userName)) + user = await CRUD.read(query) + if user: + logger.error("Пользователь с таким логином или именем уже существует") + return {} + if "access_level_id" not in userData: + logger.error("Не указан уровень доступа") + return {} + if "password" not in userData: + logger.error("Не указан пароль") + return {} + userData["hashed_password"] = pwd_hash(userData.pop("password")) + if "photo" in userData: + imageData = userData.pop("photo") + photoFile = handleUserPhoto(imageData, login) + if photoFile: + userData["photo"] = photoFile + try: + newUser = await User(**userData).save() + except Exception as e: + logger.error(f"Ошибка сохранения пользователя: {str(e)}") + return {} + if not newUser: + logger.error("Пользователь не сохранен") + return {} logger.info( - f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {newUser.username}" + f"Пользователь {newUser.username} успешно добавлен, id: {newUser.id}" ) - return newUser.toDict() - - -async def editUser(userData: dict) -> dict: - id = userData.get("id", None) - if not id: - logger.error("Не указан id пользователя") - return {} - query = select(User).where(User.id == id) - user = await CRUD.read(query) - if not user: - logger.error("Пользователь с таким id не найден") - return {} - changedUserData = userData.get("changedUserData", {}) - if len(changedUserData.keys()) == 0: - logger.error("Не указаны изменяемые данные") - return {} - if "password" in changedUserData: - userData["hashed_password"] = pwd_hash(changedUserData.pop("password")) - editedUser = await user.edit(**changedUserData) - if not editedUser: - logger.error("Ошибка обновления пользователя") - return {} - logger.info( - f"Пользователь {editedUser.username} успешно обновлен, изменены данные: {changedUserData.keys()}" - ) - if not user.available_own_toolbox: - if editedUser.available_own_toolbox: + if newUser.available_own_toolbox: newToolboxData = { - "title": f"Тулбокс {editedUser.username}", - "description": f"Оборудование, полученное сотрудником {editedUser.username}, под личную материальную ответственность", - "owner_id": editedUser.id, + "title": f"Тулбокс {newUser.username}", + "description": f"Оборудование, полученное сотрудником {newUser.username}, под личную материальную ответственность", + "owner_id": newUser.id, } newToolbox = await addNewToolbox(newToolboxData) logger.info( - f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {editedUser.username}" + f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {newUser.username}" ) - return editedUser.toDict() + await ServiceRecordsHandler.add( + user_id, {"Добавлен пользователь": newUser.toDict()} + ) + return newUser.toDict() + + async def edit(userData: dict, user_id: int = None) -> dict: + id = userData.get("id", None) + if not id: + logger.error("Не указан id пользователя") + return {} + query = select(User).where(User.id == id) + user = await CRUD.read(query) + if not user: + logger.error("Пользователь с таким id не найден") + return {} + changedUserData = userData.get("changedUserData", {}) + if len(changedUserData.keys()) == 0: + logger.error("Не указаны изменяемые данные") + return {} + if "password" in changedUserData: + userData["hashed_password"] = pwd_hash(changedUserData.pop("password")) + if "photo" in changedUserData: + imageData = changedUserData.pop("photo") + photoFile = handleUserPhoto(imageData, user.login) + if photoFile: + changedUserData["photo"] = photoFile + deleteImage(user.photo) + try: + editedUser = await user.edit(**changedUserData) + except Exception as e: + logger.error(f"Ошибка обновления пользователя: {str(e)}") + return {} + if not editedUser: + logger.error("Ошибка обновления пользователя") + return {} + logger.info( + f"Пользователь {editedUser.username} успешно обновлен, изменены данные: {changedUserData.keys()}" + ) + if not user.available_own_toolbox: + if editedUser.available_own_toolbox: + newToolboxData = { + "title": f"Тулбокс {editedUser.username}", + "description": f"Оборудование, полученное сотрудником {editedUser.username}, под личную материальную ответственность", + "owner_id": editedUser.id, + } + newToolbox = await addNewToolbox(newToolboxData) + logger.info( + f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {editedUser.username}" + ) + await ServiceRecordsHandler.add( + user_id, {"Изменен пользователь": editedUser.toDict()} + ) + return editedUser.toDict() + + async def getAll() -> list[dict]: + query = select(User) + users = await CRUD.read(query, True) + return [user.toDict() for user in users] + + async def get(id: int) -> dict: + query = select(User).where(User.id == id) + user = await CRUD.read(query) + if not user: + logger.error("Пользователь с таким id не найден") + return {} + return user.toDict() + + async def delete(id: int, user_id: int = None) -> bool: + query = select(User).where(User.id == id) + user = await CRUD.read(query) + if not user: + logger.error("Пользователь с таким id не найден") + return False + try: + userName = user.username + result = await CRUD.delete(user) + except Exception as e: + logger.error(f"Ошибка удаления пользователя: {str(e)}") + return False + logger.info( + f"Пользователь {userName} {'успешно удален' if result else 'не удален'}" + ) + await ServiceRecordsHandler.add(user_id, {"Удален пользователь": userName}) + return result + + async def deletePhoto(id: int, user_id: int = None) -> bool: + query = select(User).where(User.id == id) + user = await CRUD.read(query) + if not user: + logger.error("Пользователь с таким id не найден") + return False + try: + deleteImage(user.photo) + user.photo = "images/users/default.png" + await user.save() + except Exception as e: + logger.error(f"Ошибка удаления фото пользователя: {str(e)}") + return False + logger.info(f"Фото пользователя {user.username} успешно удалено") + await ServiceRecordsHandler.add( + user_id, {"Удалено фото пользователя": user.username} + ) + return True + + async def auth(login: str, password: str) -> dict: + query = select(User).where(User.login == login) + user = await CRUD.read(query) + if not user: + logger.error("Пользователь с таким логином не найден") + return {} + if not pwd_verify(password, user.hashed_password): + logger.error("Неверный пароль") + return {} + userData = user.toDict() + userData.pop("hashed_password") + await ServiceRecordsHandler.add( + user.id, {"Авторизован пользователь": user.username} + ) + return userData + + async def initialize(self): + from .access import AccessLevelHandler + + accessLevelsList = await AccessLevelHandler.getAll() + acessLevels = { + accessLevel["title"]: accessLevel["id"] for accessLevel in accessLevelsList + } + baseUsers = { + "admin": { + "login": "admin", + "username": "Администратор", + "password": "Alex0172", + "access_level_id": acessLevels["Администратор"], + }, + "manager": { + "login": "manager", + "username": "Менеджер", + "password": "Alex0172", + "access_level_id": acessLevels["Менеджер"], + }, + "storekeeper": { + "login": "storekeeper", + "username": "Кладовщик", + "password": "Alex0172", + "access_level_id": acessLevels["Кладовщик"], + }, + "employee": { + "login": "employee", + "username": "Сотрудник", + "password": "Alex0172", + "access_level_id": acessLevels["Сотрудник"], + }, + } + for user in baseUsers.values(): + await self.add(user) + + logger.info("Инициализация модуля пользователей завершена") + return diff --git a/db/schemas/__init__.py b/db/schemas/__init__.py index 2eb570f..314ee01 100644 --- a/db/schemas/__init__.py +++ b/db/schemas/__init__.py @@ -1,6 +1,144 @@ -from user import * -from access import * -from toolkit import * -from categories import * -from db.schemas.toolbox import * -from stock import * +from .user import * +from .access import * +from .toolkit import * +from .categories import * +from .toolbox import * +from .stock import * + +from typing import Optional +from sqlalchemy import inspect, text +from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine +from sqlalchemy.schema import CreateTable +from utils import logger + +# Импортируем все модели +from db import Base +from db.handlers import InitializeDatabase + + +class DatabaseInitializer: + existing_tables: Optional[list[str]] = None + + def __init__(self, database_url: str): + self.database_url = database_url + self.engine: Optional[AsyncEngine] = None + self.metadata = Base.metadata + + async def initialize(self, force: bool = False, reNewDB: bool = False): + """Main database initialization method""" + try: + self.engine = create_async_engine(self.database_url) + + async with self.engine.begin() as conn: + if force: + logger.info("Принудительное удаление и создание баз...") + await self._drop_all() + await self._create_tables_directly() + await self._initialize_data() + elif not await self._check_tables_exist(conn): + logger.info("Не все необходимые таблицы существуют. Создаем...") + await self._create_tables_directly() + + # Проверяем после создания + async with self.engine.begin() as conn: + if not await self._check_tables_exist(conn): + raise RuntimeError("Не все необходимые таблицы существуют!") + + if reNewDB: + logger.info("Принудительная загрузка данных...") + await self._initialize_data() + else: + logger.info("Все необходимые таблицы существуют. Пропускаем...") + + except Exception as e: + logger.error(f"Инициализация базы завершилась ошибкой: {str(e)}") + raise + finally: + if self.engine: + await self.engine.dispose() + + async def _check_tables_exist(self, conn) -> bool: + """Check if all tables from metadata exist""" + try: + DatabaseInitializer.existing_tables = await conn.run_sync( + lambda sync_conn: inspect(sync_conn).get_table_names() + ) + + required_tables = set(self.metadata.tables.keys()) + + if not required_tables: + logger.error("Нет данных о таблицах в метаданных") + return False + + missing_tables = required_tables - set(DatabaseInitializer.existing_tables) + if missing_tables: + logger.warning("Существующие таблицы:") + logger.info(DatabaseInitializer.existing_tables) + logger.warning("Необходимые таблицы:") + logger.info(required_tables) + logger.warning("Отсутствующие таблицы:") + logger.info(missing_tables) + return False + + return True + except Exception as e: + logger.warning(f"Проверка таблиц завершилась ошибкой: {str(e)}") + return False + + async def _create_tables_directly(self): + """Create tables directly using SQLAlchemy (bypass Alembic)""" + async with self.engine.begin() as conn: + # Создаем все таблицы из метаданных + for table in self.metadata.sorted_tables: + try: + if ( + DatabaseInitializer.existing_tables + and table.name in DatabaseInitializer.existing_tables + ): + logger.debug( + f"Таблица {table.name} уже существует. Пропускаем..." + ) + continue + create_stmt = CreateTable(table) + logger.info(f"Создаем таблицу: {table.name}") + await conn.execute(create_stmt) + except Exception as e: + logger.error(f"Ошибка создания таблицы: {str(e)}") + logger.warning("Все таблицы успешно созданы") + + async def _drop_all(self): + """Drop all tables""" + async with self.engine.begin() as conn: + # Получаем список всех таблиц + existing_tables = await conn.run_sync( + lambda sync_conn: inspect(sync_conn).get_table_names() + ) + + for table in existing_tables: + drop_stmt = text( + f'DROP TABLE "{table}" CASCADE' + ) # Кавычки на случай спец. символов + logger.info(f"Удаляем таблицу: {table}") + await conn.execute(drop_stmt) + + logger.warning("Все таблицы успешно удалены") + + async def _initialize_data(self): + """Initialize required data""" + try: + logger.info("Инициализация данных...") + await InitializeDatabase.initialize() + logger.info("Данные успешно инициализированы") + except Exception as e: + logger.error(f"Инициализация данных завершилась ошибкой: {str(e)}") + raise + + +__all__ = [ + "User", + "Access", + "Toolbox", + "Category", + "Stock", + "Toolkit", +] \ No newline at end of file diff --git a/db/schemas/access.py b/db/schemas/access.py index 726d2bd..7004bc8 100644 --- a/db/schemas/access.py +++ b/db/schemas/access.py @@ -23,7 +23,7 @@ class AccessLevel(Base): tools_registration = Column(Boolean, default=False) tools_registration_edit = Column(Boolean, default=False) tools_edit = Column(Boolean, default=False) - tools_achievement = Column(Boolean, default=False) + tools_delete = Column(Boolean, default=False) users_creation = Column(Boolean, default=False) users_edit = Column(Boolean, default=False) users_disabling = Column(Boolean, default=False) @@ -31,6 +31,7 @@ class AccessLevel(Base): available_own_toolbox = Column(Boolean, default=False) view_all_toolboxes = Column(Boolean, default=False) view_requests = Column(Boolean, default=False) + view_services = Column(Boolean, default=False) access_level_view = Column(Boolean, default=False) access_level_edit = Column(Boolean, default=False) manage_toolboxes = Column(Boolean, default=False) diff --git a/db/schemas/categories.py b/db/schemas/categories.py index 7306e99..bef42b9 100644 --- a/db/schemas/categories.py +++ b/db/schemas/categories.py @@ -9,7 +9,7 @@ class Category(Base): id = Column(Integer, primary_key=True, index=True) title = Column(String, unique=True, index=True) - description = Column(Text) + description = Column(Text, nullable=True) created_at = Column(DateTime, default=datetime.now) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) diff --git a/db/schemas/records.py b/db/schemas/records.py new file mode 100644 index 0000000..ef6281b --- /dev/null +++ b/db/schemas/records.py @@ -0,0 +1,75 @@ +from datetime import datetime +from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text +from sqlalchemy.dialects.postgresql import JSONB +from db import Base +import utils + + +class StocksRecords(Base): + __tablename__ = "stocks_records" + + id = Column(Integer, primary_key=True, index=True) + action = Column(String, nullable=False) + source_toolbox_id = Column( + Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=False + ) + target_toolbox_id = Column( + Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=True + ) + toolkit_id = Column( + Integer, ForeignKey("toolkits.id", ondelete="CASCADE"), nullable=False + ) + init_user_id = Column( + Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False + ) + accept_user_id = Column( + Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True + ) + reason = Column(Text, nullable=False) + quantity = Column(Integer, nullable=False) + edit_user_id = Column( + Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True + ) + edited = Column(JSONB, nullable=True) + created_at = Column(DateTime, default=datetime.now) + accepted_at = Column(DateTime, nullable=True) + edited_at = Column(DateTime, nullable=True) + updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) + + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + def toDict(self): + return utils.toDict(self) + + async def save(self): + from db import CRUD + + return await CRUD.create(self, refresh=True) + + async def edit(self, **kwargs): + from db import CRUD + + return await CRUD.update(StocksRecords, self.id, **kwargs) + + +class ServicesRecords(Base): + __tablename__ = "services_records" + + id = Column(Integer, primary_key=True, index=True) + user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True) + details = Column(JSONB, nullable=False) + created_at = Column(DateTime, default=datetime.now) + + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + def toDict(self): + return utils.toDict(self) + + async def save(self): + from db import CRUD + + return await CRUD.create(self, refresh=True) diff --git a/db/schemas/stock.py b/db/schemas/stock.py index 96cfbc6..6ccdf81 100644 --- a/db/schemas/stock.py +++ b/db/schemas/stock.py @@ -1,5 +1,5 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer +from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, String from sqlalchemy.orm import relationship from db import Base @@ -24,6 +24,7 @@ class Stock(Base): ) quantity = Column(Integer, nullable=False) price = Column(Float, nullable=False) + placement = Column(String, nullable=True) created_at = Column(DateTime, default=datetime.now) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) diff --git a/db/schemas/toolbox.py b/db/schemas/toolbox.py index f04aac5..14a1a17 100644 --- a/db/schemas/toolbox.py +++ b/db/schemas/toolbox.py @@ -1,5 +1,5 @@ from datetime import datetime -from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text +from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text from db import Base import utils @@ -9,10 +9,11 @@ class Toolbox(Base): id = Column(Integer, primary_key=True, index=True) title = Column(String, unique=True, index=True) - description = Column(Text) + description = Column(Text, nullable=True) owner_id = Column( - Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True + Integer, ForeignKey("users.id", ondelete="CASCADE"), default=None, nullable=True ) + monitoring = Column(Boolean, default=False) created_at = Column(DateTime, default=datetime.now) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) diff --git a/db/schemas/toolkit.py b/db/schemas/toolkit.py index cd7b849..ebf4c22 100644 --- a/db/schemas/toolkit.py +++ b/db/schemas/toolkit.py @@ -11,14 +11,13 @@ class Toolkit(Base): id = Column(Integer, primary_key=True, index=True) title = Column(String, unique=True, index=True) - description = Column(Text) + description = Column(Text, nullable=True) + specifications = Column(JSONB, default={}) category_id = Column(Integer, ForeignKey("categories.id", ondelete="CASCADE")) category_data = relationship( "Category", cascade="all, delete-orphan", lazy="joined", uselist=False ) - image = Column( - JSONB, default={"main": "images/tools/default.png", "additional": []} - ) + image = Column(JSONB) quantity_min = Column(Integer, nullable=True) quantity_min_extra = Column(Integer, nullable=True) external_link = Column(String, nullable=True) diff --git a/main.py b/main.py index fb04cd0..c771ae3 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,21 @@ +from utils import logger, setLogLevel + + def main(): - print("Hello from tools-stock!") + setLogLevel("WARNING") + logger.info("Приложение запущено") + + logger.info("Получение данных из базы...") + logger.warning({"query": "SELECT * FROM tools", "status": "slow"}) + setLogLevel("INFO") + logger.info("Пользователь открыл страницу настроек") + logger.debug(["Ошибка загрузки интерфейса", 502, "Bad Gateway"]) + setLogLevel("DEBUG") + test_data = {"a": 1, "b": 2, "c": [10, 20]} + logger.info(test_data) + + logger.debug("Приложение завершено") if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 30a3d20..31181b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ requires-python = ">=3.13" dependencies = [ "colorlog>=6.10.1", "passlib>=1.7.4", + "pillow>=12.0.0", "python-dotenv>=1.2.1", "sqlalchemy>=2.0.44", ] diff --git a/static/images/tools/default.png b/static/images/tools/default.png new file mode 100644 index 0000000000000000000000000000000000000000..cbe4b496dcd0f89f423a2408c5c189e6d1b5a4ec GIT binary patch literal 70321 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 Iymi?B0Yt)AI{*Lx literal 0 HcmV?d00001 diff --git a/static/images/users/default.png b/static/images/users/default.png new file mode 100644 index 0000000000000000000000000000000000000000..6cb2d7ab017973092df7a3842e207b089de4ed18 GIT binary patch literal 52120 zcmeFZcRZHw`v-iJC>br;t4Kz+>`^3JB`Z5w+1c3%krYC9Mr7Xho+;wa4%xD|?9Fpr zcc1U?@8|jd@p<{k?YhqMypHo2@8f+O=M{8cf9yd8wV8|PXQkiILa&EB^W(Jd|v1V z#r8%x3dMkum%07Ob#Q6K%_DL;;pAk;Gbv(=iII{0yoR5Tp*?L^6wXb;a#V0B=a`pe zVCmqqgnesEa!a%L`SRk_cS(*}B_)0H=DmF?w96?PqJ5lKvY%YQJ8LQD^XmPpC5-Uw z@llw=SI?EWl}xav|JPqW1y4Ki{#`BP`}V1XDe)gm zt!DSh{{}FgmuacxpgRBWSG#*n)9oonIN0Cp#CSpr&?dx_{dAdxcNVpFJN~<8m|}9= zlla|G`*<5w;IbKUj{`o>-oU>}>M3|-$B&3A0)5*X4+lEDZYdv84%BvP6V;hT#FCzTJwIcs+(2IrRNw#*XU zt{=3rqnD!6=#T#f3j0dcukIYMl$Dgw?tWvD=;CZ~UC7K(Sy)-IsXg9bjANQ_VM&Uo zB?%m=tg2F$pNSQ)JdD0*oO1Tk^~-3Tvb=rwu>*YzXXou# z+uPfXf@2;IJ|tG z&eayz!?kAB+Wm!W7d`Kzt()OLo9W!G+6DS`i#FmN+B47k-w9wco^Flg>(A4xvS@gD zI?ii7BFQn)Wz;Q3A_;9bQt4F1v}aSj*3{T<;C+-A5)uMSv9PdcpZNZU6s=Y8MA6I3 zE0d`xI~z0B-F*=|m4=3fO7znYk&&m{g=cBTkS*xx>od*Mt$42J;83)eurbx# zrEaBial7TgpYo?I;hC6duCevFvBPxbaQi?0@e;V&mg$IB82OGV0VZf-ZD zOq4hGJQ}{gA@5gwsHv&>2TUZcu&78!MPhF%DEx(Hj#d}LmL^SX`T%FCEx*1xEAo6w?f^89SI}ly>)^NXyM)8d9@W*oHe0VtL?A~K?U8$- zG!Il&-?6q3;rrD#HOX#mZOzA+C0ficE&U?(`s+s-e0j`shA-)C{ELOm>E!!z;Xgsu zy*C$&OG`^{YUS6PX-9b-EgF=`^S*5OlzjZY!*wEvrSh?kj{5!kpI*MiJ(?h|Gncd* z_wkSzvp9E!!~AePx^9?eFGGi2GjFp5Bt&-Q3YbtLAzHsiBsDWLvnD_PK3DDTlYTC* zwKr@_&tmbMDRu(J3yj^m%i%d19gwvT$`!&o(^Si%z z@sfaq=KkHg{@0abndG|+yUGk3UgmUnch{Hn>mR7t+S>A4(d_CIHU1c?b}JGQ6>S4I zkm-H%yKe~r@*vaTcQ6q)<$nTj8@U~)eq2zF<(E%7oMIVkX>BEp2c_e!_>kZk_S%0w;9$!&8)oa{bR26#}5iuj%(LadkS;%_fHl792^|<4Bz~ANj3iY zh*Q7yG7Nsa+bo&5pCNga$wxtCD%ZVJyHnG8?Ov!O2th(#*4_OejDXJ7Fw}^`3sYFg z6a$+yFfuYSSZ15VS+!Kiizy7*=#Z2&2(NJ4nCa}*G@va%SgBIwWM{`jKXr6+im3xv zMjmh{*`|k|@0Y=znNp(Iz;J=_J)w&$F|@R_ruXgzx(!*El}PN4`xW)04J7Mwb90?~ z(v?R*p>F)ZU@&_dNhdV5N83Yhj=bueW)oa)?RnJJOOuK_w)&Au2rU*R9WAXWa@Fv! zR*fC4w7|EE8Mt4Yj8F}_tc(mZ2B8uTMQdwoo8oln(LoWZZk1=!*+hWq1oQ)a1xF;5H)ysd+JA7jb_SD5S?)8 zDxcy*(=^%?>xrImy_gZ_^zErhvn&7HZt6F=janI_{(58Zm^@yasq|ZeD|~vRJ;z?M zo}M)&;M>3U>$@)#z@KC6`t@9T-Nalzy+D_j|9-L6tLaX=YR$Auhwo|quK7|)$H(Kq zf6s}!#+dN8Q*O;OA<}8>s|cD+t_*Dw9gjmN-4~MFit@5EdzDTLgMd5bL)2}~2T=<`- zE2e04tm~iDCwmjsym9kgzm7d;qe5O`$FguhbAMQKze82xoIr~P#bfC`(o_E)UG`4p zZKLYh>pI#LtHSB{=m>N2P~8{Gq@@Of+6tb;&93Tx8u7D_>Ci0ya|JvN6cdwciS)Lo z!eyvGej74mrYf#D{rb6E;?S~8Tqe^Cw9KmHQiGAm3FW^j>hDuFZqlIpnFj5?-_+V1 z)M94qdNlp?me7Nj=&%L%&sE!3|1kc$v`N4i&mif>r#FIce^ZL37afn=d91e7_P>4= z(72WKAGS)vP<-Cudsx0)LTAL=<2yN$w6J6Sd2cuWbl|l$`u|<@m7;M+AiSI|^5+Y` z#x+}|*_s?i35kUh;eRtpWrgr~d81K0y`MU%0#>`=yPs8#6aG>f`pbUX-!m{`Zlgmkw`gx}u^B9|)nT49;AEf7lHtOhH(u!OW`8SC5$odb|pTtKtvgGee5@ z2-yjzM%qzr9E+i%o>4{KRyIBa{ z4z+x36Gf9ZI&#W0h^{DLci%CI04`D~g(*W@94OFP4X(YfWaAGm)~JebG=J`&Y^iRd z3fNJZwuj5?CLqJ%Hl4kx-%$|mk;u4uzVlcsYp2SyO=e7$mn^`>C~3%{nof1|1-rku zV&OX&bX-XEEX+XhZkD}?y9TXEmi))wAY~|Mw0ZGG3aZ0vgnd(SrXmWnk;^nT=9UDO zwF7!JE+j;W+=ZTH4N51<*f`ct{>8Gu=bpk}~u(6(=h~Cjry1A8=_Ida&H$t2-_V&kz*vJ{5K^^pU1!;AKg}5SKcc*JnU-cM?9pbA@DVL_Ezn zyZ9%NKGC(2SdgE2PvGL|fb^qnfOTP->gobK?Ch4$baaM_r2|RS`}+G;B^>AO%NrXT zxA>kSXl?wH&zi|Q^M*2j!}2LFe{>moKBfe*M}c#nAa7 zI@+Z?Ir+~IWxLX+c!~8N0}sm90YC0YCZX!SBBlXy-#+$J3d^aP}(JS z!wU?;{T?4rGd(?>`RS7sh!Qae|^7?)3w>jwZw&@U8x>~6SIZ|uDbp?5t*or#ODy~IDtdc+)kU3`v@UXu z7fH@|=vNPg&HpImPy~6iv9h{3OneJ!L*#xI57)Y)qT(Jv%s8S(^QDy)30)l>_g%F2 z(P9{=#e859bv)4e=?Ew;m%72Ai)Y{%w@07{iv50zHDef##@ z$k(rPo;F5C)u=9u?lgIk=?`j;)YU&{Ww9wIh^R-fsP?r+vUAPP&AryZ8}t)DFk-f! zzjEVxW#XK~fw6*}UNHT2k<|#yWD5f#JBr2#C&-cuL`Z|ksbn**#EtFul}YSk0Ii%H z?{#?VUnasgGchqy_g=O%I5?C%>XEFOcSV1kZ(Z!mjiWe&$C;IvC*jnQD9-1yJm_>A z_W02IQ?5d%R}!C)qnei17gn|OFF83%peeDuCc%J{(^68dLWYtJI6ul{zFl;+`uprg z%j!;C^8FhWB!LGn8w~R^GBV0)YHF%dQ&S<=F3EtrK(mD)IO-xn=$d|<)t5PExgbH^ zAQ$mb&$aVxpxYr|Dk`zzYWYybLB+d{7BU*)_UtF5W&vTg3^2f0FPTbsIyN+>TMpNV$V{{H^2 z{{9R@ZlUu^B>X-`Kls~xuT>5NS;Q7{SVnZro;&cU{RBTuo<7}-NvGUCIPwJlG6JfC zsLL|^I~RtuhV1kv4GI1H^0N5z@p|7V*ic$w*T0{{J(O>z6bz^8b=2WGgkd<$T`uvVRJ34H+ zI5}-%!K3_1NlDp<#2vqpn?yiBp!Wn4HL=ct0ZKg`p7;rqO0u9J&d6~u^g*{9351i2 z@%4x@_qhcY4RTXa?WaT=>~}}!xy>a99v9TqcqShCr(3L4tu;}vx`V$lD6CNt9JXt` z%T=@4t$wh*97jWhpCKRAe^o4xS}e$kDz*1*n8J@5uR6&`4<9~vUHFw*LPkdB^!@wy z1F*Nn*x1--^xlVSk3Z8fRa)myl|4CpVv$^;^-eUEGUCCRGcZh(H< zj)84*o^5(dO|B5jMyu!D$*=5rI7weO*>#Wp=rfZ;SIS-go($Ex&Ar(qgPwtb7;B2A zv`2z`l$vh^2v7PR4P9T4KMH@OrS)6Hda&3t;=1ynfxdn%`qL-FI*ZsULEBN?14t+G zl!FBAwMm2~j@w86=i z0oP&IJFm^Xkt!E1=j>p}31h|F3iGPh6^>R%sdD**Ci$E`U-RWz#0j>k2jL&H6})q)r6;>>nM0B-xb6Qf@vcF(D+;s zYv$&6jV)wtIsBC&q2BrHm*Y?e+)+}fPFZCTy7s^g4aeE5vILMFkFmoU!4A-pW=X_dnk3pr-7$Bn#tR5KZj;_i4A>vNHjtQZk(H7% zv0PYK*aYAE@I4qF9=?#Cp1$J^>anV6_4=b#J_Y(t7bT~HANv*Ntc##pEIXj{jb1i? zN^6rdf9wY!+!DDrkY--R+1ZxuK_Z_ig1}8?rln0SLpZ($|K5TOHJg)-ZGR|2RiYxZ zwzgLFp5HeaQ7IPD(~lME*uvUZ<+-w&vbv#qW8n_D(od9u}SAlc6y zlG(mRtU9%IHZ1<|&;xvEO+-@Cn1sv9@J$%qJtIARrfoLSeI*~CASmeTcNko8r6-a3 zCD?@B%Y-RpzEofFxl0dr$jQkCk))T}tRu0;2*QNz;euw}vtQ#V zp0oz_U>n9Kh13Odi1v&Ni4P#Kv9;orrrliCp8#qbFUYthuMOF>N`0lzyPW;-l9K0` zr04!-Wl_6`Wn-Frg#H+~Iuf0a6c1nY28HiDG7CBno|DFm1wrNWiT7Ob12qjixB8QS zy|SFTNFyRm*TYXn5@@0Q?MPVOjKC&3sbsM3psLRO;Defq8=t7Cd(W!ZP8ID?srBLq zmyhP?`%0#?&Bpf&&*-tKGP$>VzIDP-TdswGS=&u~=O)=qpwFnq|t zC2z2uM;-KHevOKp6;ne%AfP!l(k9;foNqk#6C#pSvsrzhuD-Po5nwEd$GE8p*y4>6 zggddOqPZGynVRW^`t^%fagp7bg56>0-5`(-rHy3X(?IKc_MaSfoa9_-U<|b{GHn~Y zudc2Ro4hpUy;C*jsa(+edK<+PGvD@b-SXPHFFfbu3U-W02ZBcD-OFc55s)H^Y-F~K z-Rns?V5}2^gM+0ZCJhdG)t$@(QJ${i2kNaBf74CXI_k^?7z8Yn=O^72*=(Yta9(B; zMZ)3dz)&h=g}KN*p$stk?jG-FEJo9wq;{|j9)=d!oZS&(!QsDd@*7NTQT&SCj`ES= z*X7wF9&u#KJ@iczldlMW&SY{|LXa}7<37K~JGuZ2b{Z6So^Ydd-G1XcD|ugAuZ_59 zGgH%**8v=_UeSn<;C{oCY(6+gNLY&2tJ)S-jN%Yh?bel74>$c%x9(h+_FSHMf%F24 zTx;~@``)iV=Ki_^<^x|o_AR*!+|oy6k1BwOn_Y_GGpoJACB&pO)oK4YhN7bOu_kXFQT6?;sc}*pP|L#5MZ$aUNPW&=I)nPQ|o%3OAFdlch2a+_(z5G*NZ2Nwq z(^Qw!RN+5Y+JZw7Q|WkylahMlo->hx~4ke_Q= zM>HQ3zpli)SFgeL9-Rk1#YZNdQMQ<>m8Hc6NUuDoYk2QcZmG6}oXFz45rn3a1{`!a zczJpr<-k;n;gB=zWozWNa@@FKK3wHuoAe}uiKVECkrDKp!r@gQC=FfNNThKViObUO zHA73wMUQ^+OIIfF`hNZ5x1MZzOTK$_yhm=}v4$sqKt)A$N93`uxrl_?8tyk4WQ_XP z3<&?S=BiE`8}5O{1D-&Pb3VAJ9#|ig1z>1>#AWR0zL(c=Cc8$?jnQhi4fkMTq8d)( zcOA*hS?3{YQ4m4Kg{p2R)6FK?*w`?zwX_ty`$SLLursiEJ0@RGQpmU|q!Qt#xg?>) zp{lSDdHKUH@(Q8>PU-NkrHUA5O7W*vADXRU|B4^D!C=GX>f+z=0u2r)$WJ=5(Y5|ICD9qHQZ zxvvX-n1TfSNC86b)JMT$-uy1?UNvj6kdR|J`_ThkTrP% zaG9+|FnUW3A!iz+@0O*7EUt9J z6BkVI{S`LAMMB%mMe{*AcawE|=7>l=MW_^j+vVWksCJ%hPdIm$(D%Wv9kr}J7|kYt z&OInoaEz8(4^@SB6l#*C{Fysfy$|@;=anDODRzg4bT0V#?OUD0>FiZ128qHBrZdyg z1`@37?Ac5-B&tgb3v7h`cWS{sn(D!jcVg)&{*byp)sq?8f4#La+ksBxHEAtF^1#RP zHKo^XWO6;H4Au>})}Q;siT(KxXV!N^pm&LWFs0qn(XsP6;?$i1>94mJsS`ccTVfCl zIa5|#9A6EItXfuf_AG=4vlrYHo74gJ54kBm7*POE0=X>YR@>*?tqy^Jl2{7uh&G~N zyK&=p#fzT<_(e}M*w|<{wR{H5QR=ZD6v^$D4o9~KEqWbx zrtZGEVq);Tg?ZL$pm2H>K+u+h<+Eq$T2TUe!NhE5PW$p4*OTUn6=xfiS`AntW!0kv zQeyY3(IDunvZU@l6vP+KA_EWlL4}w5*xSI}KcJmWS}ojuxupNf&jit(5Mt_Ex`#t! z$IjZewyW|sWXiAn>xFjf-94_1r1Nxu7v2HCLFWnuM;9ohJ#nNe5|;|me;nEv8Va)d zMjHa#vvyE5=rvUPCTsq=#VDYdjFr(E_cf?Pc<&t_9dtM!;fCo&Tsj^2>P@}S+UI6QYw9GsMAMW!)w?s}wcVuBax1gBnwlV-^T&0I)_^KwQ zX#Y8)3pKYMCi}s%v8H&cE@`BXm*z5+*Ch4Wm)~{XC-;yv%)@SDrVV2uJN`-yIGB?i zYOtG{6LdjAfu3LX`DIH+*;6tHDnD%&87?y5qUy)N*@X0rVb*R*QJx__*-JWcTZc5r z*6U!otm8Kj3Kjg<>~NjwN)^7TsR&PZ03g_Zhm)u>aez2|Q)T{0mVC3-!sgf`!BqTTt@K6f*ioO?0+KZ2m zk7ds*pKwcqz#f?N!gOtoZNcq|X{7QZirtyvDpCB>-B0B|hZ8bS-`kJP zkvo~xT)KF+3$Sv$AvQ**whN!x$>9y5IYyuaxz$kVa@g4Mb{Xbie_tj13417!ACI`* zS0^TWiJj78#(XGi34{3wl-Z2z*XDM}m!b=G_u0X0a2X*S8h&U#fT(&$dJGsJ9bo%b zcaL}KPAZ__?-qVN?Gh78s9hvnPUz#*tVF)kVjm@_Nekj}+=KP#7-J;fpa9Z>Mt4!H zvLEwLb8M-qk;t{49L`F5D}v9z!XqBz=Vwfj&ZC%B2bIzjweE-VvvQAbSmdR}gr~~^ zcw$QDhKHj@oF4_P4b(X;TGN@v>P`;oI)GcPJ($l(s;vaZMM31Tx||cE591jtApN?5 zMq*j{U0@vXF#CEM?!QG`n9#7FQ?vSRl$UlmWK*@=19^^Zg*wRt&mRp9JWBVDg2~tu zKRDK=-3?cX=2z|JkdI73ZvA~9B60nX^(>(fx>JkPdHOZ`D@b8q@2Rykih|XbR@C`} z9=Me~fXQ(^b3I?A@KED;*tqgzeNf%)6Yd&>$~b);or6ZCcY!{fUztiwe1MxMCSBeV zm*PdnudzBj@P911k4xBgfDIW7m#vfaL9hg>{(kdZaGkKiN?Gkz?i*m9N5h)&&j2l> zp|77986DmHNRfmj^g_&64=B)!r5&^WoMm$+bl{EqB6M;C7r1pkq)N}tJIwZcDK2#v zm_*y0qy7FmFK-u@9h%uUW^J*bfzDQr;k^s>bD*))?&1&`5I7ZA3L_h8L4P!NPqYji z=FoKJ_NND>n5X*^pdB;_STcOoG!weWL-sle*-=k~70mm=KmCQTT}T=fp$pQn-pj=3!oYZuIjUC6ga zeW08l@_0gEiU<%De#>0#^4&gRV9KcLQc@d?+6qlD#~Xb*wnviTDw)~YMR}DAY`a8cy<@TVL!b00f(ftNqSFC-+?Bqk>|pB6F72|-%v-l=b-aw6K@j#~t; zzlRe#@=F!le!BB-qoru_)B_Qy?i9Ywp3MyGNb%0TV=k`EdMDd;Oi}GfQBI?~%Tj-w0oE9njr)0`#KO&AFzW$CAkse9z zHjqj67gmCJSE2TUxF-2 z-Zm1-$P*(h_E9)zc(GP@O!$nVi2Os7G_E)JuMxRwpZx2SB2A?J{H2aJL+VKFhz&?h zF5QWZz3tx_XM<@Yg+YSl3!Qsf0J}$fvo*bMck{~Ae{c*)a|<_RbtW*K#b;A8z=s6< zPwH9lv9|&yD>Hwy$s{qINFGm0wtH-L-8)GImc#ky8{(!ulx${E;g(=~Ez64`ZlbNt zO{FvVekQ=y*wRaQlxwc}wcKFu-qGefLXzetBun8AnY52qbhn#37$=aV_?>Umc#W8t z*sN0*)l}T5uugiVt*wm=aPxO3CnxVncSG~KVsmXUJFe@>V7z}r8ue^MNqc5DJhOrB z(2YG;uT_*Y>Lqa0d zTx^0u1sGW^m(z)-_?eH@9Szkb6nP!(4@oC?62Wo`n)sFd8~;c_z7zuJRI~Aqoz{`s zhsLlB8Lr7(Z}35(w~_Epq&NKb;Y>$T($Vq3>X^EY4#v?0)tokWc{VgObOMl-GQ#!` zDO9@!M?S5Ct!Uy&he9p4$C9P{-Tp&fq(abPHC(>N8%t2n)0y%jWiWRQDrPC_$9TG_bs6z{ngV z_z{pgx7Mt8O1vFd29QH@4J$LM*`^!YB_v0bl#~WTVDHak&(`w{G*GU5_l2&E?@(#r zl&~AGr(Pf>yvdx{M1v*;xG;ISCko{KwR~`TyMCn=LHH@;`mRQ9j(?Y{@qE@eEBlO; z?vc+o-m)1Mnq9|m8RY%W#B|ML+DD@QEgYLAbRb2M7SxcPy*HE;o;{h#6lNO5Q| zDGf%B;xd>*g_FO5lDg^;YtX+-NS7Ds2iZxNtGaI)@+Ux^w^O{>)JJ(W`TP%-HP;R**m z1Z8*mmY8VsDtnFK3Q64cYy3g4r~n;{d1{AkYKK%}o-u7|;2D}fmJ*?)o>kvc;9=g2 zi;G(|x3r9#pwZ=XqO7;?Is0PMEWqeXK>?3LTdcqcBC*`x%4$p0&}`=h-Ix?CH~aw| z`|}iGmVM8!LDO1!IY8Lo%l=Aw_;%HQ7do!)kk@SwTC)E#a55hgV}9&5C*VJz)HwzK zk_W*8QVUCJOZZkvc=+|9v$v4qLbVpMyO{=@;jb8pDOx``pL#+*iM13AfE6`wylM0 z&=dKzC0RPKJ>uJb07}Jmd3iZ9T!ZzNkYl!lQt>H1S~e8QI0SlBeSQfMlmsvpRaWjV zwYRIt=h&V>g%{gTHmM;kq^GH9=qi;B()Zy)`(W(l!WjcGqAn{>TX|8x9bH^hpP8F$ zU0+>jbd3yrL=Ub~1#y_~D?g6^%{DlCW&(X%)1O!<@zp-!#OHHhx`7`ScInMhH)|D_ z`q|lq>eBWF_tJD0S^7mju@ychN~p!E0}SyNoI4u;H^jluw2L~#F?cDbB5~E3E3HDdFESD1YB2%|595k!A1=0wDRU)9j$a)eEvNkustDq z8RshEi9@boZ)>tUq!R${h;7NwB&j2VSuyHMMMaE)G>XN>%;I2D-pOfeN4eoPK?UPS~m!8b8%`!im+2uddNhjR&c@7{e}W|IJSOLCh~)(4Ha4em=dau>U- zjuv7#t2p$xk~MuO&|J`YaSgl4{7v7w*uf2+hI-m5zeye28g&3+4}@r4#p}ggh%Ns~fuMEW}R^H%|GP z&N8Fo?=uOU{ zW!C@{(HDL^RD~wJhNdREvunQ9MwH84oxX5`7xdEyQMgUYKXyVr+(degU3cr;A(9IM zd-r`76y$-$8Pu(9347rA)uOlzBve-#7_s}r_iuZzL_LLgOg1w!Q=1eMvzO0g2^Rh4 zEC~*S%B+PpGemH5w$gp>7b6+RsIKhw_X{9tSxUs_$sq(J}~i z_DAp>uWezro_2_1nP9A`k%1C!w()?lWqp&bgKMzvL&}(Z@&n<8w$Kvk;05)V*-j4B zcpEp76V$lcfBew8ZjqFxPfJX)+!HMw*7)dz-fju!s`qCc$g_|fLu!Gec`KhOySDat z6Ut06{G_OF^HMhzqf9Jd`Req$^#lrJI!@-K7naFhcXf6;ZGb6rKSu(doWo&uDr3s$ zV^)kh|2Y&>Msl)|sfo$78kB5?Z!o3lxJe z>>qSyXH3D@4{@o@L80%$0gQ5>0C-rm=bPQCS1aPGh zvKS&iXaQAtjDvb;tQ~dBc2vv~Xu6^gu05A$FPn;jf}&*4AX_8t2#u7=x!KuqcN9@l zPXaE)ZNMh0Lru|JYAu7aD%VqP3oc8=>JH=jo`(Yot%#$@O&nB^cHej}J(SYl7O}Ij zt@8^>g_N!QQy5%CLmwIo#%zEEY`VC+pJB~s@JGG z6O(?g4&ZM9R#7ZMT&(@njaNr&Snd+k+mH#MP%cp>#CWd(nam=9gpz^RniA#n0PC+j z0-8Mom?k5jJ@y-veN<8}z&Z6Ah3Tb1E(qZoozoc_}5U8fUswaa|sq8D0bA6%n4 z!zBdPj>5{428IR}fA6X(~ zXYHHH>{s}}w_FFOJZ=?x7WHsW%i8jrth9{#$KOLkI<@9qDGmbV>)g_i{>=U|xdYGe zZyV%8oTdx46`-c`%T8!X1ci?(FVduzMT@eZAM-vr;=8QlMnOz6lFrG8Y{mubW<0~k z&ji@ccZt{s2X$6Ny_sGQ!c&7E0ZXDoyKsbnYg^--5d3)5+?#_=Zud=uZk_|oj}Rve{`PrCzpGqUl^||+lcOKI zgC(%AIu921r?a-Bx&~|kLNOvqbbUfU!>{*g&;sKPVBqsNkw6spMSw5CT;=00wH0(d ze^ep?hyg2GV>w=OPF)J~XU{~`pCkbL(5G^1ogfAvcni4r4KixF>uPRt(Dzl(eHB?n zgHWF}gNW^DHQ@mRIf`3s$@~2P5OYKowjVDNUyO*UMAR8;FH^*LLD2lH-hP`CWw@&q z^%*ui>ZX3RYiwwq`&`L3qW*Z$2}p#4Y*;1_6D3G6f?(wn-~(A1Al*MeGZ)+U0Dl`% zPJmSZ5$jS<+MiuX1z*G0g0kQ4Vr@pNWCgf;KN#H5ZLe5_VenrQo(&xukXo z;PpS#8RxZ8{`(QUXo zy7p_2O7J4zAzp=PTOgbE@jtb1`mBi0+P>q;Ej?;d(q3y{jD-=IK zzc=U%aW^C=!KwwWSAe{gtkbZl@rUgFs`tMVXbHyfiWKa~oBtqR~ zDd@=2Ps6@kVs$-0I$Z*0g>bL#s{aA zyAJm@GA!*}=i*D3hPnM&fBtXLtj_>aOR>XZ><*+uj3|J*hn{j_r-7SNPm2Jh23$aN zC+Ito%+S!#hQ&n`MKVdhhMRfpQ6m|q@F_(A+@H}RD&{yO^nElC41^yM+&D$q^%vDe zMSRhwxISSQhzXflX-Jxf>wvW+bPEjFZtNVN$Fyv3K)Q{6*eN0<@5Q;fGo3UL8<*6g zmm^<88xbYaffO_>s0jPC9c&MA6Av2*o8}f4T9YYJhK@pgQXH}T7U4tbtTvH>?IomV zp?5;=6td(MF@iW~4$y{x9P$>jWQ7V%ntd8iL0|Y=<3IhS42L$WXn8eM{Tb)d0GF9E z?~~(};9TAi4SK3z1CSr8TM7ZL7bmq2-r5QMTvbI;+z!obG&RW~Cp8Sg^SgbrIp4E!S(3kawOHvsSU5I7yib9#q%NbH@<=&j z1S~cDs|6M9HRu#_09s28Vm%k}DVRzcj=uo7j+mmOs#MlaAHzo(p27Y#%tZU}7|*9* z(XV%4K$~rcHoAt5lo?b6)%|rGLNsLKpDQZdQt(&HGI^Cd)E?`m%#4Q1Ts5HO0xY3ay zS|$g2oj(cVHoihM^)e;?qzZ}xNHBDR(G8aoG^uVBap zkl9?w0Tj?wi|1hBkSuGC6NoJKpR>=QssRq_4nF}ZBo~~km>X2R!2?|Kh1~4yKWkPD zM>=p26V-u4XW%(0QOzCcmv{cR&%y9lt&Q`jCM_r-2H>Nu=ZJ`jo5WzD#C39@R}bJI z7d3Vfz(Z|s?~_r(#^}+x25sClzYD3HG@>lea-g-qITi=ilz*+11Gx;~%k!yPOd;Tl z(6^A*{wJ4K494;^JT_1&0;y?fO!rn6X`0SmhEf&bzXyt^s=0Uu56;#2@LVCn2OL*@ zOPP@yeA7ql(@?Rqumpl^n(jYslb)WMdU|iQdmIosa7a?Y*b#mf8bFh5z7ZiR#3aK? zn-Je`{oqJjX?Lr~#ZIBBq63rOecYiIn=j3qoAyb*2BmMjv>SHT7jAXAt{Jt;+ zprpy?<6cP}2sK4v40;)5vPb5f#cPSAkTO`K{J|Y!I^;x!hia07JjIA2hDz6ZuB4BV z7QC+ob{b)gsZLNnYJ!zwj>q_@rWCbO4s>hdBX;7mWH_kTe#k`4uKZn-T-ATS8p%67 zqQ$K*r|h6OOEQoMTHU>Cat;dh1O1Z4+pA+^X0fIpvmlyIwpY~At6ZmHg2aSD8M~C^ zFJI=)Ap6A5YpqR1L-NoX39eW@Z{f*W?q=iSl9Y*nhP5ItN+azkt`}v)f=W05RmXKMaZsBE;o=RuHi8`%i%TSSd#dLZ_Oa3H8+qG;HrzrUZJ z;Rb9O-A`@=D21JPDqcdN##`1a5qNr`hkr+2Ngdy(YiiCR6ZYBg74iVwA0aKUL=H1S zch}nhBW`s{lx&e-ddF$#@xVUelxwfEg?wSUa-2pZ!xdB$9JKNbKJt6_`M1C`BvNB9 zs{V-c%*}1D;m3Gn6pqh>8R#b0)}RO6UjGA&VubzJNEH5<{jlqhZ!CFuEA%N%l89^w+O&Ai6L2llt`1Br_1|NRxqs97iue}u zRnp$RK4+o*nf&4G?`q4d@K|_AV9G4eEPVCm&6~xg$N)S5j$ae((vsSK{5Vswi3#6X z!8msQXLX1Uwcoa2I4>k+cm|cqb?w@<8>_TYlgfJFl>b?-aQgz8Vz`NRpYP9~O7e&| zOl`PjEqr6=J-5CVf+_#=1j;|h9U;}^q`8!UBaoKgZ|{@{H->l?`V;y%SbYUHI;=Y_*qbpJBTR+ z5edEKQ8qC(EoJp8F%l8wI)y^buId4tL8-PpLcdZ}RJ6oi#&YeNbbygiqcpUZK)&-o z_hEY9Nzuk;#j9>i6@#k3v378ToQ{Ze*OG%$%YVq&t-ZlQD;#28g$uOEuXc-}xJ7t* zc}e&om_0bOz~ou>217rd&j+l3Oq?6%J46~nR3RuyZ{qYx;`xM4Z|r@Ayxj7^61_t_ zz&-E3_KS_MGq^tsa3Q0%@$2-(d1hfTpX|$0c+7|jxslW{d*yPab@-jnuyxWh)A0ZK z0?o=-;MBJt*n_-N^Yio7{7y;Fe&!Qmk^vt?jOh2b*^;^qU9r&jdF3Uzz&|)477FKTPdLE3uKh_`uFb!*Jd;IvvNyXvOZPC4&@VUq;M=>H}WTJt%hxk%dKwF>QZicn;9`Xz17fdj>)F zoolkU(qe_|pQKJ`(p9QcAUy}~uOq?;;Esw+-8X5E7uK=3f|5%b8Z78KAZomCYC6M6 zOma4Y_(FmwB)M`}xcBSZ2*1hx=KSiehs5KvsP5&bH!&L$n8L?E4wA6lVj^Na=ENW- z^dCx_a_yU}{g@REO*w*am{>pd97?tc4iEHv+U*T!Uqt;6jU_o4c`a7VCR(zwu{{G` z#&B0%L!;E(&Th_Ghcg^SVxViWLD=6ur`dDc_t_krE;r}o;n4vMXMHkQ@9^a=-AFqL=tqY*@+f zsDvBn(Gb0u{&uE_!h~3+$->r9TDR(NCYnFPQnkXSnF~{H1mCeEQqym}+mP{+W99ekavMv7>{8o;^6AQ`#cR?MVOO zP8G=H6816KwK^&j-)Yq7x{Ns4Qk@&^qKCG3pP$-MpMkn{DlRJ{ZFTREW|1rYE{{Q> zlx`r(kr&c`92iE%u2DVD;cD&9Va%U&3e_ENZmolfx@q~~iQ$}+7GWgVsuDI&Z>orj z^qx*Wnu)We%jZ~(7v*rYJtjrTsTvs>DO%+ToRY3X@nn6g#|8>}tStHT->B})9Cmpg zY;PfDvb2p;riKWgwYxF6&``*MoHoB<@7?$`nUM^c2g|kOg}8@_R0f?uwPO42@qG?% zCLy4eBC?t_NWnYLf-d)b-gP!{Ii0f1cQ|7Wc8`xd&2_`vMV_k=vfV5AQSXI!wn?B- z%<3&|)DsQb{blz#2@d~!2#V}T!cxD>JhGEtTDrXn)%EBEyucBvj%mjuppS>-%Tb~?`BhT z9gvr2sM`F_|HGsnhIVG(KW`nL^$zZzQ^p<<12VQ-|IM zGp1C^FA(Mv6x0lp`zFT0ys$Q$Z8Bh+2(gnAc>=?ieU(ic<)@cZ?!F-bx!U`q7)}9e z3ta?nSyU6n(BB##4l}0gO}`x*2F9g{@du~fz3KFs7>e>M2%|oOC&GbwF*c2fj()y}V))Kv zsSSkPGjZpo-_%r=inny46P%~*tUkKa56aJ2v48h~u0LJmAU{K+KyLHmBWYmI7qu&% zPg6dQ!eI~xyQ%s#6`1@{-QSn#nvKL297rK3vA9}%<%(e4r%snYmC)c(1FbH~x(X<{ zyicD#iC+x*#S-&wglzP-yl`GWPXC5a!7-y0%;Dz`A?3%?MzG?}ip$`<7jogPcr00n*8y`O>rE7jQ&<-lax;bA^rc5vLNzKp{+u(=(!YQIJ{h4u z8$JPn;?=O%BLJmOpt(d3Y0E?`NdCGWrW@YhQKgZmt4(_bPXnp}QO?fJSL&c4#S-pf zmPZcjadJvj!nu!gKx>GPc6F`&!f@4jG<=M{Ms@bW&9`^~?WrO60+(Yak+u%GLI_)^ zic z>LMs;u*}s3C<|H-!P(S}v=_5S$U96N;AJg!>mAT+|BmgLfh>ctD@S%yv|`{kf{nxP zLiOzLal9iz;B9DA_dXcXp@YLJby;Y%fhae3{-|ZxV)eET923@!Slyk$L@nl3tt=xg zA>PlJIQWM?CiJg3o8khm0F=URUtTbp+ws<9hq~3tSELEn;73b~Fmp>O2j&+2NJm6X zvmrMH(P~&rJmtZ_1{@(gA(bCuvJNj+=~j@(FsmzMy??}(dE-{}Y}1T$T|^8H>c@-Q zoH)>I+K_zUHar@TwErukb`xG3a`b?oN-yRwCTe<*2KBx{Myz(BF&u23+#B9Oa1*+J zL{p}2Gq{6)`R5zO`f~DSaVB|oD>^(^>cqEy8=t5@B>uyI`k8-rxG)%uxxx`hK7z%C z1^3g8 z-)>UfX)eU@#^wDaIJ+t65Q8#w{?XAvZ)IueE^SN}(DmC+Qnhpkyr%YBq!-{~jJtt$ zIBwGMW`?3lf=Ee};weYGC(R5R$P=ApxjdTYnPz0t8YerKTBCKauyLkWrmI=kWGk(L^U@)S?6EAC?7r zDmxSf??7pd?|cu7|0w?A#R~;OLc;Z_kt{3xz*hk%lq)tcKhW*{18)8K3wQT73IDET zj;JtUrFv_lS8VcV?=?iv`-YvQ3yli|R_XhXwo zv*k&f2it?IFf0Xf9mQmx2;}>4&Qz%MFCOaWX3%g+i43GAan+=a3fs_ognEJ<&p_;P zILA<_BK%_L%v~0BgN?2G?_awj9(2_#9PoROWwDPr_;=BaD z>kk$%PL`10Dm{&=#}b#-uUbFIF@=){$&~Y=xG@|$rAv3by-)ICtA>nWQhKa!y!dT7 zkSs`My_^Zf{pPwo<{{8r?kXznXWyQF^`JlhE^r@G3fO*^r$?Du(~Qx$C{xm*Qgv-@ zw>@~fia7I0XD2O#!ZFU(6-)HJ&!?+X#syWNY(Kv=LVm9)vhfEKiyWF_UOR8{IkxJu<`Nt(JzmFn9 zY#~i6^>I{hbW&1BB+7@fDM#x6A?m#YsqWwZ@q;9hk&I+VvQHvphKjh6lE^p|DP-^2 zu#&8B%1Rj-5wd3?dnH+!Sw&=zV|=gY>GS^m`t!bddA*+F8jtI7JuV1$G~z_7Dgm2j z;=934Q^C=WiIj&!rS=GYsj5S0qt0pPvUl%-vq>>$g9oza`#;``F6=YQap#Yu)BDPtC=GA-@!pVne41Bf2@&<}clPwR-zNcJr+ zgr>}b{0t@x?jUw%vOQi(3LK?dvtNx$jSsDi1==Cvv6U)+Ei5E77bPuBj`#jxeUwj_2cQ74Yv>K`M7iocqImqN<2z_yHR!W%AAuI` zr1%v@h)hxVGiDg=Sy{|1V)#w77wm#2wAQ)wbichUCZHhgEJ#!Lg#hNy*9m@{4((v` zv*H4fh#H#&9%m^J27B$4)laHKDAFcf^}gkJGej@A`H{*Xu*Lo&@95uI{*4Xz75vcS zO6zEV!CBf5#zN25U`2U(hrk)>)$^zMvNoe*-x{ey_<%Aq+_S}yc*U(|ZPC~lFIF7^ zKk|GTlm=xt;5eH8sPXd)t-KXmMRO(pV(b5sxmr`BH8VZEUJp@9jOI!`o3(ziutu?M zHUQ24Es>;nJ(*(XDn~baj0m$XUVQ6icHFqecXtSQXGS6zJS9Vktq=>fAU}V`#oK?H zeECJtE=^jzfi$wKr+;9crnpKY%Kd+3Z2^WjY~)^@Hlf&$BLId|WsKr`&iR&q>_guxJm6R3&+ z?GY<7VF8aaNmYmL!aX7$G_`_Ddfx6<8c%;XG_u!Be8tpCXmu372dn#OaL~|g$T2HR zg%nPiI6|lVI|skHvmK$~y9j>YH}mFPfzw0=K?Z|!gaCR&(8b>BU~sb_4J9D%!~>zk zljlQwSx6sI4i!{{M~^F0Rp65fl84y3mlE;a-`Y0jd#cMS?M2{*^V zEE|2U4vOM|C~)^>D$`x=x%p3wVUGU%7|q|QHbX6)o%#~2wvksEb2sU_bIz~I9t!0` zU?V!>xEytPlg7m2`DaLJ;@xny4}tqGx3$7o;8aEnL9(<46{HUJR!jTY2YTsXSNw&D z-=?^+chgc&!yN+jeV)t)cBb>(Jn&_cQ&SgNPn__YKvfym&zkiHA^4PfUB+khL%TxBz-BIQ{BJoPJmLFzL7Wv zZ-{Kr=|v`sn#a07R%HH%*X-?R8FL(=MSYsgfG-ka>rDVTVuYo-dbOL6Z{hM>Z-Gr< z@WK{5oLC|*zN#wElo<*2nG~RwZRsC{D6aN85PAWGxqr=Ei6zF(O05Adv;Oh`sv*@z zPkA~g$(|g0R(QAboTzd6st@#U=oy{E;3vs7vHfQxBvz9lUf7Ka=ZL7nHRS-3#{?niTlP}~ugd#R@#m0ecq_DN<)MO@rZ~zoR z{k`iU=c&heFeosv;}|1jRUY(w3B(=2oa$VjYP|&&S(^K)6ZX>U(y95s>&$NBKk>W= z$l)0oLK0G^N6xnT{9gVG$e1T2_^$pM;ipzs6T!ongzdw|JK^ax_)e6g$0&@VJ>Mm z_%Soq2Vw->BB3*r9JaJYMbYCGq%kGE(pHmD=5m3CI&9*sK4 zyQcm-<{N|8d{|Wekj4^9y(BE8k{h_z~xs z1l^J*qk`Xc?^2r}m#59|1o-#%BbWBL={Y#AH~2_&)ftFK;=X4C(E;pdEkbc4q;YHDh>2Xl>K_03`m0tk-V37kvr4Q4Kvw4F3Vuy=uV}9UHZ9uWF-G`|;yp z2Lde9q=+xxOnh<8EV!>ev_R+ft*yeO#~jD#I??=>FKq2h1Cn|&8Jkz?g6GO)`9J?!MPV7&&$J6#eb>X;H z)|WU@z!g|oSgiCWMgn1T9dAN8jDbj)q%>M&J;@_r2&oAkGJ~dsl?DwUc4mUQAk|Y^ z>bff<)tazDwFeMbG1lU)SN2R42ef%(h=%ToGGKrFoYsE#9x)QdsI~Xc{f)96y31J8mm*WSF6-+`5x4=Bu;(38M6*hh#0~vj$AQune8o}Z>s~$>;EndzE(2t z=!V=!zKhVVHe&SAs+%4l1GP?26B?ZI`CQOE3t}Q>%U$?%c<%&BTEe7!i@e5*>hF**s)IWc)S_6!874XJYn6c^H z7Euuqc5i6wwPrLmo~1d`gPM|R8FA#mhN*YdXMcV$IM!k}9V)rc;j*1f=;|_#F}`-K z=hUJ+P2Ck6<8@i#08}iZL_tn%{gPqj^BWC2AU&y20a~k}^InHA`Rd|Ktglwgx#(tJ zDf;c5Y2bVTXlwd?gY{SO@VBHd}n_lpt9Q+T+KLk~9rcir=sWKksS+8WO!>fL`WI z2Bno|(+sb`YW#yaH9rsD9Ek~vL zu7Ss)j6zd*XzhoO{guftC+v3I)J2R0F$OZ~tRgKP)|a7zTZ?d^OIszv{8V(qP9OTL z!8LFbcgjkBerb@JHj#b+3>+0~5%;>nfrui8^u`MUQL7^$%ZcAnQE}>m>sfmYT`XMG z_bq2Q1W z+KgRFfyo~0HtJJyZRyjvz0H%b4HPlr4ii_}LAOXWJgIyqe19|C1!M)z$KOA51mgs3 z7)mcr6dJsa(q~UUObjw{M~V2UM^uq$kX_C0`rX8f5|}%^lPuQyt{icq#)F`J8hNRK zBEYgYf;RjT9vwnhMr$@s?9i6F;0mDDu&hq#J6Vv z>$ID{)y>XQ4k+r@xBrW=|J}~|D>L7wf>WF3@@hsr8rj_{CqFKdDBaME%5|NBhSTRA z9UTnIG}P@~U2F@O$XMjHC{YFzU3%$;xrOGB%vRtR)HDOUCe&R`%@um8v7h-AVABxo ztpVk=l55|GW_MjyVgnW(6HTm;WYW8Y1*iVK9WH-k;~}m0*pa+CG7K))ak?D+L6BK8 zite{1O$cg#qg+pdaA|!9cT^6DPx#|^VI6?*?kX^R1$69#!a+^QsweQu_ zmqc9>84|9amp>f6727kg7}3n|KT9;ZJQ&?-W6XgpsCJXBRp=a7{H zk_tnJJAVCkP^x=C;DS8uut+N~J1|(HHfU3NkT zhl-1!xKNC)PctFnSca{PUWGW+05F8O<`_H=5gLuIdEhF4zB7^Do&#14GE-yo`;9dsrzcg3NF0d{vrv=yD&WWT+xl{r4mkpJ82e^*96hjv%S- z?J3Ya`Jg!5AdS&v-e9{^?YbXtJ2VVRAl_G(^a4%lpjwg$)EW#n1fi0+r{DWI!jpP~ zqDUcVvSVgqG8yXWDU=@I8TN@1FyrwTO3Lw`j=a7Y?-bRz^UVhoSJ-^vyGFjNDb41d zcNU~g+-Rfgrli}Y(LfqXxTD0|d~wbpxi7YFc7Fbu@8n5N8R$Cw0CHt<1d*}fPTQeV zN(ZnG4Ei<-0wN;tzakdnSTd)Rp#wh449X|_x%t19`Q;>rViL`Yg}f3!Lg186bYIs> zY20ANsxFN+MV?HgmHVqSmw&r4H{{Y=C}EY4Dvuh69m43}2Qj%t5LZrLYI}J$Eo!-H zn#*k}FS;ed$ zfYha(IQKUjJ0a4_Sy)^&jrjFOF>wRKH6RCSJw-JjTsPPDQa@7`;58x_;N{tw2Vel3 zK&D~l^4spucl_Q$5@QBAi8d!3yo;H)VutyJWlhtiN#3Xe%!275;z|N0(HTpNls{n= zne)n~U+scL(ij>XT(%t&UB5L6sLgZG)#wAMj+rOQDLkSw{zCMJ4qU%}ef3M4M%*@U zfOfLNursu(O5TdqJ0~s8@?4eY;>EBQI;RcFQ9ntFdw6uVCDWE!EIrvWD!84?*Y(S+uEjB z>Ao~^78+q>RzV%zav#F?MIX?oDYh>oxg)23oWKn2D#=i(^GPo(bW%F`X=K*q2~?8% z21iCl)*#`Tf?|#e5(_`U!Xo_3SY?s4;}LBK3C+ofkU7a<9( zm1rJ63YT%pCX76Iny3F>YbeX2!SV?IneDc-ojY1zeZvks^I)Z7bK)h3!>e`P3@iz7 zB-9!b_^({KGAO}x>^wJ4ZAmY90n^RS5g0almaLBZr=3TU&{+dG(CtL)cD#+eT24vt@>1BPER!?-ZN7=c+RoomHqRPiSg=^ed8DH|3{_GG)fMxnEzCw@P}>7U`Du zdTZ~)(#rH=&@-8~of8XY9WfC1|MS@YSvxi8a~kH8yD8lUdhXjTC(EYUCqY^l!x=cH zue98=v!jIugWN3;=`pUKI*8?fRHxkemu|(;Y#Iti(6c!=%?J0A5K41aZS_1?!!Rk- zQqzeEUQeTtk~34w)$n8&ilAu|bIaawtKd>_7z|Ln{c)v;+u!-6NGp^MU7*zRTgQB; zpzv-=A7%5joP}?eY<}bJIf{FzCXJN)euZi=eql!-ZwT2$EQsy<70x~_9}pgzDx}?B3FTBZ@O+~ zmI4Jyv2rkB{b2MM3qdqj+2L=1~K5=EOOIVp)frLnt z>@!*wraVmdQ<2)ljl>ztU>2Q;5wqe{e+z>(0Q^9;#v$V!sXz1Gq|-t|6{+>|huhaJ zobddDmtUOc)nsDbuA+d=C$R;zrA;n#T}k3{L4)_ruYaKj>pN(QVg5D>RTm3{MNTz zI{`e$(HwH5gmqvL2I|Da{uv*8@ZiIs$5omQx}D^4RQ_=&Mg@RxPw;36wK{98#N-*} ze6zr(3o5#>5^Th_X{YefPXduZ_mQ;sz_>%_1dt-wp?Q=o{%q7Ar&%`y2H=U!_xOsV zr1gNw*BIZt*%U((A#K)Qd;|0ybVo9TWiOat-y?7gayUVW20ukKeYfZQ`glfiFUUZ@6BRJF4qD?wKOE-E<}eA4tGbd0AD4gs9;yAI{lg|Mh$t>if8 zH^GVJMu2+mgTSoOi+zDsz#NMky~*ImiHYyD6$jU%5A9o+0x-OIowbaUt0W50#JhB@)Q7vAlWK}>36=G} zL6}q^#=FI^uzD7WRG>nMIycZIaWs^~!*NYGyszVs^ldmAf57N&1SqGFzf}YZ>zy=U zLVkw^j#225TeVJQ_fWh2r}=SypGv-;6GaFrj@PnXMwZga^m9mS6c18;y#+ihf_OwC zH2u35L*}YK!Y|C9eO_XQ`4sH?G?5=R&6KzP=?Oy|-$jI6a~9i{*f;yn1GZCn@b&}y z0ds&W6Id;H@8|4y%~eZg(Xiu-!%A`40+O0H1w88K)86CDs_ zS;Q>@Ht`NUDsAE8g~J<5;iJeAb2C@c`umST=n-k>BP(bH-_vPQ?Y*@IWmAQ%t58DTj?p*CjB)Et8NDFj3n3_KgiAow?%E%5SuQe0#>TtJ50%E);tn^_ zzyO>YmJrpp*Y-CvRaAkKglSSnsa>0~Nz8?X?Kdwr*6;$cJ@^Nndzu zJM@I`9!bQS7gdMoP$GvDlAJGaY_Yx+I0+`T>8T0l@tognFs< zyEIi${GWdEu73vJIHxfQ4%{8u)}Li*^;hcsQ1mA_Q8 zR=DbuZG|igHHYpF+@?P*zJwFHP-$0UB3{A@_W+D9fin=~9=mo^XnkfqL{DI;>|BSw z!|{K$K0nZtT0hn$q$=^a?p@;!@IwlSDfK`rHCd$~Z4loPH&@ra`ygZ8SY}i^&rsn8 zMJW&J<{I0VkCC4yTGfJZS!#Oo%?l27PKI3StO<>@! z13|51^G&%U#;={kPH4{T+x$!##FMS_>vPNHlYu7bF33gIP1%YP{wLgBH5F zZ1i1Agq?K2-$x*TSgH6*@rIeQKT$*e$8&aBljx&$%Am$<5Gx&X9RDUBM!z_X#)0in zbQ-IDShq|=lUQs4R8u0vLFE46xgS<6TRHF7fo8H_W=@XW^Z{l||M3ICnDk$#T!-U; zta)#Kc9tc=&mEBFldOd0+I9R13`QxI*z4kzmZczZE3OXumXzm^o^0SE=i6mEysqy< zAFv$(kZ()Hu&@Ol1DO$UDlVVJVQtX`jCf(WW(H&{1|YZj^(}_$_#Dz4|8K6Rx)PMI zzd$&mz?TQ*t%?oVRY0qOcH}QiY*R$Ag#>@b1d5v?@~`pX#cECtXEGHry3538~(%+j{U@e}dqTx-e% zeJTJQSZ`94mPqE$mRo%Vi2uLk!K7dB4Wd`Ci?xt{86EBPyp>#&)6HrhH0f&gzGYrB zerl(`KDYs#gY7>~L_Su;8hTOx7rtxbA5#CKVFxQn{rX`yXZmkg)P*rD-67{A)YO$} z`M54h${rcBf#bd_a4nk5ku#_jNvxu+1AN$1S2vpwCxa!&KZ^gx*l#ynzSb6b^0H`4 z#RPWJQ_)~5#Y+^H#o#%VVlmP?kH|8%v7$;lpib#+4u_nb((j3&D((u)1s^wHLz$G( zNPWH8YgRDUm2-X)RT?8ukq1Ii`M~2`0Ii|%dM0lM>F}}f5=`IhfU*1;Bp`1c;{@Ny zkv_Pn^X51F_$CU`F{f&(Xys|O1w=&`V&9!RkpAj;Kg{#!Z7q85FRsWNfKKKZY6HV; zE@dmQ9iasjl_HT7bY1YQ(`Ev&!AC=!1*!%6?c9rm7+8U4ol0D?c%VQN?tE7R%6i@l z_hlh}^~wujV_n6zYa1II_l03QrI0m*ZFZ!gvGif z2$~KR$0R4`0X*W%S1gio%b}?qLenU8+A+%b*|9AX4b-eFZ)DXEgt9qugJhnadtMn8bSrmM%EUf7v}!UK#PdaS=@*g;w-2tL-r zHKhpDeiITFo{w%cyL5$`COQC^fS78uBub|yNQ0HQ=p0B@9;x7d!vry3kL17w1-1t7 zh3YssGv3IL`#ZZAOAi*aM_TD0On>Fn31dZk`3^quP2qkBx3r0vL#s7NSsgk+`WQ(I zzZiXy?qh11f8}%*sn!BgsGs+ol#+6jJ{lrz+Y;q=6Hh~W5e{nTzc&S%v`xlD1RXuz5F&2+ z8I$W1CPB?vunCy2h%<-z(q54Bx&NeXYwa)!*nW^V7p_=@pNfjuDuW%_pB_633DVNjOK)WsHbCq#J*;B6Yuz0hkGP03 zK{z>Kbs$*3jS14SQX}WVrd1BcR1Km7@_&mn1T;+LA@P`?+Q~xUR-e4?EVMY}XfIi$ z0Et$i7NmXHe0QjrCsM6zF}Z;o2qm~j#PhG1dF9Z1V5##!+ai8BzNg?AO^}n^UPwH! zv;VcKU^!xWL4b$JQNTYF9`UBzs&;YYy7^|OFI*p-5%D0WUs4GM_w=>Y4_BjtYoVs4 zok=gh3@?~Vq%X3Rz-2XGk^IwI`|UUzTO0JJwREf9oYix%3IVne#)w$ci+E~x;totB z6o{k1pF05!OUuzevVGC9{MUG{bY8A52+{*b(IZ%SprseVbU|fk|0neYx&mcdopokc!d<5cbd0JQcQ2oDm(=Ij>{Ik7fuf8v4~EJV|su+lC6CEQ;L2= z3gPM%;}G~`|2V#>CsQ5)30QQz0Mj!EjN=$cPaE>R&c4eZ>tZtr%CHXxNB8f7s_7f` z>Oy*Rw3XGP)x%kl0$T=Tf-Ho(n8Kq?{bx%yxJBCGum7`JA>H;VFxT;Gv8{U4%B(L9 z&T*jlJ?QWYLXt)iHeR_t$Z#fNo`V98GjTh&Cj?)J8kK&~WX+aHouI>`qNL&5&Dt)z zQX%?8!Yd}r|J^M2eBGURaLFHySTnt00L&_V?YHyL!O;SZX-C5jJdxq3gOE*#zWsiE zDIp;n>@@KYh?@in-;8nw!8O5|pf(Kr5>O}JRsPP(E*4V2wyhmEi3zr(Dx5kCujDVx zI0^RyL%Z;yD@V7b3UO#gf3>R}>ez$+7iVTd&kKy#=Y3Zn_p$0CnO za@gL!DX`!bas%F%P6egmY7LINC68SIUVmR;>qTzcQY-NzBA?J9_u(`fjabh6_ru$E z`|EbJP%Dsg9EI_v0s`8))n}H?PwL<94Y9mc*ykJU>jVe8=_uOQ+V*7+J7?36YP`B4 z4h7;^_5?x)`$(e+sEE$qT!ZfnWQmK(+7zAND4 z@O3DE$DGO|V@Oc?l&RHX00he*8c@R#ilf4(pO9SRczY2@^`QW(I<`Y(^U)IIk5LoQ zd}QNZ+VV&`S7I5w&m9uvpSZS?KmL9aG|f#V1K|@bbVwQ1L4ezI#O6A8x!}=IXxyuS zc1{;Uqr!SVkoak(N*C;E=(=}ji}ZO&cp%;&azo~)^)5`(Ft+V>Ir2>z#;*MWi_0p? ziB++)(Z@@}Q}_Rl1N{yJP}e{#{=vJvyv4qo)&+#>jd&q(53pNp^k{YNryG)sStU520-&(krIIWUs*B8U`T>)$%!FjJ0XDzi z8RpJ1b13@mlvdJSR-cJDMx#rD;i5toFTyxV@5HN&oL4Dk9m#vYlJ`DA@rl!8cdgG2 zBFk72nz@cm_K`W=`)P=>UL3bVpeh_)Qh zuT$zne73RBV{=%&l~?4&b25a=DW8YmRv>CR!=k?|=!n~BZcgg`*DPcLHbh7;4>Uu5x71UC%%QjD*Y{DmaUb=r zbHOpxBSM(2PWtPcMIsb1E>%q5&Bf)9C+JizvT<;j)a?aey!m7MO@JxHSOt=$t!O?% zC8TTT!^;}A&M2anhJUrrIe(_ukDc!g^oTkvp-XvvS>JC>pSB}jDo-o_K2Kg%M}KR? zCmiofT2XK4P#Wg@b*@35EBA&cWAK6u*yFZ00bpeD!^xx5dx4e_ETw;nhYrtlChymv zQ4HZ%vZlY1G+7mdD28oKAgjTT#>bdmVU3VrIYDeE6c*rt^Kze>|B1wakr~uc6o^=# zg|$a`pB4^sN*0(yRO7J*e3QK@1k^@HwX|Wm40J#Sr;Ka8d=_9BYjj|Y&U>y}6*lG# zwe_^?h?3j$WBLwOfx#r|pD5svgpNXvZzO}o5DcJ z+eX8_y-NB=>1uhQL^M@bS0@DXWp=fogoT^8EmXex21GObWw?noyh|EF(%vvp@kRnn zb>aspuRSt)Hd6e2NOOTRP#UwH932&+p}wPia_E%wNz;@3)v_5P6fyk;^GTyb8<9dh zG6(ZIcTmoVM{qRV>HtsRnMyg2T282o~B{MQJ>>gZ&0zdLg7Uu zRPgLFgf_KpFK*ax!qn>(AJg66`fkwNZ~HTf1WyC4P*jTB-Qk9yfwu;1kz$`|8B)N7 zEv1oXu{^b5J}4wzxeWs!_Y@)07-8E8Ws4%k-ADJK_lMv9?r>u`r=9LHC{>nqXo>Gb zmV6I|*0pWX;?g_NHkLn*Tr20F-@nUGEij8ftAW#j^%w{6T0|}YIf>@*6UpU(i`^BA z)fJY|6PJN{;u`h@2F3-7I}_sOp`3s{g6t&TY|g%K<;mDzn8G*Mf@TzjLmgqs2be3@ z6x6q_O97W)0!Eo^{D2~VQHFMM_5Ai-Ei=H$iI@oP;QMCdPKHNyVm`k#G*H(e=Q&Bq zVf=k=T1>R~)vY!epflOqe~@FN$uq3-T$6E+&Ajurrm{s*b=0BFI0z~h9wxYRkIBU7XjK%^{33TWTMb-{oBQFP9>u+ z2`9dg2l?$Z`t9taPR3*yq2xc1ISbKhHb}bb!s&H;S5YCG@MQ--Uo)&wb0W@$fCwEX}RZnWT&NBTm+mU3ntD~d%=aMmYBY@ zBCjz*;40sT(z+&5CnepU{ZHYnOBT)L$8|NEp*5SEQZ-xcH6Mku2T?a1AIyb(gs~j0 zXQrY9$S0PTn%ZW&D@ftnbx@Q`x?|#8lRPiK;f-F5xRTdCxZYT1h#_O8qIIg?gaW|C ziP6n3`?@)L1C%t-WQQFBQ5O*_8=D{Au+TIJ(E8ZueL8vOM%0$XfH&CBf5g=2#wb6% zu;VP}!#^n!3Ka=;i!hYk&uqx`NE~gg5#&^@fc>o}p-DZ6s6rE@obparpE;edxUdz+ z1&d7VoT!~t>wS};%NMUU)^g!p=FTjdmU;F@*ORjt~$Ju}gzH{K9D!gaAQY=0Eewlm7X)~R&6u%5vK5r0d+{}RV9fm+P1~m5K zRuokJ!sug||I-hk`6v$yq&nf1#byHZd!egtgLy=F9+ zb2r7pp!YxkrrntK2Oms}HUutGAlBvt;EJ zmm$Lti-ftWPCa>&9fgB`g-o|v{!;{z<$VH>wH8(lrJ+BJEBlmQU&73qCv>dXp|*`U zh}8(mpr3yVyz49OH`jcuZk&7-e)Iwa)eHrPAXUy(6xu!aYvb?{GQSlmzd|#ZDp9jP zU)Z^qlzLxO$Ui69=;&Z+k}}3HJhNN=eIf$DmF6k{d;j(>tT%k0}!CL11igc zDwNFjpmlCL2F7i0r_s<46L>s}q&2Regm}c!4B)fmu)W1H?HT9f1FR9pcD}|{Uw7Ks zTnZZTUTHr89KVgdQpHQw;uxa=RrdJHE6d9;Yqba|FdspU5`QS*#0p*cL3`%6r!(gV zae9jP*i16mfQRVGK}(B-qEq}I`H?>NPytkofLAwUh|=+)q%$s z26U&{a-ejF=NOeOZy8s(vcGVPfc6_b9*XyH%0HP?V*G7(+1frz4t!Nh8#mULU;E}K z4>PM;0Lm!xr81(qNa9cVsy4bU4ean;z#GmZ(NcZep}i8+J81B$L{j_PUa;mWc;^Z$ zP}e!3NYG7uHUHp08ac=+EP!*Bya^Lu_XyC@1Ji%?WT^)3gc^b;#6N2GwaK+LsBpFY zXPy?MSaD}cEw zZo!4z9gWJ`{j3B6(YQcw(CwR`Q26^$KK`8Tc01}kS_j&QSZfVWca`EBS*6tX$1xGT zRDjt`FW)u(^1d0(N$>zm*$y#8HPQj?Q_t$S^XUrscn0bG@Y}Zzt&IL_R2xQbcm-s8 zNtqme#a0UCS*l{Y(LDkLliRisaE!7X4!9vpbwwE(D<07@ z+^#l-BJw9FEhh}Z%UVNS(^Gwg{h+@vK}lV_1}cmR1`ty;h}6BH5gnQrD!mU|z`)y;ltyGYV2AlT#r z4>z>6-4LmS@cF-FX;AES zhI?2Bu0(FX5cEFdpv#7vuUN7|+xncgZ8~f=mY)d+8xz}^tx2my3F0kh7QyEK^+6-m z9s>dJJydLayRDp^BWH8vmSnpbFxW;#ccYB66*GH4>hA#>XRBSDu)%WM&u~RwoiA{93=pYP9eO6>FSxu)Kto{(mK`p`KhJg#D1$|YJ#c%(^$`IN~a z%d_X2Sm;F{`$AX>Ls zFnRik4jMshy!4q$%xDj;y+}Wx07i9Gwf5P7V=4k!+5`_r6}o|3G$4o_l?+^_G02zV z!7v(sB&oG;gXz-ZZBOX3s+VK(G{JUk%s?&FbpeDt+EtLYZIGVu;}N*R>I8F=caIfnUS1WHn_pF z=Te3L`|ff`sX_aBNSWXs%7ypfW35YJUJ0l7e10Rx517nRt+SIl?e344%a`Hhn7ecU zr}I=6!}87}?OVa*8{I(FA%2Z38Yg7+d2)4 zyyFJ#5kFK=9AF;jQ%e2d~y)q=&J?{_cifI?S4{lV>z) zFd_4Q`byaA5Ot^(+T1TxqNQUg&^EGBhVrdjDoYRQjUwZ#=t)>!Jfg*HpATB~6suF* zeyhkT>$kVFf_C^90z&je<7BJjz@|P+p!`_>6$N>+v;c)D{~-E zm3hI*`x`#9mP!#b+g{n-6Vl9`>U64tfTQqK>l6WL5a6x9Ti4iv;OL_>5fFg zBm@ekIhmOrzZ+qORJ<8X=Van#np4$07{dLcFYL7H0i6S+N_o?v3W<1cIO9GOAj(zh zj#%ix3*P;vd5XcYEaB=we-kvM*g7M@vE>zv_T3slOo_-YA5+!gU<|e4nL`*dPxED< zmW%%cGR+nzDs@|f*>7x&I~DYyCYt97E<_x&YFRL%==b0yjB-tfJ5qh#vJGzvYg=`q zuFK_QtS7tw<5KK_+De`IH4~TPs0&(O5v*785X{e6h4!6}_L-GO%9z#cg|GN0rZUY(G;xxHqgWmKg6D5W_ac#_JBVLzD>hs zzq`(8$Q~Nat9%A5;^aR-cXtL19NrW8q~4BM%?~~>*0ou+`0H=3q|Dr;YTxKdDAUK+tk+fX_m;4Vc5IZ}EVb0*( z=dM5KB(Z!7;KNY#EqdC~DE=yl8}F-LFZu<@@!lqkhUzo~4X4bdI&F~ncF)(D{9B+N zeX7b~`&UMdI9%2Yw0nGJez=7Lw0^m%eOJoO`=>+=e2>RRdw%sj=>2OCQd;Lx48)8*5(ttoTSXh9rF3w zxxscz&mPvoT$;9XPmbgBgMow6_n^x#*$XDNGNGAw??9XV*VHjqkYg_QpEc92-+U+x zLw|g2;khmVyX-e4x{Yip&kly&EbSzRqaS26PFdTuJuE7Po&Rp({#Bj7SJ?35>@`}2 z&9S?`=jIek;TjDzJU#r08^d;onaQH#52XF^&*1>MiIva3X^aI2UI@{LM9{==Z~*2i zeE0>UC!{)CHN8~>jaUvgWOExrnLB-^U*6rDfsRJe2e!8I`8sl|*Hr;88sn<@J8PDk z2cD(|4rKC6$o0g~5Q!S5au64IP=CiCZ7r988p*r1(V5dz9{E~z4_c$_0M`En(i?G# ze{0G59sn@`PxtxT9upmWdR6~y1Dok&;Sn@8*}eNB^R6STl6KAJM5wIt2G^P6JYUG_ z9n0whf-#JS38Xp(HO)T5jaWb@b~<|ij_5Q120bGLNAoh=Pco;NsHR*Y_Xz4D*JyG1 zWER9;YzEwhaLokirAz>Uy!2+P#mtvqOy7xMS-kWy^*HXyu`CSr;-Y=>#u=2ING#!R zKPQfW;WU6-*#PlyQcurCMNBQm3hvt{4Jeg(IfAkAbDs#|A35^tf_Xk2TF66!h9oT8 zqa(h5Zf}O!dH>u&W5wRY2H)+mKMzcI}MBGO#cux&6XY6UgceP`)7vGI%>(kaJqjgXEO! z&Szuj^QCTW&~B7$dP2$YJ<0j2x*Z_3i)Cnf%SI5Lp~`mD^d0}3Stq(lgfNUcZKPQD zn*6IRKOJcmnh|`!ElpyttNo_9M6I@&uDWgCO&(qp}@is~G|1nw$l+Ifjd8NsX<4R~QNy?*bOK*wX|@#<)&ht$`kn?63R-3gOyeNNlrf@VX3Ue9_GIDpla=V4}{j8 z2+wC01#P2e5wq9>80i=6;7zR2D25Dl)iYpg7MN9*s_1{VzU)!1|NIjjDC(P6smI*% zR|(B&O-p@G7tGwDWi_b`I*gr6z5W#SAnhMo5OCDib|o?Nq{ehjy?QsE;MC!94-^{3 z9so_M88MUGlszNx)buX!K*vbxv62sdME`>3jyplI)NcSFdEeh4fTTT0j7w=SIuoQh zO`O*<{);d6hU1l|^-_GQ1Vy+SDLemXFmRZ|mi{Vh8+oJbfj~q2xe7_8wy0_K&OAU2 z96kO0?uLG1Kz^kG?s311PG;3B$kO(w5k#Hn^4&mt2L=s!b2%@B-h2=Y_6LV~de>iN zN=odCM-nyl+5DNzhvNW4Do*_=8Qq&i@cIQ6mAW%it}KVCNHE*3?*Veq^8*xpK@LLj z*f2=n_A8(QP!jm{g8AV447xKZ zGzHx)86S&^5>FpGfMIzQTB#am4zPDRjDWQm2E9~*sT+9LuXM`RYNlTkP+)}YkB$x* zn|Y008fo3>CByqTh(e^$_?pUz$jcmC{UFOCDOssa!yS{6+PR!`!JR zx6+I*OR{-PM#`eu@f0vGmeUgg))C4?Zgdby1anDfVn;tF#qu&&upgpE7P1=@h1Q=)+5mR-?@eG#1S|JVvO@7 zm}f@N>R{ptMovDm-7>iX#BQYm;TpPwtIQ|6fw?GEd*Sa;?_%v=)b$a0EnQQ_3-G$T z(Brb)hO~w}#}s1=*r4hWd3T~d#2Sq^ngO-$^&b#QFFDVaiUYw=YLN?A4OsnbW6zqs-##mxz7V>oPVUpg_oPR?GKr?-pfc_ z{{k{fv62!Jayu~fyps7mt${#sPFV)o4P`KGY&;FpN__iTYeLu^h@ce5!Aq3=hQzN1 zjSjvB>~wDv(YU!YEXP39fh{2&(j0Rjm|7z%@fR#wnXvPsswSLJVbyQ&w4)bgl=$|} z#T=$qnKkk?`dDWVm{{NcZ17eo8sXgye8^qDV1GKySXbF!O(Q}WyKM2oW6qtiA%Nq( zSD~@BTTG?ThyLdUb8+HRb;-{w@$H`Ct2q2KFNT^L`Y#9T;NH2m!||-Pf+am$Ffc!F zB-;1$;%$IYKp@@Necj&E>Da|v$OfmQv|SIh0zqpIKI148z}hS1np##WoSX1z6RYb(vr!YoTH2W!su?V@b0{qEkeIdS3REPHFF`)d?m!a4mqkVOqwSZO;u z6F`^FYL3BNF^OQJr0p?TbboIKyDysu3~!3Ufq6T>rBG8FnnZ5%!I9vp1OLk~1hd>J zSbmQtI;zwu<8H3G`E9sGqmMfa^(Eh5=$@*di%-1g4k{fyN0?!1*H@HbETIUt0RUnj z?b%yWZ^&-=(;L*2Q8z69H~4i@*EsN{jMMZt@AblDc|SltIZYtsAP2>y?;f!X&@Y|J zTpZ$aRQvp|)U>-`kj_H!N@EdjybE^~1H>=I6dJD{(i^ssQLis*+LcqE<$lrzAe!qo zs?ZD|2~Ct4dh2C|LLrFdW9SeMMa~nt&-Eb;eF+XGC7vbYy>a)JAz{CdjDe!>LfMQc^mwhQLs>B){>P85LKwH zHJx1lplyEaBKOlb?@@_99wJTCk#FBtyAWRXK4sW5N($KHDI5`hoiOAy+RI@`I<8|q7flK+jpzgbnhN?c2j~(MxmF} zCuoVr;BJZqd)+(^bDvhepJ`-U>1bd!-I;w15pO6oYQ+AZG5x^_5txnxxuTMv@8v52 zfyOjS1`w;60p`2;6KOq~_JcanRfrD*k74{D8`#?Z`4TExKKrscnt#7f+jluyN@YW< zezH0k&iPZpZIbP6LiaT*_c6pV(FS%Ep$+B+6Uw0ecbsgwCSn1qmw^+?(%lv;H8di< z5Akozpk!wag!XW`zfd?6^8uX>w}4fyGaNjSM+a7IQq2BYyp+ihU2z}?c7A98{`_ zEs12G8IBSDhQ>iy}^ei8A zFt7U;Rm!|rZ|wdV-TRX)YEUrQ0!Fp{2%w~xem`zwUI}3)E^t!V2R};JAePUPUHf*; z)EyEDUju#p5~)s7bCAll3=7$#mWip@@)HVx4%3az*@At2!y$L`=7v;QqTtO zj+<#;F!xq>%I7-sWZ_{-s2klej4#zwh0c1D(8YT<8I_w*e8apmRm#h3d~A ziT2|L4$D#IVGARnxNXfwuOm<^vMryC(nw;HdhnwYYI`Fk%6Cz;)N>xP7o|K>vRXSO z!xFP)t`}{n!dM8JvdKOc#3uauXdu+p#@$I~jZU zs7m8cI$?{0pB{2!M)&JibiG(Dk-F+uxZ>XC{tCD)Ber>%I*4KXHTyQ)ckOp}-rg_p z$+2>T`NAO;AZ5B`NX{uA(5)jIgWQ6DLtOJfjm)Pag^sA&D;0(ex`VSPOv^$w+Y0(Cpi4ifO8 zn!UBWJeZI{$P9!pzJ%kZbD862(Kj_!BtWc~w4WdDV>6$fS6zPIS)T1onYh5Qvd*B1 z?Elx^mp?+?Mtu(|W64ZNT1-u4t(de~C(Teo_DUp1skBNdYf)oKN{f)Pj8a+@B}J4B z36)7g3N0pDC9)*EpUeF`?~m_a@b=SvkMGR)y3Tdh&pFpQ$zexKoGA)FI}f;C`4IkV zG@VCgYKDd))84*#$&wnY2mK;4Pia<`mgRb3Me1(jA9=6V#35gszXch+6gtPcu{9JC z!q1~g_2H_W=5y^?B}}?5MY{FCgT|If=>)Bj=dJ&qr@>ElKXmr_<}I0H`A`ECRf!0* zQ{Q^MZpBSs`tTP$no$NSG&F7EpZhcTsg^W$xft zGEBaW1ezlz{qK)V-@Sdi@#9|-h`;BJF_J9p_Y=owR=elr<=LzUbOK;L_(vAWmmU}S z2VcxGKmPK3@)*_1NAn^n3Qe7RD|ZQB*)1*ZC26NxlDiKv`5f{=nOOJ#n zy*m~^(Bq#R?`}LnF6!EBWBM!Gjk=<6sY!E=NLss>C?431~$5D zZw_}a9qE2F{PV!0;Jxtf-!L1iaikqRxSeWqJv-af8&1ge2Jr9|%jTEoy`M>%tA5}| zv2Lqt{;G3|=TcYSYdttv32XVUg;@0B z)dbu7c*!C38Kujt1E{n1+0woo8nE6!WlaR2^}k5W^|p=-wLZ!P)^UtWRSGI}L0y%? z2d`N%rI3oaAk>@LZ9wa&CxpmDGw-elk#S;+@1FeK88!hG6|;PG^?sWu6?X1%8N&UH zf9FZceWVF~w6^KkBMB@i3S}&;`Xg$`mzm4R4~ZtsV03}p&(@)1 z+cf~`&ACmp3w9Lf`H#0Jq~?779^YOm%F*H5|H(v|%$(Mtp9gMLZ@#l&Jgye8=)iK~ zcI?{4_JQAAjbfwQx%W;>c)23!V2@IxS?Hms&~5_moqcf8@l}1d(wIhWO35D{3!c3wHgfpo=Gx;c2249&~~>1eqSJu z%DXlsi?X7}Indr4G01pHCR`fY(R;rI!Rvk(}Qt?=CEL44&~hI4d+v7ROiRw8l38-iO{_DBsX(0?IUn)7;3V zZ29B4Qy1?@+7FSrg{}t8Cq$-ZHo*+9nt2|P6Hsb?HuJ{&Uy)OH_17JE*qw5yg>Vg% z%vB8&G&D6C9YnS00!^EwU(ZragPV*t*viq5siiyKP?6LjG+q(^yU5Kq0ClQ|{=RJ; z?mhly$Nn{mG&xk1hA}Z*a^V*MF6*>FqUEteXo&oj##VaDRFc$R#nxT^WE@K)%L z?G41zwmuX4y&8`TRrbrC6ahV}a0fIDJlg$%X}kjc#~~`28XW22>Z?klT_^ToLmmL+ z-9RtWJnvI5DQV7zm>r4n)#4}Kciw12PVCzK3f5~qlA6DBgqwbtO5-K9x0HR_VW zwyQnwEjngBK=nY%t5uVjoXnIbqm@~H-VK`3yR*e1`M)oB_q$_)q75? z3S4h6BNMDlplj`szqdw$`Un!RJj4;(RsAh_`66RwO9cVcMoSzc5y$R^l777YW6N)E zGEO16Fx6#yHeZ(ek7BBjl_K(htS#T7IF>I zc2xsKOUs}H&*Cnbf5oVx0rEISIQ&9bntjFA+=Aw;ni9pzi%~o2H^c1T*R_xS79fP5 zl!k8~AK^Y`f|xU7QiqzO&mcnnmn(V$n|E9`r8(*~(Vs`^I4n2P1611fl4i(a?krNeQe zMFuwGq804IWxZ>-s%--cx(s4gMMC?0b-1`ZbT>lM#Is3TaZt-`Cn#GqC$Js?V_Ag8 zq^pDkl2J$rp8lCb{UP|qh-O~u45!K~;nd8#w|pNqiw+QuUlQ^Wi56E?PQ%bL(g%)hpC9Z`V}LMw_f0OA;@?}ac~oIdF=FkpBN_LbvwJSK-dZ1KLk9w zbtSW7F}AI+3t84{g`bnRCzCd7YdXFg#U5aO=6+F13g@MF)5oo7@)MUH&{g6*{f&*J52b?l4%?Jq$SHH zFW4M{=^}R%mCu{uY!#YjYUkHp1DJe?T>PP?xJN}Olm)c&a*sGE+l{`J@*?7Bg>-^W z6#d%p*IUEeYDv;pNRoZ`8X$}0?YYTUii?Z)qf$&S|L)z1z3d&dW@EZsa}=|K&F2Yl zG(2`}^K|Nj6>*HAmr#A(7qyIX8hDPmn6pSx7}tf75h&eq(tGgz_A5?9DD9a4bfdt@ zkw;%}RaMm=DBxy+-SlC!+di}rBKArl#4?e*S!vlz1W>y@z9*21oCqWiQ&0ap`2rsHd;7$ib|Khr^K!q8e$qV;ZkSn~Y81L*^7C~;i?GyUIOj^Y9{&AWz$_ljd6!vTLQ!IHM$|R4wfH9o zP;2=htGKwi14M`y-~x6M0WOsa$3=Fz`F-(I=0~&8{z|l@T*0amL<3N`q-Cc zXX?KUlS*GHtXjE$#^u*rN+=V^lHA1CiJ4eX+iL{J0`_wc^cN3K?Hz8zE+Hq{<#iWo zIaE1#L$Q~2!bLh~GXg<5Qq*vj*#RJh(vgi)Pum9w;WzVtS|`X!co|Ax(R$(;Mx_Zl zPY3TXM~F-E8!r(tZeHPV=@lZ3^d%N+lp|1>vJpdaZDriK!qQrC+hc7X|H-T{m zTeMmN85`yFd!Xn&`9>VNv>0qh2~bqTi}$RZox(hEb*1b04qP$pvAwEZ{gO1z`;13^u5^sa?N^EZp_Aw^9$mjHoB-_9@ShJc359p%)qy&i<*!#urgNjGth5SxIW;Lq>hjE~?Qu_|Vk~4CLw82N|i56>aBm}|TYd^s- zE!5c8$)4-)8S_66W0=X1LzJ%--TxmD!nN_~vJHWW(nyd1?S+g)&OdJm74I{^(s zdxavCLir81LvMPEklb}_ zMQnP5t6Rusq*S+DS`|V#NpFL7Hro95hziylJ`8zN>rIA9cbwOK{J~*`+UlWNYrW9M z96Tt00YO`EDNy*O^OQT;&v0l-;Sz1uW5wF}n+%mW(ZkXez@>gQTniUP&2RnQ;j$emr!Z*?_b(j%BVVyj2fyLu|cprZ)KKP2Ra0Wt+8mpfN`Xls0 zK13tyl1ATsebNl9z2^@7*z46`0k(S2!7utO3V!Zb!JIAwWCkyz_n;o1rOtl54((jfk?RCA_x}HK~EN?6l5a z)7n5nh#W+H3;zDD)=N{1=?r$6cp*<-$~J&1K3cmAMq>-Vk*7V@h}^9nw+53E917Oo zY^)XAbhHE}-4KVd=?qTmBja;$vePCUnjbUMop9-qO%Q%3Zl9FXM+q;%AZzjr$xygl zzx|ExenZ{RxiopX?tvm)N1%Ap;e~8`hjpKzmeXvHN=KC&DNNiUk}KtE-E5q#1ZD;< zdM7_Ow=23|QCzyQI)SX@zS*&i91O!#f^_?PX)zcoM)X&uTmiEOZS1R(E;?#lcm^-5 zTR<;88i}%U&tDkFdGh&YcpIi{Wsm#|`nTexV^ z@kKUxn(LBf;jnf4cDaF&gH|2#a**#UDTQ+l_=MK9y-t>fbH#SUZFZkG-FY)Gy1D3> zJ6f^>A#8I8W!%)d^1c>#1223Ogz6egVa^Mxwkvb0rXRy8DZMqJrL4} zpm;tI<79em;=IHModVW`Dlk*ap)DDti^@mRdm`*Z7byom#hWcFata^W0*7b{3M7Pf~^RDmi3 zE5&BjrW#2J`$Ju$U9PoASz&7f8K~Ao=jaPC1viC&sxfQcXX{pj%{?72eKhB3K2YFl zwfJk<@G&1saKEm;7eLWQv?{tXl|fc{2d$C=5pc8h<9{0EZJPXdC3yH4hx!ugar2TT zOQs^e4CBQ=-z;oH?jhpmAVWoN0 zXNLD}ao0ExSGh70LhISTNq_KDEC|Y&T1&H8Iqs-K#uKTkp@)+tJ2L9V6}}{;3^Y_+ z+3J(?6lx|`N6)dcUUfNU zgytM>_iRbMY6br{2Rc#!ma_M0nIh zt@GEi{CpT?`ud0&4%v_#a?G5YN%tYW*Y}}2 zGsW3VP@kJp2Vc1&qGfu5WIPT|d#`;}o1(_5SV5lQ-*k`iu}=6A zNDXZ}CY4#PnLG>Sq#MogjE-e+aPVc+b{WMt1a9r*cBn(;&6Q=E6lmFOMo{T9i%@=mVDRe6JWb?;@ft!##F}(>~K&`;YYoJ^qi4Ba|UhSDd~P_ zauchNNwZ!lr!aB9{kRWI zp;etL%z73?M<+q8#k_szT#y?NYQ)v0Si(HMD%&l3(`v+i7ZvZWZwlA0ETm~cUag*4 zN62_TOPYW2z|VJYKJg*kCGWNKk2{03yc!|B)>vVlTO^xU%M3(QFX8E#+7gIW-a%0R zvhL-cEbrAH#AT);J8Xf+mSyr0C?8*hslt@A7e#faYTCJoGFRP*oKwG3%YJkR2bx`* zF+x^TRfR~XZoQYAW#$ge)nCKny*edK!8E4~wTEY)e{QQk8^0o)^Dx5s-yYoP_P3TyAqx({4RyjGHAyGt_EQVn()^Vs%pWf#@C}DxqmB&oSjSA0nY+VULq*DGhH-znR z&3;aO=vJ8p@sPtz%|tpUn%KdItd(T;0Ti0@^$J&4*X0pEs}sT{h=n~v)L8G&=HP&k z)3qeUV4wR34O>V3m-eHY-ng5U1z*@j z)>j31Oh4C1w;++{OjI9~PebLbox*opfwO&*YmVfAm&15EO)+&FL0xg%)EL7KDR45Y zz$T4d90W9cqnw@#@i$l|F+VKQo^QU+?c2q*KFdQt24-`3r5HJ=n=Rn4$ws7ym(Ge1 ze;3`vV?oz`1O<%O0!~?O!Q>+{<2%4{eNDU}`|#fbAzOWXjviphN{{X+4>gjDoo*}| zRSk&-EOq6;8%alq3EG1QVOsF_+^BL7ghY~Lv9gX{nMohV6P25(nXnZV6&r)+&&R7- zE@RzJEk6<85E02TgJ^8B)iF_Bw5`qsldvhQe+&IeMA-fT_hHUNi&LaCi|3|>00pOT z!e2Bc8#FvilG1DZM*a{WcQ?LpZd%QpdQbGV5IhZW4iEuT&1V_$R6Nnt#>fb8cHB(s1ayI#i8GjS{&_t(Y6AkuUmlYu)f|hz@0%EOxzKnL|YzcjLMIn`%-2G03A-slRpsR8yOUi1_ks z316IATO%=~X~JhLT(V?(!uN&B8hAG)3Zd`!N$NjoMJ?~%tv~-S$GaMzmIx4nq&U8n z>HG0^%NuwjKo~%>JrPn)PeubOih>2?@22&69&na>FSseuct&V{<7&C*Q&@+n0?f}X zSo|h1ONlsP9fr>oBU69TYt@p%i4@fP;%VnpPLh^6|M4vFeg-OHbjYJ-;wh#<`>s2L zJ$BehP0tHq>umz@vpmn)JZW)cxwPtWDHc8ie#6~fUs1XbmW+g4V0HDqX2skiETu~>wD`|2E49JjS~ z^ihI}pp-mDXrEGeSDCbFxG0Znn`RLccRR$df3kjVt7u4Ks4dUi@?4ZGZ8`1(HH~gX zWlpJQ2LK+$<;SOFqN_S3y|nQdBNF|%6!+SDajwmujo&>x?@VReVf8AO0ole?SgQES zP0I(4;hkpQNA=uJWN6jl8lFmvz7Z=2%L?L5*{MgoikO>kIT~kH&UvT2cQiV1S3b|5 zOB#WwuKPU{GCdl*ygFGu?bf{BRPi&sh7<6?eBE5z+tJ5GjUwxEl$YrQiO8ZWu$*Qu`pB zsDxWR6?h&16?JSGl704w1O9jU2E#2>Pftj|{VaI9^^dj4rn7-GJ=ZFEPiOdPeap>c zB~}ldz~|2Q6@wX{kC}|Cnynf>`m6Wuz5S1J;_kq~RgY(2R|ljuQotF>y1N@k2vHTq zO84md)JnskCS(vOT&v!(X@u~0j1z9`Zd3O3#mo6H&<@K-S8eW4=k4vHaYlPf4L60|gTPr@F`hq(l~ z)p1ARyJ0XxR+;!qqQFAGd+|BA2$r*{4~{L^TvhIVeDd5gmDF)OqCz$fuMExwB&Rcw zoJo#Fc!jp;+)RbPFbL}8F66gS-C+|El)Vv#uU9&va)|bds!WWtmH_?3*IZ!|&d}yZ zszqhSKWOJsIoys#@DdPcY>$QTf@_up?laL^&nL8Etme7t`X|MloDc*i`Ir&H z?ldOqPus%v)Q?NQzgU-_{*azd|7*q~IwzJ|o__`|U*@knhZI4K^zf>72k}>j@<4=pu8#HzQxg- zIkV?9JQKu3Q^h&(B*LncEgYVL^&u&fCWp#wIw;Ko*<;Im8})t=xqN|)M+8c^luUwM|TjV{B>h|ug7ec7G;=guN-8&FytD8H_o=r4n`jqS$ z4`<$ZdRt5%b6B+df4J;&qoTJpX%7~;##IfB-*|WX+QmJ$zR`&xC-M@jW3!wrKQ)B? z=puOHN)F4TZN3N|LwqVJN&5Ft51Zhvj5vN4{`5nQqXh-StWtZ>qvZ&qAC;A829j9vBKCW zk=!Ug5H{h8DBJ~tt`Z|1b(lj?wM);I`6wykX`xG&`nEeLDo0K*_|&1927KaEDH0Fs zk4S|EQq#5SHh0l;H`2=1|Gp^L-YyVVi)jDRz&oCBvg*MI5i70c0EXuEbmZ#~7bqqE z$1u8y-D-q2DP601U6lF5r)JFHOK7TwD>^o{;&atu*B@p{2KZ7!*;@tca0$XPKmlVzUK4w-*`3n!qm8}+pM;&#iOHxx5!H2*yNpS&qtU0l_0RRr zn9F}Wcw#b96rN+Y+nIC%l=EwmGWw5wCd^5zxmTX{F}It<;P&g<(HvU zm?$iZvOHcZM!^J^m?}+a-AMN{MJqrFX4;Yl{L$Gj8^-Z%aJSuYM$c%+Op0o569NRz zmtuuaof5_5{A9C#btQ~OPPf?B>`s^bX@`C3<1p&5(3tgMlm5B&)C7zTqsRvf60)jg zYuvzSj$aD^Z^syQ%z|V9t2qA}{c7Qa#`rUyP^mmRF-^c9_m3i2LcI;=Egbf}A3(5o zyE0B?&`#B&!}LEYWRx8#g97QbH&T5j{g^rG;oG6H9c(6o+Pj%`ewmWoKa8Hk|LvWu z-C8YXrq(Fe_-LR}f0*n!(N!~%=n7}eR+x||XK&lGjDR?eVG z!PQfb1#7(4BJViS&(l65A-3nb0t&D1M5>H7=5offA` znn$Sh^ag`bvqJEL`M+kj1Zr7g6{-i3YS{^=Wv5_)*<6*G8pjMQ)JVAhMah!Qe{qr( zlc5_fP#?9$x8RnT^6l@774NkjjFRM%2uL~263z_UN>1T_(ZeL=uepF~5=4_3Pq<|v zAK7EzqobW^?c0WKCO-tq#@g?wAJZUjA7TBZnENM!Zs3R2`J$#h+vNs@!qU)O1@rVt z*Tzd+sHqDz&}{x4jjEK99ZF;zQ$3rgxv@hDydbZaCH| zt7Pp~k*&UO96zG+I?HFZ|2uub_9YnvJx%ZQcPm;`t^PF!S7eVD*H5YKGkZ4Ku|w?x zb6-k)M&qSgMsHMj#IGFnefu?4*d9qa%Oe1vlqW2JY=|BhW&5Vt)osUVXgzzfYo6ea z#O#M5h6Am0d#4efwL#{hb32b|5Gx(KQ?R$dujrdS>88kH`MdsOPb%C$U1f@?tc2HZ z%2@!QlEloln=6+yG)Iyrse}$93fRlBUQdo|eXeh6Q}XAAA1oC9atuxR0k?IBYS8ZRpL}(#P|=>ZiXDxgO|{ z_01}Zf7bcQ>=l62ySjF9-J?X-M00CLp#Ep>K8?!}k)_b^VUM$4pH_?Z!DWr$LEsY9 zCF%Wrf?p;N<9L^!eycpx`2dbBE2p+_+^aRm#hjoLm+z0p#?N>kR@-_sFI3*T80=9Z zvK+@ZEmny!k+z}-m<6^T%X(VeiK8z0rTzMNQs;+mv&nph=t1Z5wg+HJ`w73LdW`H3 zmq`5`QR_0rY5}iG{FS%oi=;yAAhil>4s$y+1=Ri*_@8MuV@6Fk{`3F;`+riPWTcfb pjrc7T>fe#3sW_Pb_rLerSwFt%l=R<3yF`ovwy-t7$?-h=e*k!gu*3iW literal 0 HcmV?d00001 diff --git a/utils/__init__.py b/utils/__init__.py index 90a4922..d1a5ce9 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,3 +1,5 @@ -from my_loggers import * -from for_DB import * -from password import * +from .loggers import * +from .for_DB import * +from .password import * +from .image import * +from .safe_filemane import * \ No newline at end of file diff --git a/utils/__pycache__/__init__.cpython-313.pyc b/utils/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d862dbe0b9e8d99444dbfa2814f1bdce2484218 GIT binary patch literal 261 zcmey&%ge<81ftqHnPouwF^B^LOi;#WGazFsLoh=yqc?*&lLCar2xl?DSSzCi;K(ii&Ac}X67cQr{3Z% zPE1RUPs_|n%}vZpUCHnn2g+TQH literal 0 HcmV?d00001 diff --git a/utils/__pycache__/for_DB.cpython-313.pyc b/utils/__pycache__/for_DB.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df9ad023e0845d17c94cf5f430ef76543d67e28a GIT binary patch literal 946 zcmb7D&1(}u6o0eXZjvUUZj$YXND1iLZlP|2CWj&x+bF%Xg~XJK!m>>^HInSg?j*Hd z>PhsZIg}))RuKF^@Lw^e5SW7({R6g1J$Q3ACXLV@>VtXnoA)*E{f6lT0>eP%%lDc5 z3k2}PIrN3;fliQuJxIYrxWS~*MR?4bXp50tgflf zK(b$ZV1f{=Ba4{>iwVK9pa;*kCf%naQz+K!Y`W*nZUFp8K65Hx0C?c- z-?VXNf3eKw?*k zOHnZyQnD&mpYh`TS+O`PW+USAytpzirX)AKuT`g+Fqv=^| zJC7d`_m+lLj7`E}BazQw;<2!q!}+2nF@$w$6O^$Wb|MiX$!SY7Em_sGrnVy|w6cs1 zqp)c_l}k8Zu;iR!Dv8BwPj*a)ovv~o)A~TwbFfDtI9i>oOt$!`Z~Rn8@cFrR7=qI; zZ`&d84!`DJakd}ALN#8AR~IS^2eDR2YJ?=)4dWBla;03|t?V9bx5nhgn0)L4e!Rx+ z=jyq`3*t}VQtjUR_}h3>m}v>oh7dgzt{-y{oT!EO%k^@TzuMyE1}}e|TWa!&_K4ei z2OS0JQq%(yB4PLTCyeCZQOqGRfE&oN@HVrhyfo zRuHIUHb`O7HdQz2E>umMwh(@sO6}rVafLg;s#T@#EG0;&Y#)~$yR*XbZo2wqyOkrekYvm0{uSRuJRN1wetKufyt5MXvGKy(OvE>NW zA$hG_)e-8eL0E+nd>p;StwM2T0T$e|Ce)mDaV$p^a+aGSSe4bEeieKyn$RsI`0^NM ztc*cVFQzX5z71jDP%NQlcfpE$MyALeGDE(Fzp4B=@*SBb-;hbL-pXGkKS=qH$qblp zk;(i83HH7R-`D2H0C)!u zdKpTec^Q4?aL@yl>^p#M;BK(Vw85u`Rn4%6&tz16-}{DR_sD=coJpoNObKKND#mGS z@I&cA)vynwwbLrj7<^KTL#Q4*ts0!3!9knhq(uDiIEBGHM4B0}hhlKap@hMxI?XAW z&1erPx{CEsOdG`Nnb0ovbSRTfr;evbLfK3*rH7LA3Ok3-7!I1myimLX;+x>ne}nEE zdcrS%#Cu9MUho&a%W@}2PmVQy(Q>0@eE;n7(5!dUWv=8#qU+Mg`4Q6GF>CKAdY9%- zjh-4?JL{ET( z`e!3Y$e|C2|7bysK5prLYg(^LB*0(0!j2-p<|M#?ZdYxpNcufN$H` z!9C~@U_MZhB#5bzyd7Y@4uTb_-8Eehh@VBP_KGD2UJ14phzzpGXiSveo!KJvx)wAb zJg=a7N-E4^f+-6N{a%S|qY<*bPL$Y1goLd@dLf_HLZL=6+F-4f7t&c@wJ9WQH=A&P zkib|YB+lNmu0b^(%ce4&!&>67DJ$960g-$KDl$oa`hTjQ_3D{eCOJ_4hV>A3L1?xW zx|jHQv5aRje)>|5S!OLD3LZZBYLxnK@ywY273*ravi$4XZq+Crg4p?jb{i*7 zOdKWK72@kFi2VTc?6J#NN2uoaejOkY`Iq?5M~I&~)vzT1_1x%ob+1MD);hy0xO<_t za1(dGNq~88P%u2EmUNc?Rj9lZdU#VwumrQO(H!)sd}wh8s9ZLszKvf48>NpfL094! fhIxXd()s|iWPIm5f}ym<&G^UGJVh|zHQ@9w)J0se literal 0 HcmV?d00001 diff --git a/utils/__pycache__/loggers.cpython-313.pyc b/utils/__pycache__/loggers.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a48cf8540ea06c8004e496e9fa5f4c916950e52 GIT binary patch literal 1934 zcmah}&2Jl35TE_B*LLir&W92MRhqO)9MbsEM50hyAZbepIF%Peqy$#m&Bkol?56Ln zs}?Tlg*Fn0fD|>6kU)Y1+&FUNPax@sqt~cHqLPv~3y$8swfd-l|2_)QwXcvjt4Sk@AInbn8D8=f=&-XDY znT7gY2iYZ6h)@TNdw$04t< zl(uLP|9!7gvB)kjhN(8WP9V>{?|qHrtgr?%QQiP$aYn3b&frUgYg8PIq=S*f=mMB& zRJqjbJ<{kyUm8ZJNv9=OoM}}2L$wT_^Z-m}r$?thD#yp-p+PTHATJkp`A;w8MHaK& zIm`7~i{;WPv2=r)ww;f&q)ly?`i5&-OrEn%pUI9*kuFfrWy)N2afvd8R+lWoWZm&9 z`2>@x<;*k1v94JTlNM=($p)!Vrdroc-FD|ardZc~Vz4ASHa`zVAs^Z#blr3eN_Cy` z;`K&rnGF@n)FQNCxO2q1QJAo<6@1Tgu6WlARo`}K0RzA{pd(8+2!{r1YkHXx9v)Ah zzJ}!4GCWRouA1LX{B(T%Of5CInHt>5cHJ5Jc4%Gu`N(gbo7r=>$9L59N_?edwfsZJ zubscYcVC^_LtFTT*RFVBmp>C0N!4}NShRGVC3St#o2xpUZ_)LuRl|vHT6KNiCe(Lq z*K$2w598~^6n(?DP2KQ)VqdBH7G}-TQ2|WLadbT|5PU(P95!!}98$ucEg&2el=sQA zui#%%RxS&F1E=KWy2ck7({tzT3a`zG?b<9Jm5gLj#cW^^58P{j)XOMc1CDS_czqhc zK-FHD!Rv698mO&H09;|}9O{dQ>Nl``Kz~4!0_`J+?#9Jo(*%7VIohmqNC&t=laB`e z8*Xvpj70}mEQ^QDn|64&1M`r|(qWUQEYzn;FMNRgnBo;_%%A_W2)d0a6X)M4Uu5x* z-!2s|6)!U7e5o{5!j_41;M#753}B3ew>`-tVe+Nog(;S(SkZK0>Su_32O-W>io&49 zVj=4Da`2)o6&}O86!AQt10UOh#p}lWIe1eAU}7om$*Tkg&Br<=z0ad4WN`g$&{ES%wFWWf=(} zOwwE!!&ot!0RAi!#*XK4J~3D<)MUwd+p)$2;1fR4Nr9V6)F(lP~HR#!dojvJS2 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/my_loggers.cpython-313.pyc b/utils/__pycache__/my_loggers.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..debd67b888f90402f3cf1b360b1f122613c07923 GIT binary patch literal 1253 zcmZ`2&u`OK_}?;jqN%OyZ~JpV=b zHvxDjg%K2TX7C=FC*XiXz5|v~!pKlo7`aW>^pVRkT{DmQ5$7nYm_tGh&}*1ElSlX* zIO@oT>7TXH#x}*z+7!k%`We8udKDaLJvqvCOjp&6XJ$Zb<46iOwklo{uZKY+N`iVA zhg;UoB#C&$3D{0)0L0L|C~R3%|i5L|4fZXy=0kXesSc zPH2yPR+QEJRn{TFodIZ%7q@0)uP5<2jOAc`Un#ND9xv-AR{6sjz>(gXZmBEkGPJZN zxkPFZfUVv*k|}K>^f2!DacUPuDdb_yQ#bZ}p>;wp6#CLfTi$bT@-Qc3YdpJ&^ zQEc1Rg~t78T@)h!z8?v-%LAdgEZ{=F_w$gNlzNfNIi*~-P?OLXON|!y8E?38hxwb0 zrhmVY;?nLUzchMj81Y7TQ^v=!^7_UmJB6NN^2D6$Qd`DX{w1PMeK@pcw!LS~7w2~_ z?_2Y`*8Ji0?9+um7j~SNQ@hiP53U~?<%h*b6Wgulr+1Czm$Q4u@&H}oFTlnPN@KU{ zQz}Z7c9TvolKcdvKlR)w*PNnsJ!CwM!q|@!O0%M7u&)$v%cE|ZvhYqX^|4h^&b#*f zD5BIR3|9zTZvGoKiTHEe$b&bwQp8mnbn;ud#2sj-&oW8*4bCq l*&VuPE%l8z=47t_de2mN0ZT-?6!wEjo^UqE(P#Q)r%9W(#{ literal 0 HcmV?d00001 diff --git a/utils/__pycache__/password.cpython-313.pyc b/utils/__pycache__/password.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4a49dee9c6a7ef7b048cfd7270e3b11c90c54ff GIT binary patch literal 1013 zcmZ8fO=uHA6n?XR*<_n6rAd`)+pQLLZD|g+1&b|&+ENsXbcqNlNPZ@1B;5_Oo3`=P zqn?%fXVR0Wcf1! z_{J|i(Yi)wg&BL`fP)^w14NJo>*yLdQW80`quiB<|T zvJuf4)91bxok*sAO#EclEf8E!-p5x zVvHQ(Q3lwt3oT>t#27g;M)vE+hTT-`4ue5>rV{sNIlzvQML%KoRUk* znJ%It%*&%tM7Fw0_@ivLTE#1sFnK=wsM`-~3zKrO%*I&uTniK8kvVJG4$-LS+tiuA zPx=|3@XV+t7W9D&jg7g?DLLTOb9Q6MtJ+rf{@D$;8Fc$ zcI?8bGTv4cHPJGMPRy|*bF5(>o3l-Q_IJyOcR*4TU-j6lzL$N?OVb~n!~PGC4_2Cq z`7ioH%Z&eRtIXk#A7`On#E0Q^Bo&k24yVHMr?AS*RaTYB_n=bFO?B6a@G*+I$RM}) zLQo~gR!5}v9*D_dk+*(t(<{^}c!6AH7~ea8i5+bjA#@72erRA`X{cJ@Vz^%X8StrnP=t%pW&;EA4^V@kY zyIhrk#pc=(e{2Q#oxhZe-Gt?N0x$;@C^-t6sEI~GKqV?i2(?fvs*P5l+NlGzk}9Z9 z>OytXHK*<@QKH7lVNSh)&Pcz+&Z(HbA z)NRy{x}CP5?x3xx0UAUN(VeKfXd7xf?Lh6MU8uY19@K6lTaUrL#((Xl`!K$r9*B@b zM3Y9Pp^EarS8I$qR^Q6hVS*PpLY7>1N`+ts@48YU6x_<8^Z ze4xuol7tSNUtWAb502Vs->9wKLi_9BoJFgk;g!fCBVwN*x>R2h#bCil#%FmK3Z4ft^zv7X@j|6K7zQttSG&Bn&ES^@wR&vvN}qVA`3+rm0iGuzEh2Nu?5JQx}5SOgy0n z_2{@78ILE_$!Jmyq^FppcS2377t-23=F4c&q@IXpR4tlO1HFmVSTv#U3#_#8@=Sc( zPay{jo}PQLJJQYGC2v#F+f?XV^0q8^TkgeP=zMlBHa6prGt0SDJn1J|1Gn-y_G#Nt znd_BY+766)c$0M~Ie4VFX1gwT6+Jz7V~d`iMWtuK(zA@8fLW5!N%cSTZmeY^_CE(B zJd06=S59LRSK@W^US8QGjPQRp%8;#0t92vp5GrAR=Gy&__VUZ^Uw89Rsl1=~-{FmH zvA|x!Cx@39E-2n1R?M_=|BN`rH-MoPDn|esSN18jwc=h}=^gJdH}2(O;)XUE_in}D zbmhpq(bnlu>z;R77>P_vk(Q??uqS5WJ0t!lj?!OYtO>IwQXi-qlQb2NVfHMu>KQGb zW^z24!Q&O5RQ-|`;8q8J(KFG>w7D0>Iy?D>JB+>ZWz)K`Wi3AXC6pZep{$>;SybwC z;gSuURkPifyRY`TAY=3>p3#j35jfk$r7%&Gj&{F(gG1;w{u@oBC2D}IO8 zj+M5X%Vrd2%cf&^2$&U5!=%ov*>oDGLB`P=^J0tyhwkPEe(;S!heBe|_TI@Mzj|tk;P-qjhJ#a@B)Y*N%-=HUk z!-|e4=!lgJLPxtG!(qe>2Pd3Zs{^xC2(GThJSNvSVkVPYf|$$Xt{%*0uw8f^+-nE! zeP*)ZHQDf*Y6kdgUD-CbSWW#H+;WZh(VbR9Pz0D;T_d2;(Ei-y|mD%u`ZP42% z;% str: def pwd_verify(pwd_plant: str, pwd_hash: str) -> bool: - from my_loggers import logger + from utils.loggers import logger try: return pwd_context.verify(pwd_plant, pwd_hash) diff --git a/utils/safe_filemane.py b/utils/safe_filemane.py new file mode 100644 index 0000000..5cb4c11 --- /dev/null +++ b/utils/safe_filemane.py @@ -0,0 +1,39 @@ +import re +import time + +# Простая транслитерация +TRANSLIT_MAP = { + 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', + 'е': 'e', 'ё': 'yo', 'ж': 'zh', 'з': 'z', 'и': 'i', + 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', + 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', + 'у': 'u', 'ф': 'f', 'х': 'h', 'ц': 'c', 'ч': 'ch', + 'ш': 'sh', 'щ': 'sch', 'ъ': '', 'ы': 'y', 'ь': '', + 'э': 'e', 'ю': 'yu', 'я': 'ya' +} + +# Заглавные буквы → в ту же латиницу, но без capital (ниже мы в lower() всё равно переводим) +TRANSLIT_MAP.update({k.upper(): v for k, v in TRANSLIT_MAP.items()}) + +def transliterate(text: str) -> str: + return ''.join(TRANSLIT_MAP.get(ch, ch) for ch in text) + +def safeFilename(name: str) -> str: + # 1. Транслитерация кириллицы + name = transliterate(name) + + # 2. Приводим к нижнему регистру + name = name.lower() + + # 3. Заменяем всё, что не буква/цифра, на "_" + name = re.sub(r'[^a-z0-9]+', '_', name) + + # 4. Убираем повторяющиеся "_" + name = re.sub(r'_+', '_', name).strip('_') + + # 5. Ограничиваем длину + name = name[:80] or "file" + + # 6. Добавляем таймштамп + timestamp = int(time.time() * 1000) # миллисекунды + return f"{name}_{timestamp}" \ No newline at end of file diff --git a/uv.lock b/uv.lock index 616a623..3266a11 100644 --- a/uv.lock +++ b/uv.lock @@ -60,6 +60,64 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554, upload-time = "2020-10-08T19:00:49.856Z" }, ] +[[package]] +name = "pillow" +version = "12.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" }, + { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" }, + { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" }, + { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" }, + { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" }, + { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" }, + { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" }, + { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" }, + { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" }, + { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" }, + { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" }, + { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" }, + { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" }, + { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" }, + { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" }, + { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" }, + { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" }, + { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" }, + { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" }, + { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" }, + { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" }, + { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" }, + { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" }, + { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" }, + { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" }, + { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" }, + { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" }, + { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" }, + { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.1" @@ -97,6 +155,7 @@ source = { virtual = "." } dependencies = [ { name = "colorlog" }, { name = "passlib" }, + { name = "pillow" }, { name = "python-dotenv" }, { name = "sqlalchemy" }, ] @@ -105,6 +164,7 @@ dependencies = [ requires-dist = [ { name = "colorlog", specifier = ">=6.10.1" }, { name = "passlib", specifier = ">=1.7.4" }, + { name = "pillow", specifier = ">=12.0.0" }, { name = "python-dotenv", specifier = ">=1.2.1" }, { name = "sqlalchemy", specifier = ">=2.0.44" }, ]