From d375911150fa3b295a30ef0e1df079adf19027c4 Mon Sep 17 00:00:00 2001 From: "Evgeny (Krymmy) Momotov" Date: Thu, 13 Nov 2025 16:13:56 +0300 Subject: [PATCH] [UP] v 0.1.0 --- .gitignore | 192 +++++++++++++ pyproject.toml | 21 ++ requirements.txt | 2 + src/YandexApiManager/__init__.py | 0 src/YandexApiManager/api_manager.py | 188 +++++++++++++ src/YandexApiManager/data_models.py | 285 ++++++++++++++++++++ src/YandexApiManager/yandex_fleet_models.py | 75 ++++++ tests/__init__.py | 0 8 files changed, 763 insertions(+) create mode 100644 .gitignore create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 src/YandexApiManager/__init__.py create mode 100644 src/YandexApiManager/api_manager.py create mode 100644 src/YandexApiManager/data_models.py create mode 100644 src/YandexApiManager/yandex_fleet_models.py create mode 100644 tests/__init__.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a36c19 --- /dev/null +++ b/.gitignore @@ -0,0 +1,192 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +database/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sql +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +#Keys +*.pem + +#bin +*.bin + +#try_file +try_* + +.vscode + +.env_* +loggs/ +mediafiles/ +db.sqlite* \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2bb318c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[project] +name = "yandexapimanager" +version = "0.1.0" +description = "" +authors = [ + {name = "Evgeny (Krymmy) Momotov",email = "evgeny.momotov@gmail.com"} +] +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "pydantic (==2.11.7)", + "aiohttp (==3.12.15)" +] + +[tool.poetry] +packages = [{include = "yandexapimanager", from = "src"}] + + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4905caa --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +aiohttp==3.12.15 +pydantic==2.11.7 diff --git a/src/YandexApiManager/__init__.py b/src/YandexApiManager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/YandexApiManager/api_manager.py b/src/YandexApiManager/api_manager.py new file mode 100644 index 0000000..6f01fac --- /dev/null +++ b/src/YandexApiManager/api_manager.py @@ -0,0 +1,188 @@ +import uuid +from enum import Enum +from datetime import datetime +from typing import List, Dict, Any, Optional + +import aiohttp + +from .data_models import * +from .yandex_fleet_models import * + + +class YandexManagerClient: + def __init__(self, base_url: str, auth_key: Optional[str] = None, token: Optional[str] = None): + self.base_url = base_url.rstrip('/') + self.token: Optional[str] = None + if token: + self.token = token + + if auth_key and not token: + self.auth_key = auth_key + else: + self.auth_key = None + + if not self.token and not self.auth_key: + raise ValueError("Either token or auth_key must be provided") + + + async def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]: + headers = kwargs.get('headers', {}) + if self.token: + headers['Authorization'] = f'Bearer {self.token}' + kwargs['headers'] = headers + async with aiohttp.ClientSession() as session: + async with session.request(method, f"{self.base_url}{endpoint}", **kwargs) as response: + if response.status >= 400: + error_data = await response.json() + raise Exception(f"API Error {response.status}: {error_data}") + return await response.json() + + + async def register_manager(self, auth_key: Optional[str] = None) -> AccessResponse: + if auth_key is None and self.auth_key is None: + raise ValueError("Auth key is required for registration") + auth_key = auth_key or self.auth_key + data = Registration(auth_key=auth_key).dict() + response = await self._request('POST', '/yandex_manager/register', json=data) + access_response = AccessResponse(**response) + self.token = access_response.token + return access_response + + async def create_call(self, data: CallIn) -> Call: + response = await self._request('POST', f'/calls/', data=data.model_dump()) + return Call(**response) + + + async def list_yandex_fleets(self, **params): + response = await self._request('GET', f'/yandex_fleet/', params=params) + return [YandexFleet(**fleet) for fleet in response] + + async def get_yandex_fleet(self, fleet_id: str): + response = await self._request('GET', f'/yandex_fleet/{fleet_id}') + return YandexFleet(**response) + + async def get_yandex_fleet_api_key(self, fleet_id: str): + response = await self._request('GET', f'/yandex_fleet/{fleet_id}/api_key') + return YandexFleetApiKey(**response) + + async def list_drivers(self, **params): + response = await self._request('GET', f'/yandex_fleet/drivers', params=params) + return [Driver(**driver) for driver in response] + + async def get_driver(self, driver_id: str): + response = await self._request('GET', f'/yandex_fleet/drivers/{driver_id}') + return Driver(**response) + + async def get_driver_profile(self, driver_id: str): + response = await self._request('GET', f'/yandex_fleet/drivers/{driver_id}/profile') + return DriverProfile(**response) + + async def list_driver_groups(self, **params): + response = await self._request('GET', f'/yandex_fleet/drivers_groups', params=params) + return [DriversGroup(**group) for group in response] + + async def get_driver_group(self, group_id: str): + response = await self._request('GET', f'/yandex_fleet/drivers_groups/{group_id}') + return DriversGroup(**response) + + + async def _inspection_rout(self, method: str, endpoint: str, **kwargs): + response = await self._request(method, f'/inspections/{endpoint.strip("/")}', **kwargs) + return response + + async def list_inspection_worktime(self, **params): + response = await self._inspection_rout('GET', 'worktime', params=params) + return [InspectionWorkTime(**worktime) for worktime in response] + + async def get_inspection_worktime(self, worktime_id: str): + response = await self._inspection_rout('GET', f'worktime/{worktime_id}') + return InspectionWorkTime(**response) + + async def list_actions(self, **params): + response = await self._inspection_rout('GET', 'actions', params=params) + return [Action(**action) for action in response] + + async def get_action(self, action_id: str): + response = await self._inspection_rout('GET', f'actions/{action_id}') + return Action(**response) + + async def get_check(self, check_id: str): + response = await self._inspection_rout('GET', f'checks/{check_id}') + return Check(**response) + + + async def list_conditions(self, **params): + response = await self._inspection_rout('GET', 'conditions', params=params) + return [Condition(**condition) for condition in response] + + async def get_condition(self, condition_id: str): + response = await self._inspection_rout('GET', f'conditions/{condition_id}') + return Condition(**response) + + async def list_expressions(self, **params): + response = await self._inspection_rout('GET', 'expressions', params=params) + return [ConditionalExpression(**expression) for expression in response] + + async def get_expression(self, expression_id: str): + response = await self._inspection_rout('GET', f'expressions/{expression_id}') + return ConditionalExpression(**response) + + async def list_yandex_fleet_inspections(self, **params): + response = await self._inspection_rout('GET', 'fleets', params=params) + return [YandexFleetInspection(**inspection) for inspection in response] + + async def get_yandex_fleet_inspection(self, inspection_id: str): + response = await self._inspection_rout('GET', f'fleets/{inspection_id}') + return YandexFleetInspection(**response) + + async def list_fleet_checks(self, **params): + response = await self._inspection_rout('GET', 'fleet_checks', params=params) + return [YandexFleetInspectionCheeks(**check) for check in response] + + async def get_fleet_check(self, check_id: str): + response = await self._inspection_rout('GET', f'fleet_checks/{check_id}') + return YandexFleetInspectionCheeks(**response) + + async def list_group_inspections(self, **params): + response = await self._inspection_rout('GET', f'groups/', params=params) + return [GroupInspection(**inspection) for inspection in response] + + async def get_group_inspection(self, group_id: str): + response = await self._inspection_rout('GET', f'groups/{item_id}') + return GroupInspection(**response) + + async def list_group_checks(self, **params): + response = await self._inspection_rout('GET', f'group_checks/', params=params) + return [GroupInspectionCheek(**check) for check in response] + + async def get_group_check(self, check_id: str): + response = await self._inspection_rout('GET', f'group_checks/{check_id}') + return GroupInspectionCheek(**response) + + async def list_driver_inspections(self, **params): + response = await self._inspection_rout('GET', f'drivers/', params=params) + return [DriverInspection(**inspection) for inspection in response] + + async def get_driver_inspection(self, driver_id: str): + response = await self._inspection_rout('GET', f'drivers/{driver_id}') + return DriverInspection(**response) + + async def list_driver_checks(self, **params): + response = await self._inspection_rout('GET', f'driver_checks/', params=params) + return [DriverInspectionCheek(**check) for check in response] + + async def get_driver_check(self, check_id: str): + response = await self._inspection_rout('GET', f'driver_checks/{check_id}') + return DriverInspectionCheek(**response) + + async def update_driver_check_time(self, check_id: str): + response = await self._inspection_rout('patch', f'driver_checks/{check_id}/last_check') + return DriverInspectionCheek(**response) + + async def update_group_check_time(self, check_id: str): + response = await self._inspection_rout('patch', f'group_checks/{check_id}/last_check') + return GroupInspectionCheek(**response) + + async def update_fleet_check_time(self, check_id: str): + response = await self._inspection_rout('patch', f'fleet_checks/{check_id}/last_check') + return YandexFleetInspectionCheeks(**response) \ No newline at end of file diff --git a/src/YandexApiManager/data_models.py b/src/YandexApiManager/data_models.py new file mode 100644 index 0000000..0c246b9 --- /dev/null +++ b/src/YandexApiManager/data_models.py @@ -0,0 +1,285 @@ +import re +from enum import Enum + +from pydantic import BaseModel, Field, validator +from typing import Optional, Dict, Any + +class CallSide(str, Enum): + DRIVER = "driver" + BOT = "bot" + + +class CallStatus(str, Enum): + PENDING = "PENDING" + RUNNING = "RUNNING" + COMPLETED = "COMPLETED" + ALL_MISSED = "ALL_MISSED" + FAILED = "FAILED" + +class CheckType(str, Enum): + period = "period" + specifically = "specifically" + + +class Operations(Enum): + eq = "==", + gt = ">", + gte = ">=", + lt = "<", + lte = "<=" + + +class AccessResponse(BaseModel): + success: bool + token: str + +class Registration(BaseModel): + auth_key: str + + +class User(BaseModel): + id: Optional[int] = Field(description="Идентификатор (автоматический)", default=None) + username: str = Field(max_length=150, description="Пользовательское имя", example="john_doe") + #password: str = Field(max_length=255, description="Пароль", example="secret123") + email: Optional[str] = Field(max_length=254, default="", description="Электронная почта", example="john@example.com") + first_name: Optional[str] = Field(max_length=150, default="", description="Имя", example="John") + last_name: Optional[str] = Field(max_length=150, default="", description="Фамилия", example="Doe") + #is_staff: bool = Field(default=False, description="Статус администратора") + #is_active: bool = Field(default=False, description="Активен") + #is_superuser: bool = Field(default=False, description="Суперпользователь") + #date_joined: str = Field(description="Дата создания", example="2024-01-01T12:00:00") + #last_login: Optional[str] = Field(null=True, default=None, description="Последний вход") + + class Config: + extra = "ignore" + from_attributes = True + + +class BasicModel(BaseModel): + id: Optional[int] = Field(description="Идентификатор (автоматический)", default=None) + created_at: Optional[str] = Field(description="Дата создания", example="2024-01-01T12:00:00") + updated_at: Optional[str] = Field(description="Дата обновления", example="2024-01-01T12:00:00") + + class Config: + extra = "ignore" + from_attributes = True + + +class UUIDModel(BaseModel): + unique_id: str = Field(description="Уникальный идентификатор", example="00000000-0000-0000-0000-000000000000") + + +class CallPrompt(BasicModel): + owner: Optional[User] = Field(description="Владелец") + name: str = Field(max_length=255, description="Название промпта", example="Приветствие клиенту") + content: str = Field(description="Содержание промпта", example="Добро пожаловать в службу поддержки") + speak_first: bool = Field(default=True, description="Говорить первым") + can_interrupt_greeting: bool = Field(default=False, description="Может прерывать приветствие") + + class Config: + extra = "ignore" + from_attributes = True + + +class YandexFleet(BasicModel): + owner: Optional[User] = Field(description="Владелец") + name: str = Field(max_length=255, description="Название флота", example="Флот такси") + park_id: str = Field(max_length=255, description="ID флота в Yandex", example="park-12345") + timezone: str = Field( + max_length=255, + default="Europe/Moscow", + description="Часовой пояс", + example="Europe/Moscow" + ) + + class Config: + extra = "ignore" + from_attributes = True + +class YandexFleetApiKey(UUIDModel, BasicModel): + yandex_fleet: YandexFleet + api_key: str + + +class Driver(BasicModel): + yandex_fleet: Optional[YandexFleet] = Field(description="ID или название флота Yandex", example="fleet-123") + driver_id: str = Field(max_length=64, description="ID драйвера в Yandex", example="drv-abc123") + created_at: Optional[str] = Field(description="Дата создания", example="2024-01-01T12:00:00") + updated_at: Optional[str] = Field(description="Дата обновления", example="2024-01-01T12:00:00") + + class Config: + extra = "ignore" + from_attributes = True + + +class DriversGroup(BasicModel, UUIDModel): + owner: User = Field(description="Владелец группы") + drivers: Optional[List[Driver]] = Field(description="Список водителей в группе", default=[]) + name: str = Field( + ..., + max_length=255, + description="Название группы" + ) + + +class Call(UUIDModel, BasicModel): + owner: Optional[User] = Field(description="Владелец", default=None) + call_datetime: Optional[str] = Field(description="Время последнего звонка (по МСК)", example="2024-01-01T12:00:00", default=None) + prompt: Optional[CallPrompt] = Field(description="Промпт", default=None) + driver: Optional[Driver] = Field(description="Водитель") + status: Optional[CallStatus] = Field(default=CallStatus.PENDING, description="Статус звонка") + data: Optional[Dict[str, Any]] = Field(description="Дополнительные данные", nullable=True, default=None) + channel_id: Optional[str] = Field(description="ID канала", example=" tweak-456", default=None) + audio_file: Optional[str] = Field(description="Путь к аудио", example="/audio/call-123.mp3", default=None) + + class Config: + extra = "ignore" + from_attributes = True + + +class CallIn(BaseModel): + owner_id: int = Field(description="Владелец") + call_datetime: Optional[str] = Field(description="Время последнего звонка (по МСК)", example="2024-01-01T12:00:00", default=None) + prompt_id: int = Field(description="Промпт",) + driver_id: int = Field(description="Водитель") + status: CallStatus = Field(default=CallStatus.PENDING, description="Статус звонка") + data: Optional[Dict[str, Any]] = Field(description="Дополнительные данные", nullable=True, default=None) + channel_id: Optional[str] = Field(description="ID канала", example=" tweak-456", null=True, default=None) + audio_file: Optional[str] = Field(description="Путь к аудио", example="/audio/call-123.mp3", null=True, default=None) + + class Config: + extra = "ignore" + from_attributes = True + + +class InspectionWorkTime(BasicModel, UUIDModel): + owner: User = Field(description="Владелец", example={"id": 1, "username": "admin"}) + is_public: Optional[bool] = Field(default=False, description="Открыт для всех") + check_from: Optional[str] = Field( + default="08:00:00", + description="Время начала проверки", + pattern=r"^(2[0-3]|[01]\d):([0-5]\d):([0-5]\d)$" + ) + check_to: Optional[str] = Field( + default="22:00:00", + description="Время окончания проверки", + pattern=r"^(2[0-3]|[01]\d):([0-5]\d):([0-5]\d)$" + ) + monday: Optional[bool] = Field(default=False, description="Понедельник") + tuesday: Optional[bool] = Field(default=False, description="Вторник") + wednesday: Optional[bool] = Field(default=False, description="Среда") + thursday: Optional[bool] = Field(default=False, description="Четверг") + friday: Optional[bool] = Field(default=False, description="Пятница") + saturday: Optional[bool] = Field(default=False, description="Суббота") + sunday: Optional[bool] = Field(default=False, description="Воскресенье") + + class Config: + extra = "ignore" + from_attributes = True + + +class CallDriver(BasicModel, UUIDModel): + owner: User = Field(description="Владелец (пользователь)") + prompt: CallPrompt = Field(description="Промпт для звонка") + + class Config: + extra = "ignore" + from_attributes = True + + +class SupplyHoursCondition(BasicModel, UUIDModel): + for_days: Optional[int] = Field(default=0, description="Дни", ge=-2147483648, le=2147483647) + for_hours: Optional[int] = Field(default=0, description="Часы", ge=-2147483648, le=2147483647) + for_minutes: Optional[int] = Field(default=0, description="Минуты", ge=-2147483648, le=2147483647) + target_time: int = Field(description="Целевой временной интервал", ge=-2147483648, le=2147483647) + operator: Operations = Field(description="Оператор", default=Operations.gte) + + class Config: + extra = "ignore" + from_attributes = True + + +class Action(BasicModel, UUIDModel): + owner: Optional[User] = Field(..., description="Владелец объекта", default=None) + call_driver_action: Optional[CallDriver] = Field( + default=None, + description="Действие по вызову водителя", + + ) + +class Condition(BasicModel, UUIDModel): + owner: Optional[User] = Field(..., description="Владелец условия", default=None) + supply_hours_condition: Optional[SupplyHoursCondition] = Field(..., description="Условие по часам поставки") + + +class ConditionalExpression(BasicModel, UUIDModel): + owner: Optional[User] = Field(..., description="Владелец условия", default=None) + condition: Optional[Condition] = Field( + default=None, + description="Условие (например, `SupplyHoursCondition`)", + + ) + true_action: Optional[Action] = Field( + default=None, + description="Действие, если условие истинно", + + ) + false_action: Optional[Action] = Field( + default=None, + description="Действие, если условие ложно", + + ) + + +class Check(BasicModel, UUIDModel): + owner: Optional[User] = Field( + ..., + description="Владелец проверки", + default=None + ) + check_type: CheckType = Field( + ..., + description="Тип проверки", + default=CheckType.period + ) + time: Optional[str] = Field( + default="12:00:00", + description="Время проверки (в формате hh:mm:ss)", + format="time" + ) + +class BaseInspectionModel(UUIDModel): + owner: Optional[User] = Field(..., description="Владелец проверки", default=None) + name : str = Field(..., description="Название проверки") + work_time: InspectionWorkTime = Field(..., description="Рабочее время") + + +class BaseInspectionsCheckModel(UUIDModel): + owner: Optional[User] = Field(..., description="Владелец проверки", default=None) + check_obj: Optional[Check] = Field(..., description="Проверка", default=None) + conditional_expression: Optional[ConditionalExpression] = Field(..., description="Условное выражение", default=None) + last_check: Optional[str] = Field(default=None, description="Последняя проверка", format="date-time") + + +class YandexFleetInspection(BaseInspectionModel, BasicModel): + yandex_fleet: YandexFleet = Field(..., description="Флот") + + +class GroupInspection(BaseInspectionModel, BasicModel): + group: DriversGroup = Field(..., description="Группа") + + +class DriverInspection(BaseInspectionModel, BasicModel): + driver: Driver = Field(..., description="Водитель") + + +class YandexFleetInspectionCheeks(BaseInspectionsCheckModel, BasicModel): + yandex_fleet_inspection: YandexFleetInspection = Field(..., description="Инспекция флота") + + +class GroupInspectionCheek(BaseInspectionsCheckModel, BasicModel): + group_inspection: GroupInspection = Field(..., description="Инспекция группы") + + +class DriverInspectionCheek(BaseInspectionsCheckModel, BasicModel): + driver_inspection: DriverInspection = Field(..., description="Инспекция водителя") \ No newline at end of file diff --git a/src/YandexApiManager/yandex_fleet_models.py b/src/YandexApiManager/yandex_fleet_models.py new file mode 100644 index 0000000..5a64ffe --- /dev/null +++ b/src/YandexApiManager/yandex_fleet_models.py @@ -0,0 +1,75 @@ +from enum import Enum +from typing import Optional +from pydantic import BaseModel, validator, Field + +class WorkStatus(Enum): + WORKING = "working" + NOT_WORKING = "not_working" + FIRED = "fired" + +class EmploymentType(str, Enum): + selfemployed = "selfemployed" + park_employee = "park_employee" + individual_entrepreneur = "individual_entrepreneur" + +class Account(BaseModel): + balance_limit: str = Field(..., description="Лимит по счету, например: 50") + block_orders_on_balance_below_limit: bool = Field( + False, description="Запрещены ли все заказы при балансе ниже лимита" + ) + payment_service_id: str = Field(..., description="ID для платежа, например: 12345") + work_rule_id: Optional[str] = Field(None, description="Идентификатор условия работы, например: bc43tre6ba054dfdb7143ckfgvcby63e") + + +class OrderProvider(BaseModel): + partner: bool = Field(..., description="Доступны ли заказы от партнера") + platform: bool = Field(..., description="Доступны ли заказы от платформы") + + +class Profile(BaseModel): + comment: Optional[str] = Field(None, description="Прочее, например: great driver") + feedback: Optional[str] = Field(None, description="Прочее (доступно сотрудникам парка), например: great driver") + fire_date: Optional[str] = Field(None, description="Дата увольнения из парка в формате ISO 8601 без временной зоны, например: 2020-10-28") + hire_date: Optional[str] = Field(None, description="Дата приема в парк в формате ISO 8601 без временной зоны, например: 2020-10-28") + work_status: WorkStatus = Field(..., description="Статус работы водителя: working, not_working, fired") + + +class FullName(BaseModel): + first_name: str = Field(..., description="Имя, например: Ivan") + last_name: str = Field(..., description="Фамилия, например: Ivanov") + middle_name: Optional[str] = Field(None, description="Отчество, например: Ivanovich") + + +class DriverLicenseExperience(BaseModel): + total_since_date: str = Field(..., description="Дата начала водительских обязанностей в формате ISO 8601 без временной зоны, например: 1970-01-01") + + +class DriverLicense(BaseModel): + birth_date: Optional[str] = Field(None, description="Дата рождения в формате ISO 8601 без временной зоны, например: 1975-10-28") + country: Optional[str] = Field(None, description="Страна выдачи водительского удостоверения (трехбуквенный код), например: rus") + expiry_date: Optional[str] = Field(None, description="Дата окончания действия в формате ISO 8601 без временной зоᔊы, например: 2050-10-28") + issue_date: Optional[str] = Field(None, description="Дата выдачи в формате ISO 8601 без временной зоны, например: 2020-10-28") + number: Optional[str] = Field(None, description="Серия и номер водительского удостоверения, например: 070236") + + +class ContactInfo(BaseModel): + address: Optional[str] = Field(None, description="Адрес, например: Moscow, Ivanovskaya Ul., bld. 40/2, appt. 63") + email: Optional[str] = Field(None, description="Э trueлронная почта, например: example-email@example.com") + phone: Optional[str] = Field(None, description="Номер телефона, например: +79999999999", pattern=r"^\+\d{1,15}$") + + +class Person(BaseModel): + contact_info: Optional[ContactInfo] = Field(None, description="Контактная информация водителя") + driver_license: Optional[DriverLicense] = Field(None, description="Информация о водительском уд揪тверении") + driver_license_experience: Optional[DriverLicenseExperience] = Field(None, description="Водительский стаж с даты") + employment_type: EmploymentType = Field(..., description="Тип занятости: selfemployed, park_employee, individual_entrepreneur") + full_name: Optional[FullName] = Field(None, description="Полное имя водителя") + tax_identification_number: Optional[str] = Field(None, min_length=1, description="Идентификационный номер налогоплательщика, например: 7743013902") + + +class DriverProfile(BaseModel): + account: Optional[Account] = Field(None, description="Счет водителя") + profile: Optional[Profile] = Field(None, description="Профиль водителя") + person: Optional[Person] = Field(None, description="Персональная информация водителя") + order_provider: Optional[OrderProvider] = Field(None, description="Доступность заказов водителем") + car_id: Optional[str] = Field(None, description="ID машины, например: 12345", min_length=1, max_length=100) \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29