Compare commits
18 Commits
995be04dcb
...
749e37354d
Author | SHA1 | Date |
---|---|---|
Сергей Ванюшкин | 749e37354d | |
Сергей Ванюшкин | a5eebd15ba | |
Сергей Ванюшкин | 43eca19d91 | |
Сергей Ванюшкин | 291c61f873 | |
Сергей Ванюшкин | 09d0627d70 | |
Сергей Ванюшкин | 5173fcd36c | |
Сергей Ванюшкин | 2754b82b5d | |
Сергей Ванюшкин | 35659529b4 | |
Сергей Ванюшкин | 181c6f10af | |
Сергей Ванюшкин | 015a0bcc87 | |
Сергей Ванюшкин | 628babc295 | |
Сергей Ванюшкин | f807bdd275 | |
Сергей Ванюшкин | 2afba14e44 | |
Сергей Ванюшкин | 45dd8dc73e | |
Сергей Ванюшкин | f667026d62 | |
Сергей Ванюшкин | 0ba422397a | |
Сергей Ванюшкин | b223053cf6 | |
Сергей Ванюшкин | 58ecd82bb6 |
|
@ -217,4 +217,3 @@ fabric.properties
|
|||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace # убирает лишние пробелы
|
||||
- id: check-added-large-files # проверяет тяжелые файлы на изменения
|
||||
- id: check-yaml # проверяет синтаксис .yaml файлов
|
||||
- id: check-json # проверяет синтаксис .json файлов
|
||||
exclude: launch.json
|
||||
- id: check-case-conflict # проверяет файлы, которые могут конфликтовать в файловых системах без учета регистра.
|
||||
- id: check-merge-conflict # проверяет файлы, содержащие конфликтные строки слияния.
|
||||
- id: double-quote-string-fixer # заменяет " на '
|
||||
- id: end-of-file-fixer # добавляет пустую строку в конце файла
|
||||
|
||||
# Отсортировывает импорты в проекте
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
exclude: __init__.py
|
||||
args: [ --profile, black, --filter-files ]
|
||||
|
||||
# Обновляет синтаксис Python кода в соответствии с последними версиями
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py310-plus]
|
||||
|
||||
# Форматирует код под PEP8
|
||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||
rev: v2.0.1
|
||||
hooks:
|
||||
- id: autopep8
|
||||
args: [--max-line-length=120, --in-place]
|
||||
|
||||
# Сканер стилистических ошибок, нарушающие договоренности PEP8
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
exclude: "__init__.py"
|
||||
args: ["--ignore=E501,F821", "--max-line-length=120"]
|
||||
|
||||
# Проверка статических типов с помощью mypy
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.991
|
||||
hooks:
|
||||
- id: mypy
|
||||
exclude: 'migrations'
|
|
@ -8,6 +8,10 @@ RUN mkdir -p /usr/src/fastfood
|
|||
|
||||
WORKDIR /usr/src/fastfood
|
||||
|
||||
COPY . .
|
||||
COPY ./pyproject.toml .
|
||||
|
||||
COPY ./poetry.lock .
|
||||
|
||||
RUN touch /usr/src/RUN_IN_DOCKER
|
||||
|
||||
RUN poetry install
|
||||
|
|
21
README.md
21
README.md
|
@ -53,19 +53,19 @@ Fastapi веб приложение реализующее api для общеп
|
|||
Для Menu доступен метод GET возвращающий все его SubMenu. Аналогично для SubMenu реализован метод для возврата всех Dish.
|
||||
|
||||
### Спринт 2
|
||||
- 1й пункт ТЗ
|
||||
Тесты реализованы в виде 2х классов
|
||||
- 1й пункт ТЗ
|
||||
Тесты реализованы в виде 2х классов
|
||||
`TastBaseCrud` включает 3 подкласса `Menu`, `Submenu`, `Dish` которые реализуют интерфейсы взаимодействия с endpoint'ами реализованных на предыдущем спринте сущностей. Каждый подкласс реализует методы GET(получение всех сущностей), Get(получение конкректной сущности), Post(создание), Patch(обновление), Delete(удаления). Так же в классе реализованы 3 тестовых функции, которые осуществляют тестирование соответствующих endpoint'ов
|
||||
`TestContinuity` реализует последовательность сценария «Проверка кол-ва блюд и подменю в меню» из Postman
|
||||
|
||||
- 2й пункт ТЗ
|
||||
Реализованы 3 контейнера(db, app, tests). В db написан блок "проверки здоровья", от которого зависят контейнеры app и test, который гарантирует, что зависимые контейнеры не будут запущены о полной готовности db.
|
||||
- 2й пункт ТЗ
|
||||
Реализованы 3 контейнера(db, app, tests). В db написан блок "проверки здоровья", от которого зависят контейнеры app и test, который гарантирует, что зависимые контейнеры не будут запущены о полной готовности db.
|
||||
|
||||
- 3й пункт ТЗ
|
||||
- 3й пункт ТЗ
|
||||
см. функцию `get_menu_item` на 28 строке в файле
|
||||
<base_dir>/fastfood/crud/menu.py
|
||||
|
||||
- 4й пункт ТЗ
|
||||
- 4й пункт ТЗ
|
||||
см. класс `TestContinuity` в файле
|
||||
<base_dir>/tests/test_api.py
|
||||
|
||||
|
@ -101,12 +101,11 @@ Fastapi веб приложение реализующее api для общеп
|
|||
- Запуск FAstAPI приложения
|
||||
> `$ docker-compose -f compose_app.yml up `
|
||||
|
||||
После успешного запуска образов документация по API будет доступна по адресу <a href="http://localhost:8000/docs">http://localhost:8000</a>
|
||||
После успешного запуска образов документация по API будет доступна по адресу <a href="http://localhost:8000/docs">http://localhost:8000</a>
|
||||
|
||||
По завершении работы остановите контейнеры
|
||||
> `$ docker-compose -f compose_app.yml down`
|
||||
|
||||
|
||||
- Запуск тестов
|
||||
> `$ docker-compose -f compose_test.yml up`
|
||||
|
||||
|
@ -115,7 +114,7 @@ Fastapi веб приложение реализующее api для общеп
|
|||
|
||||
|
||||
### Linux
|
||||
Установите и настройте postgresql согласно офф. документации. Создайте пользователя и бд.
|
||||
Установите и настройте postgresql согласно офф. документации. Создайте пользователя и бд.
|
||||
|
||||
Установите систему управления зависимостями
|
||||
> `$ pip[x] install poetry`
|
||||
|
@ -134,7 +133,7 @@ Fastapi веб приложение реализующее api для общеп
|
|||
## Запуск
|
||||
Запуск проекта возможен в 2х режимах:
|
||||
- Запуск в режиме "prod" с ключем --run-server
|
||||
Подразумевает наличие уже созданных таблиц в базе данных(например с помощью Alembic). Манипуляций со структурой БД не происходит. Данные не удаляются.
|
||||
Подразумевает наличие уже созданных таблиц в базе данных(например с помощью Alembic). Манипуляций со структурой БД не происходит. Данные не удаляются.
|
||||
|
||||
- Запуск в режиме "dev" c ключем --run-test-server
|
||||
В этом случае при каждом запуске проекта все таблицы с данными удаляются из БД и создаются снова согласно описанных моделей.
|
||||
|
@ -163,5 +162,3 @@ Fastapi веб приложение реализующее api для общеп
|
|||
|
||||
## Лицензия
|
||||
Распространяется под [MIT лицензией](https://mit-license.org/).
|
||||
|
||||
|
||||
|
|
|
@ -1,45 +1,60 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
redis:
|
||||
container_name: redis_test
|
||||
|
||||
image: redis:7.2.4-alpine3.19
|
||||
|
||||
ports:
|
||||
- '6380:6379'
|
||||
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli","ping" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
db:
|
||||
container_name: pgdb
|
||||
|
||||
|
||||
image: postgres:15.1-alpine
|
||||
|
||||
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
|
||||
ports:
|
||||
- 6432:5432
|
||||
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
|
||||
|
||||
app:
|
||||
container_name: fastfood_app
|
||||
|
||||
|
||||
build:
|
||||
context: .
|
||||
|
||||
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
|
||||
ports:
|
||||
- 8000:8000
|
||||
|
||||
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
restart: always
|
||||
|
||||
command: /bin/bash -c 'poetry run python /usr/src/fastfood/manage.py --run-test-server'
|
||||
|
||||
|
|
|
@ -1,45 +1,63 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
redis:
|
||||
container_name: redis_test
|
||||
|
||||
image: redis:7.2.4-alpine3.19
|
||||
|
||||
ports:
|
||||
- '6380:6379'
|
||||
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli","ping" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
db:
|
||||
container_name: pgdb_test
|
||||
|
||||
|
||||
image: postgres:15.1-alpine
|
||||
|
||||
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB_TEST}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
|
||||
ports:
|
||||
- 6432:5432
|
||||
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB_TEST}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
|
||||
|
||||
app:
|
||||
container_name: fastfood_app_test
|
||||
|
||||
|
||||
build:
|
||||
context: .
|
||||
|
||||
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
|
||||
ports:
|
||||
- 8000:8000
|
||||
|
||||
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
- .:/usr/src/fastfood
|
||||
|
||||
restart: always
|
||||
|
||||
command: /bin/bash -c 'poetry run pytest -vv'
|
||||
|
||||
|
|
|
@ -4,3 +4,4 @@ POSTGRES_USER=testuser
|
|||
POSTGRES_PASSWORD=test
|
||||
POSTGRES_DB=fastfood_db
|
||||
POSTGRES_DB_TEST=testdb
|
||||
REDIS_DB=redis://localhost
|
||||
|
|
|
@ -1,82 +1,43 @@
|
|||
import json
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from fastfood.routers.dish import router as dish_router
|
||||
from fastfood.routers.menu import router as menu_router
|
||||
from fastfood.routers.submenu import router as submenu_router
|
||||
|
||||
description = """
|
||||
# 🔥🔥🔥Fastfood-API поможет тебе подкрепиться 🔥🔥🔥
|
||||
|
||||
### У нас есть Menu. Ты можеш выбрать блюда из кухни, которая тебе нравится
|
||||
|
||||
## Menu
|
||||
|
||||
Ты можешь **add menu**.
|
||||
|
||||
Ты можешь **read menu**.
|
||||
|
||||
Ты можешь **patch menu**.
|
||||
|
||||
Ты можешь **delete menu**.
|
||||
|
||||
### У нас есть в SubMenu, где ты сможешь найти
|
||||
десерты/напитки/супчики/прочие вкусности
|
||||
|
||||
# SubMenu
|
||||
|
||||
Ты можешь **add submenu into menu**.
|
||||
|
||||
Ты можешь **read submenu**.
|
||||
|
||||
Ты можешь **patch submenu**.
|
||||
|
||||
Ты можешь **delete menu**.
|
||||
|
||||
### У нас есть в Dish, где ты сможешь найти блюдо по вкусу
|
||||
|
||||
# Dish
|
||||
|
||||
Ты можешь **add dish into submenu**.
|
||||
|
||||
Ты можешь **read dish**.
|
||||
|
||||
Ты можешь **patch dish**.
|
||||
|
||||
Ты можешь **delete dish**.
|
||||
|
||||
## Приятного аппетита
|
||||
"""
|
||||
|
||||
|
||||
tags_metadata = [
|
||||
{
|
||||
"name": "menu",
|
||||
"description": "Операции с меню.",
|
||||
'name': 'menu',
|
||||
'description': 'Операции с меню.',
|
||||
},
|
||||
{
|
||||
"name": "submenu",
|
||||
"description": "Подменю и работа с ним",
|
||||
'name': 'submenu',
|
||||
'description': 'Подменю и работа с ним',
|
||||
},
|
||||
{"name": "dish", "description": "Блюда и работа с ними"},
|
||||
{'name': 'dish', 'description': 'Блюда и работа с ними'},
|
||||
]
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
def create_app(redis=None) -> FastAPI:
|
||||
"""
|
||||
Фабрика FastAPI.
|
||||
"""
|
||||
with open('openapi.json') as f:
|
||||
js = json.load(f)
|
||||
|
||||
app = FastAPI(
|
||||
title="Fastfood-API",
|
||||
description=description,
|
||||
version="0.0.1",
|
||||
title=js['info']['title'],
|
||||
description=js['info']['description'],
|
||||
version=js['info']['version'],
|
||||
contact={
|
||||
"name": "Sergey Vanyushkin",
|
||||
"url": "http://pi3c.ru",
|
||||
"email": "pi3c@yandex.ru",
|
||||
'name': 'Sergey Vanyushkin',
|
||||
'url': 'http://pi3c.ru',
|
||||
'email': 'pi3c@yandex.ru',
|
||||
},
|
||||
license_info={
|
||||
"name": "MIT license",
|
||||
"url": "https://mit-license.org/",
|
||||
'name': 'MIT license',
|
||||
'url': 'https://mit-license.org/',
|
||||
},
|
||||
openapi_tags=tags_metadata,
|
||||
)
|
||||
|
|
|
@ -1,23 +1,36 @@
|
|||
import os
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
DB_HOST: str = ""
|
||||
DB_HOST: str = ''
|
||||
DB_PORT: int = 5432
|
||||
POSTGRES_DB: str = ""
|
||||
POSTGRES_PASSWORD: str = ""
|
||||
POSTGRES_USER: str = ""
|
||||
POSTGRES_DB_TEST: str = ""
|
||||
POSTGRES_DB: str = ''
|
||||
POSTGRES_PASSWORD: str = ''
|
||||
POSTGRES_USER: str = ''
|
||||
POSTGRES_DB_TEST: str = ''
|
||||
REDIS_DB: str = ''
|
||||
|
||||
@property
|
||||
def DATABASE_URL_asyncpg(self):
|
||||
def DATABASE_URL_asyncpg(self) -> str:
|
||||
"""
|
||||
Возвращает строку подключения к БД необходимую для SQLAlchemy
|
||||
"""
|
||||
# Проверяем, в DOCKER или нет
|
||||
|
||||
file_path = '/usr/src/RUN_IN_DOCKER'
|
||||
if os.path.exists(file_path):
|
||||
return (
|
||||
'postgresql+asyncpg://'
|
||||
f'{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}'
|
||||
f'@db:{self.DB_PORT}/{self.POSTGRES_DB}'
|
||||
)
|
||||
|
||||
return (
|
||||
"postgresql+asyncpg://"
|
||||
f"{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}"
|
||||
f"@{self.DB_HOST}:{self.DB_PORT}/{self.POSTGRES_DB}"
|
||||
'postgresql+asyncpg://'
|
||||
f'{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}'
|
||||
f'@{self.DB_HOST}:{self.DB_PORT}/{self.POSTGRES_DB}'
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -25,13 +38,29 @@ class Settings(BaseSettings):
|
|||
"""
|
||||
Возвращает строку подключения к БД необходимую для SQLAlchemy
|
||||
"""
|
||||
file_path = '/usr/src/RUN_IN_DOCKER'
|
||||
if os.path.exists(file_path):
|
||||
return (
|
||||
'postgresql+asyncpg://'
|
||||
f'{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}'
|
||||
f'@db:{self.DB_PORT}/{self.POSTGRES_DB_TEST}'
|
||||
)
|
||||
|
||||
return (
|
||||
"postgresql+asyncpg://"
|
||||
f"{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}"
|
||||
f"@{self.DB_HOST}:{self.DB_PORT}/{self.POSTGRES_DB_TEST}"
|
||||
'postgresql+asyncpg://'
|
||||
f'{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}'
|
||||
f'@{self.DB_HOST}:{self.DB_PORT}/{self.POSTGRES_DB_TEST}'
|
||||
)
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env")
|
||||
@property
|
||||
def REDIS_URL(self):
|
||||
file_path = '/usr/src/RUN_IN_DOCKER'
|
||||
if os.path.exists(file_path):
|
||||
return 'redis://redis:6379/0'
|
||||
|
||||
return self.REDIS_DB
|
||||
|
||||
model_config = SettingsConfigDict(env_file='.env')
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
from uuid import UUID
|
||||
|
||||
from sqlalchemy import delete, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from fastfood import models, schemas
|
||||
|
||||
|
||||
class DishCrud:
|
||||
@staticmethod
|
||||
async def get_dishes(submenu_id: UUID, session: AsyncSession):
|
||||
async with session:
|
||||
query = select(models.Dish).where(models.Dish.parent_submenu == submenu_id)
|
||||
dishes = await session.execute(query)
|
||||
return dishes.scalars().all()
|
||||
|
||||
@staticmethod
|
||||
async def create_dish_item(
|
||||
submenu_id: UUID,
|
||||
dish: schemas.DishBase,
|
||||
session: AsyncSession,
|
||||
):
|
||||
async with session:
|
||||
new_dish = models.Dish(**dish.model_dump())
|
||||
new_dish.parent_submenu = submenu_id
|
||||
session.add(new_dish)
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
return new_dish
|
||||
|
||||
@staticmethod
|
||||
async def get_dish_item(
|
||||
dish_id: UUID,
|
||||
session: AsyncSession,
|
||||
):
|
||||
async with session:
|
||||
query = select(models.Dish).where(models.Dish.id == dish_id)
|
||||
submenu = await session.execute(query)
|
||||
return submenu.scalars().one_or_none()
|
||||
|
||||
@staticmethod
|
||||
async def update_dish_item(
|
||||
dish_id: UUID,
|
||||
dish: schemas.DishBase,
|
||||
session: AsyncSession,
|
||||
):
|
||||
async with session:
|
||||
query = (
|
||||
update(models.Dish)
|
||||
.where(models.Dish.id == dish_id)
|
||||
.values(**dish.model_dump())
|
||||
)
|
||||
await session.execute(query)
|
||||
await session.commit()
|
||||
qr = select(models.Dish).where(models.Dish.id == dish_id)
|
||||
updated_submenu = await session.execute(qr)
|
||||
return updated_submenu.scalars().one()
|
||||
|
||||
@staticmethod
|
||||
async def delete_dish_item(dish_id: UUID, session: AsyncSession):
|
||||
async with session:
|
||||
query = delete(models.Dish).where(models.Dish.id == dish_id)
|
||||
await session.execute(query)
|
||||
await session.commit()
|
|
@ -1,74 +0,0 @@
|
|||
from uuid import UUID
|
||||
|
||||
from sqlalchemy import delete, distinct, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
from fastfood import models, schemas
|
||||
|
||||
|
||||
class MenuCrud:
|
||||
@staticmethod
|
||||
async def get_menus(session: AsyncSession):
|
||||
async with session:
|
||||
query = select(models.Menu)
|
||||
menus = await session.execute(query)
|
||||
return menus
|
||||
|
||||
@staticmethod
|
||||
async def create_menu_item(menu: schemas.MenuBase, session: AsyncSession):
|
||||
async with session:
|
||||
new_menu = models.Menu(**menu.model_dump())
|
||||
session.add(new_menu)
|
||||
await session.commit()
|
||||
await session.refresh(new_menu)
|
||||
return new_menu
|
||||
|
||||
@staticmethod
|
||||
async def get_menu_item(menu_id: UUID, session: AsyncSession):
|
||||
async with session:
|
||||
m = aliased(models.Menu)
|
||||
s = aliased(models.SubMenu)
|
||||
d = aliased(models.Dish)
|
||||
|
||||
query = (
|
||||
select(
|
||||
m,
|
||||
func.count(distinct(s.id)).label("submenus_count"),
|
||||
func.count(distinct(d.id)).label("dishes_count"),
|
||||
)
|
||||
.join(s, s.parent_menu == m.id, isouter=True)
|
||||
.join(d, d.parent_submenu == s.id, isouter=True)
|
||||
.group_by(m.id)
|
||||
.where(m.id == menu_id)
|
||||
)
|
||||
menu = await session.execute(query)
|
||||
menu = menu.scalars().one_or_none()
|
||||
if menu is None:
|
||||
return None
|
||||
return menu
|
||||
|
||||
@staticmethod
|
||||
async def update_menu_item(
|
||||
menu_id: UUID,
|
||||
menu: schemas.MenuBase,
|
||||
session: AsyncSession,
|
||||
):
|
||||
async with session:
|
||||
query = (
|
||||
update(models.Menu)
|
||||
.where(models.Menu.id == menu_id)
|
||||
.values(**menu.model_dump())
|
||||
)
|
||||
await session.execute(query)
|
||||
await session.commit()
|
||||
qr = select(models.Menu).where(models.Menu.id == menu_id)
|
||||
updated_menu = await session.execute(qr)
|
||||
return updated_menu
|
||||
|
||||
@staticmethod
|
||||
async def delete_menu_item(menu_id: UUID, session: AsyncSession):
|
||||
async with session:
|
||||
query = delete(models.Menu).where(models.Menu.id == menu_id)
|
||||
await session.execute(query)
|
||||
await session.commit()
|
|
@ -1,80 +0,0 @@
|
|||
from uuid import UUID
|
||||
|
||||
from sqlalchemy import delete, distinct, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
from fastfood import models, schemas
|
||||
|
||||
|
||||
class SubMenuCrud:
|
||||
@staticmethod
|
||||
async def get_submenus(menu_id: UUID, session: AsyncSession):
|
||||
async with session:
|
||||
query = select(models.SubMenu).where(
|
||||
models.SubMenu.parent_menu == menu_id,
|
||||
)
|
||||
submenus = await session.execute(query)
|
||||
return submenus
|
||||
|
||||
@staticmethod
|
||||
async def create_submenu_item(
|
||||
menu_id: UUID,
|
||||
submenu: schemas.MenuBase,
|
||||
session: AsyncSession,
|
||||
):
|
||||
async with session:
|
||||
new_submenu = models.SubMenu(**submenu.model_dump())
|
||||
new_submenu.parent_menu = menu_id
|
||||
session.add(new_submenu)
|
||||
await session.commit()
|
||||
await session.refresh(new_submenu)
|
||||
return new_submenu
|
||||
|
||||
@staticmethod
|
||||
async def get_submenu_item(
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
session: AsyncSession,
|
||||
):
|
||||
async with session:
|
||||
s = aliased(models.SubMenu)
|
||||
d = aliased(models.Dish)
|
||||
query = (
|
||||
select(s, func.count(distinct(d.id)))
|
||||
.join(d, s.id == d.parent_submenu, isouter=True)
|
||||
.group_by(s.id)
|
||||
.where(s.id == submenu_id)
|
||||
)
|
||||
submenu = await session.execute(query)
|
||||
submenu = submenu.scalars().one_or_none()
|
||||
if submenu is None:
|
||||
return None
|
||||
return submenu
|
||||
|
||||
@staticmethod
|
||||
async def update_submenu_item(
|
||||
submenu_id: UUID,
|
||||
submenu: schemas.MenuBase,
|
||||
session: AsyncSession,
|
||||
):
|
||||
async with session:
|
||||
query = (
|
||||
update(models.SubMenu)
|
||||
.where(models.SubMenu.id == submenu_id)
|
||||
.values(**submenu.model_dump())
|
||||
)
|
||||
await session.execute(query)
|
||||
await session.commit()
|
||||
qr = select(models.SubMenu).where(models.SubMenu.id == submenu_id)
|
||||
updated_submenu = await session.execute(qr)
|
||||
return updated_submenu
|
||||
|
||||
@staticmethod
|
||||
async def delete_submenu_item(submenu_id: UUID, session: AsyncSession):
|
||||
async with session:
|
||||
query = delete(models.SubMenu).where(
|
||||
models.SubMenu.id == submenu_id,
|
||||
)
|
||||
await session.execute(query)
|
||||
await session.commit()
|
|
@ -1,7 +1,8 @@
|
|||
from typing import AsyncGenerator
|
||||
|
||||
from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker,
|
||||
create_async_engine)
|
||||
import redis.asyncio as redis # type: ignore
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
|
||||
from fastfood.config import settings
|
||||
|
||||
|
@ -16,3 +17,13 @@ async_session_maker = async_sessionmaker(
|
|||
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
async with async_session_maker() as session:
|
||||
yield session
|
||||
|
||||
|
||||
def get_redis_pool():
|
||||
return redis.from_url(settings.REDIS_URL, decode_responses=False)
|
||||
|
||||
|
||||
async def get_async_redis_client(
|
||||
redis_pool: redis.Redis = Depends(get_redis_pool),
|
||||
):
|
||||
return redis_pool
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import uuid
|
||||
from copy import deepcopy
|
||||
from typing import Annotated, List, Optional
|
||||
from typing import Annotated
|
||||
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
|
@ -21,13 +21,13 @@ str_25 = Annotated[str, 25]
|
|||
class Base(DeclarativeBase):
|
||||
id: Mapped[uuidpk]
|
||||
title: Mapped[str_25]
|
||||
description: Mapped[Optional[str]]
|
||||
description: Mapped[str | None]
|
||||
|
||||
def __eq__(self, other):
|
||||
classes_match = isinstance(other, self.__class__)
|
||||
a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
|
||||
a.pop("_sa_instance_state", None)
|
||||
b.pop("_sa_instance_state", None)
|
||||
a.pop('_sa_instance_state', None)
|
||||
b.pop('_sa_instance_state', None)
|
||||
attrs_match = a == b
|
||||
return classes_match and attrs_match
|
||||
|
||||
|
@ -36,13 +36,13 @@ class Base(DeclarativeBase):
|
|||
|
||||
|
||||
class Menu(Base):
|
||||
__tablename__ = "menu"
|
||||
__tablename__ = 'menu'
|
||||
|
||||
submenus: Mapped[List["SubMenu"]] = relationship(
|
||||
"SubMenu",
|
||||
backref="menu",
|
||||
lazy="selectin",
|
||||
cascade="all, delete",
|
||||
submenus: Mapped[list['SubMenu']] = relationship(
|
||||
'SubMenu',
|
||||
backref='menu',
|
||||
lazy='selectin',
|
||||
cascade='all, delete',
|
||||
)
|
||||
|
||||
@hybridproperty
|
||||
|
@ -58,16 +58,16 @@ class Menu(Base):
|
|||
|
||||
|
||||
class SubMenu(Base):
|
||||
__tablename__ = "submenu"
|
||||
__tablename__ = 'submenu'
|
||||
|
||||
parent_menu: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("menu.id", ondelete="CASCADE")
|
||||
ForeignKey('menu.id', ondelete='CASCADE')
|
||||
)
|
||||
dishes: Mapped[List["Dish"]] = relationship(
|
||||
"Dish",
|
||||
backref="submenu",
|
||||
lazy="selectin",
|
||||
cascade="all, delete",
|
||||
dishes: Mapped[list['Dish']] = relationship(
|
||||
'Dish',
|
||||
backref='submenu',
|
||||
lazy='selectin',
|
||||
cascade='all, delete',
|
||||
)
|
||||
|
||||
@hybridproperty
|
||||
|
@ -76,9 +76,9 @@ class SubMenu(Base):
|
|||
|
||||
|
||||
class Dish(Base):
|
||||
__tablename__ = "dish"
|
||||
__tablename__ = 'dish'
|
||||
|
||||
price: Mapped[float]
|
||||
parent_submenu: Mapped[uuid.UUID] = mapped_column(
|
||||
ForeignKey("submenu.id", ondelete="CASCADE")
|
||||
ForeignKey('submenu.id', ondelete='CASCADE')
|
||||
)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from fastfood import models
|
||||
from fastfood.dbase import async_engine
|
||||
|
||||
from .dish import DishCrud
|
||||
from .menu import MenuCrud
|
||||
from .submenu import SubMenuCrud
|
||||
from .dish import DishRepository
|
||||
from .menu import MenuRepository
|
||||
from .submenu import SubMenuRepository
|
||||
|
||||
|
||||
async def create_db_and_tables():
|
||||
|
@ -12,8 +12,8 @@ async def create_db_and_tables():
|
|||
await conn.run_sync(models.Base.metadata.create_all)
|
||||
|
||||
|
||||
class Crud(MenuCrud, SubMenuCrud, DishCrud):
|
||||
class Repository(MenuRepository, SubMenuRepository, DishRepository):
|
||||
pass
|
||||
|
||||
|
||||
crud = Crud()
|
||||
ropo = Repository()
|
|
@ -0,0 +1,69 @@
|
|||
from uuid import UUID
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy import delete, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from fastfood.dbase import get_async_session
|
||||
from fastfood.models import Dish
|
||||
from fastfood.schemas import Dish_db
|
||||
|
||||
|
||||
class DishRepository:
|
||||
def __init__(self, session: AsyncSession = Depends(get_async_session)):
|
||||
self.db = session
|
||||
|
||||
async def get_dishes(self, menu_id: UUID, submenu_id: UUID) -> list[Dish]:
|
||||
query = select(Dish).where(
|
||||
Dish.parent_submenu == submenu_id,
|
||||
)
|
||||
dishes = await self.db.execute(query)
|
||||
return [x for x in dishes.scalars().all()]
|
||||
|
||||
async def create_dish_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_data: Dish_db,
|
||||
) -> Dish:
|
||||
new_dish = Dish(**dish_data.model_dump())
|
||||
new_dish.parent_submenu = submenu_id
|
||||
self.db.add(new_dish)
|
||||
await self.db.commit()
|
||||
await self.db.refresh(new_dish)
|
||||
return new_dish
|
||||
|
||||
async def get_dish_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_id: UUID,
|
||||
) -> Dish | None:
|
||||
query = select(Dish).where(Dish.id == dish_id)
|
||||
submenu = await self.db.execute(query)
|
||||
return submenu.scalars().one_or_none()
|
||||
|
||||
async def update_dish_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_id: UUID,
|
||||
dish_data: Dish_db,
|
||||
) -> Dish:
|
||||
query = update(Dish).where(Dish.id == dish_id).values(**dish_data.model_dump())
|
||||
await self.db.execute(query)
|
||||
await self.db.commit()
|
||||
qr = select(Dish).where(Dish.id == dish_id)
|
||||
updated_submenu = await self.db.execute(qr)
|
||||
return updated_submenu.scalars().one()
|
||||
|
||||
async def delete_dish_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_id: UUID,
|
||||
) -> int:
|
||||
query = delete(Dish).where(Dish.id == dish_id)
|
||||
await self.db.execute(query)
|
||||
await self.db.commit()
|
||||
return 200
|
|
@ -0,0 +1,66 @@
|
|||
from uuid import UUID
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy import delete, distinct, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
from fastfood import schemas
|
||||
from fastfood.dbase import get_async_session
|
||||
from fastfood.models import Dish, Menu, SubMenu
|
||||
|
||||
|
||||
class MenuRepository:
|
||||
def __init__(self, session: AsyncSession = Depends(get_async_session)):
|
||||
self.db = session
|
||||
|
||||
async def get_menus(self) -> list[Menu]:
|
||||
query = select(Menu)
|
||||
menus = await self.db.execute(query)
|
||||
return [x for x in menus.scalars().all()]
|
||||
|
||||
async def create_menu_item(self, menu: schemas.MenuBase) -> Menu:
|
||||
new_menu = Menu(**menu.model_dump())
|
||||
self.db.add(new_menu)
|
||||
await self.db.commit()
|
||||
await self.db.refresh(new_menu)
|
||||
return new_menu
|
||||
|
||||
async def get_menu_item(self, menu_id: UUID) -> Menu | None:
|
||||
m = aliased(Menu)
|
||||
s = aliased(SubMenu)
|
||||
d = aliased(Dish)
|
||||
|
||||
query = (
|
||||
select(
|
||||
m,
|
||||
func.count(distinct(s.id)).label('submenus_count'),
|
||||
func.count(distinct(d.id)).label('dishes_count'),
|
||||
)
|
||||
.join(s, s.parent_menu == m.id, isouter=True)
|
||||
.join(d, d.parent_submenu == s.id, isouter=True)
|
||||
.group_by(m.id)
|
||||
.where(m.id == menu_id)
|
||||
)
|
||||
menu = await self.db.execute(query)
|
||||
menu = menu.scalars().one_or_none()
|
||||
if menu is None:
|
||||
return None
|
||||
return menu
|
||||
|
||||
async def update_menu_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
menu: schemas.MenuBase,
|
||||
) -> Menu:
|
||||
query = update(Menu).where(Menu.id == menu_id).values(**menu.model_dump())
|
||||
await self.db.execute(query)
|
||||
await self.db.commit()
|
||||
qr = select(Menu).where(Menu.id == menu_id)
|
||||
updated_menu = await self.db.execute(qr)
|
||||
return updated_menu.scalar_one()
|
||||
|
||||
async def delete_menu_item(self, menu_id: UUID):
|
||||
query = delete(Menu).where(Menu.id == menu_id)
|
||||
await self.db.execute(query)
|
||||
await self.db.commit()
|
|
@ -0,0 +1,65 @@
|
|||
import pickle
|
||||
from typing import Any
|
||||
|
||||
import redis.asyncio as redis # type: ignore
|
||||
from fastapi import BackgroundTasks, Depends
|
||||
|
||||
from fastfood.dbase import get_redis_pool
|
||||
|
||||
|
||||
def get_key(level: str, **kwargs) -> str:
|
||||
match level:
|
||||
case 'menus':
|
||||
return 'MENUS'
|
||||
case 'menu':
|
||||
return f"{kwargs.get('menu_id')}"
|
||||
case 'submenus':
|
||||
return f"{kwargs.get('menu_id')}:SUBMENUS"
|
||||
case 'submenu':
|
||||
return f"{kwargs.get('menu_id')}:{kwargs.get('submenu_id')}"
|
||||
case 'dishes':
|
||||
return f"{kwargs.get('menu_id')}:{kwargs.get('submenu_id')}:DISHES"
|
||||
case 'dish':
|
||||
return f"{kwargs.get('menu_id')}:{kwargs.get('submenu_id')}:{kwargs.get('dish_id')}"
|
||||
|
||||
return 'abracadabra'
|
||||
|
||||
|
||||
class RedisRepository:
|
||||
def __init__(
|
||||
self,
|
||||
pool: redis.Redis = Depends(get_redis_pool),
|
||||
) -> None:
|
||||
self.pool = pool
|
||||
self.ttl = 1800
|
||||
|
||||
async def get(self, key: str) -> Any | None:
|
||||
data = await self.pool.get(key)
|
||||
if data is not None:
|
||||
return pickle.loads(data)
|
||||
return None
|
||||
|
||||
async def set(self, key: str, value: Any, bg_task: BackgroundTasks) -> None:
|
||||
data = pickle.dumps(value)
|
||||
bg_task.add_task(self._set_cache, key, data)
|
||||
|
||||
async def _set_cache(self, key: str, data: Any) -> None:
|
||||
await self.pool.setex(key, self.ttl, data)
|
||||
|
||||
async def delete(self, key: str, bg_task: BackgroundTasks) -> None:
|
||||
bg_task.add_task(self._delete_cache, key)
|
||||
|
||||
async def _delete_cache(self, key: str) -> None:
|
||||
await self.pool.delete(key)
|
||||
|
||||
async def clear_cache(self, pattern: str, bg_task: BackgroundTasks) -> None:
|
||||
keys = [key async for key in self.pool.scan_iter(pattern)]
|
||||
if keys:
|
||||
bg_task.add_task(self._clear_keys, keys)
|
||||
|
||||
async def _clear_keys(self, keys: list[str]) -> None:
|
||||
await self.pool.delete(*keys)
|
||||
|
||||
async def invalidate(self, key: str, bg_task: BackgroundTasks) -> None:
|
||||
await self.clear_cache(f'{key}*', bg_task)
|
||||
await self.clear_cache(f'{get_key("menus")}*', bg_task)
|
|
@ -0,0 +1,82 @@
|
|||
from uuid import UUID
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy import delete, distinct, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
from fastfood.dbase import get_async_session
|
||||
from fastfood.models import Dish, SubMenu
|
||||
from fastfood.schemas import MenuBase
|
||||
|
||||
|
||||
class SubMenuRepository:
|
||||
def __init__(self, session: AsyncSession = Depends(get_async_session)):
|
||||
self.db = session
|
||||
|
||||
async def get_submenus(self, menu_id: UUID) -> list[SubMenu]:
|
||||
query = select(SubMenu).where(
|
||||
SubMenu.parent_menu == menu_id,
|
||||
)
|
||||
submenus = await self.db.execute(query)
|
||||
return [x for x in submenus.scalars().all()]
|
||||
|
||||
async def create_submenu_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu: MenuBase,
|
||||
) -> SubMenu:
|
||||
new_submenu = SubMenu(**submenu.model_dump())
|
||||
new_submenu.parent_menu = menu_id
|
||||
self.db.add(new_submenu)
|
||||
await self.db.commit()
|
||||
await self.db.refresh(new_submenu)
|
||||
|
||||
full_sub = await self.get_submenu_item(menu_id, new_submenu.id)
|
||||
if full_sub is None:
|
||||
raise TypeError
|
||||
return full_sub
|
||||
|
||||
async def get_submenu_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
) -> SubMenu | None:
|
||||
s = aliased(SubMenu)
|
||||
d = aliased(Dish)
|
||||
query = (
|
||||
select(s, func.count(distinct(d.id)).label('dishes_count'))
|
||||
.join(d, s.id == d.parent_submenu, isouter=True)
|
||||
.group_by(s.id)
|
||||
.where(s.id == submenu_id)
|
||||
)
|
||||
submenu = await self.db.execute(query)
|
||||
submenu = submenu.scalars().one_or_none()
|
||||
if submenu is None:
|
||||
return None
|
||||
return submenu
|
||||
|
||||
async def update_submenu_item(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
submenu_data: MenuBase,
|
||||
) -> SubMenu:
|
||||
query = (
|
||||
update(SubMenu)
|
||||
.where(SubMenu.id == submenu_id)
|
||||
.values(**submenu_data.model_dump())
|
||||
)
|
||||
await self.db.execute(query)
|
||||
await self.db.commit()
|
||||
qr = select(SubMenu).where(SubMenu.id == submenu_id)
|
||||
updated_submenu = await self.db.execute(qr)
|
||||
return updated_submenu.scalar_one()
|
||||
|
||||
async def delete_submenu_item(self, menu_id: UUID, submenu_id: UUID) -> int:
|
||||
query = delete(SubMenu).where(
|
||||
SubMenu.id == submenu_id,
|
||||
)
|
||||
await self.db.execute(query)
|
||||
await self.db.commit()
|
||||
return 200
|
|
@ -1,80 +1,84 @@
|
|||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
|
||||
|
||||
from fastfood import schemas
|
||||
from fastfood.cruds import crud
|
||||
from fastfood.dbase import get_async_session
|
||||
from fastfood.utils import price_converter
|
||||
from fastfood.schemas import Dish, DishBase
|
||||
from fastfood.service.dish import DishService
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/v1/menus/{menu_id}/submenus/{submenu_id}/dishes",
|
||||
tags=["dish"],
|
||||
prefix='/api/v1/menus/{menu_id}/submenus/{submenu_id}/dishes',
|
||||
tags=['dish'],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/")
|
||||
@router.get('/', response_model=list[Dish])
|
||||
async def get_dishes(
|
||||
menu_id: UUID, submenu_id: UUID, session: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
result = await crud.get_dishes(submenu_id=submenu_id, session=session)
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish: DishService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
) -> list[Dish]:
|
||||
result = await dish.read_dishes(menu_id, submenu_id)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/", status_code=201)
|
||||
@router.post('/', status_code=201, response_model=Dish)
|
||||
async def create_dish(
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish: schemas.DishBase,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
dish_data: DishBase,
|
||||
dish: DishService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.create_dish_item(
|
||||
submenu_id=submenu_id,
|
||||
dish=dish,
|
||||
session=session,
|
||||
return await dish.create_dish(
|
||||
menu_id,
|
||||
submenu_id,
|
||||
dish_data,
|
||||
)
|
||||
return price_converter(result)
|
||||
|
||||
|
||||
@router.get("/{dish_id}")
|
||||
@router.get('/{dish_id}', response_model=Dish)
|
||||
async def get_dish(
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_id: UUID,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
dish: DishService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.get_dish_item(
|
||||
dish_id=dish_id,
|
||||
session=session,
|
||||
result = await dish.read_dish(
|
||||
menu_id,
|
||||
submenu_id,
|
||||
dish_id,
|
||||
)
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail="dish not found")
|
||||
return price_converter(result)
|
||||
raise HTTPException(status_code=404, detail='dish not found')
|
||||
return result
|
||||
|
||||
|
||||
@router.patch("/{dish_id}")
|
||||
@router.patch('/{dish_id}', response_model=Dish)
|
||||
async def update_dish(
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_id: UUID,
|
||||
dish: schemas.DishBase,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
dish_data: DishBase,
|
||||
dish: DishService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.update_dish_item(
|
||||
dish_id=dish_id,
|
||||
dish=dish,
|
||||
session=session,
|
||||
result = await dish.update_dish(
|
||||
menu_id,
|
||||
submenu_id,
|
||||
dish_id,
|
||||
dish_data,
|
||||
)
|
||||
return price_converter(result)
|
||||
return result
|
||||
|
||||
|
||||
@router.delete("/{dish_id}")
|
||||
@router.delete('/{dish_id}')
|
||||
async def delete_dish(
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_id: UUID,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
dish: DishService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
await crud.delete_dish_item(dish_id=dish_id, session=session)
|
||||
await dish.del_dish(menu_id, submenu_id, dish_id)
|
||||
|
|
|
@ -1,66 +1,64 @@
|
|||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
|
||||
|
||||
from fastfood import schemas
|
||||
from fastfood.cruds import crud
|
||||
from fastfood.dbase import get_async_session
|
||||
from fastfood.schemas import MenuBase, MenuRead
|
||||
from fastfood.service.menu import MenuService
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/v1/menus",
|
||||
tags=["menu"],
|
||||
prefix='/api/v1/menus',
|
||||
tags=['menu'],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/", response_model=Optional[List[schemas.Menu]])
|
||||
async def get_menus(session: AsyncSession = Depends(get_async_session)):
|
||||
result = await crud.get_menus(session=session)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
@router.post("/", status_code=201, response_model=schemas.Menu)
|
||||
async def add_menu(
|
||||
menu: schemas.MenuBase,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
@router.get('/', response_model=list[MenuRead])
|
||||
async def get_menus(
|
||||
menu: MenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.create_menu_item(
|
||||
menu=menu,
|
||||
session=session,
|
||||
)
|
||||
return result
|
||||
return await menu.read_menus()
|
||||
|
||||
|
||||
@router.get("/{menu_id}", response_model=schemas.MenuRead)
|
||||
@router.post('/', status_code=201, response_model=MenuRead)
|
||||
async def add_menu(
|
||||
menu: MenuBase,
|
||||
responce: MenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
return await responce.create_menu(menu)
|
||||
|
||||
|
||||
@router.get('/{menu_id}', response_model=MenuRead)
|
||||
async def get_menu(
|
||||
menu_id: UUID,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
responce: MenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.get_menu_item(menu_id=menu_id, session=session)
|
||||
result = await responce.read_menu(menu_id=menu_id)
|
||||
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail="menu not found")
|
||||
raise HTTPException(status_code=404, detail='menu not found')
|
||||
return result
|
||||
|
||||
|
||||
@router.patch("/{menu_id}", response_model=schemas.MenuBase)
|
||||
@router.patch('/{menu_id}', response_model=MenuRead)
|
||||
async def update_menu(
|
||||
menu_id: UUID,
|
||||
menu: schemas.MenuBase,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
menu: MenuBase,
|
||||
responce: MenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.update_menu_item(
|
||||
result = await responce.update_menu(
|
||||
menu_id=menu_id,
|
||||
menu=menu,
|
||||
session=session,
|
||||
menu_data=menu,
|
||||
)
|
||||
return result.scalars().one()
|
||||
return result
|
||||
|
||||
|
||||
@router.delete("/{menu_id}")
|
||||
@router.delete('/{menu_id}')
|
||||
async def delete_menu(
|
||||
menu_id: UUID,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
menu: MenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
await crud.delete_menu_item(menu_id=menu_id, session=session)
|
||||
await menu.del_menu(menu_id)
|
||||
|
|
|
@ -1,76 +1,80 @@
|
|||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
|
||||
|
||||
from fastfood import schemas
|
||||
from fastfood.cruds import crud
|
||||
from fastfood.dbase import get_async_session
|
||||
from fastfood.schemas import MenuBase, SubMenuRead
|
||||
from fastfood.service.submenu import SubmenuService
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/v1/menus/{menu_id}/submenus",
|
||||
tags=["submenu"],
|
||||
prefix='/api/v1/menus/{menu_id}/submenus',
|
||||
tags=['submenu'],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/")
|
||||
@router.get('/', response_model=list[SubMenuRead])
|
||||
async def get_submenus(
|
||||
menu_id: UUID, session: AsyncSession = Depends(get_async_session)
|
||||
menu_id: UUID,
|
||||
submenu: SubmenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.get_submenus(menu_id=menu_id, session=session)
|
||||
return result.scalars().all()
|
||||
result = await submenu.read_submenus(menu_id=menu_id)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/", status_code=201)
|
||||
@router.post('/', status_code=201, response_model=SubMenuRead)
|
||||
async def create_submenu_item(
|
||||
menu_id: UUID,
|
||||
submenu: schemas.MenuBase,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
submenu_data: MenuBase,
|
||||
submenu: SubmenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.create_submenu_item(
|
||||
result = await submenu.create_submenu(
|
||||
menu_id=menu_id,
|
||||
submenu=submenu,
|
||||
session=session,
|
||||
submenu_data=submenu_data,
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/{submenu_id}", response_model=schemas.SubMenuRead)
|
||||
@router.get('/{submenu_id}', response_model=SubMenuRead)
|
||||
async def get_submenu(
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
submenu: SubmenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.get_submenu_item(
|
||||
result = await submenu.read_menu(
|
||||
menu_id=menu_id,
|
||||
submenu_id=submenu_id,
|
||||
session=session,
|
||||
)
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail="submenu not found")
|
||||
raise HTTPException(status_code=404, detail='submenu not found')
|
||||
return result
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/{submenu_id}",
|
||||
response_model=schemas.MenuBase,
|
||||
'/{submenu_id}',
|
||||
response_model=SubMenuRead,
|
||||
)
|
||||
async def update_submenu(
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
submenu: schemas.MenuBase,
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
submenu_data: MenuBase,
|
||||
submenu: SubmenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
result = await crud.update_submenu_item(
|
||||
result = await submenu.update_submenu(
|
||||
menu_id=menu_id,
|
||||
submenu_id=submenu_id,
|
||||
submenu=submenu,
|
||||
session=session,
|
||||
submenu_data=submenu_data,
|
||||
)
|
||||
return result.scalars().one()
|
||||
return result
|
||||
|
||||
|
||||
@router.delete("/{submenu_id}")
|
||||
@router.delete('/{submenu_id}')
|
||||
async def delete_submenu(
|
||||
menu_id: UUID, submenu_id: UUID, session: AsyncSession = Depends(get_async_session)
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
submenu: SubmenuService = Depends(),
|
||||
background_tasks: BackgroundTasks = BackgroundTasks(),
|
||||
):
|
||||
await crud.delete_submenu_item(submenu_id=submenu_id, session=session)
|
||||
await submenu.del_menu(menu_id=menu_id, submenu_id=submenu_id)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
@ -6,7 +5,7 @@ from pydantic import BaseModel
|
|||
|
||||
class MenuBase(BaseModel):
|
||||
title: str
|
||||
description: Optional[str]
|
||||
description: str | None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
@ -26,8 +25,12 @@ class SubMenuRead(Menu):
|
|||
|
||||
|
||||
class DishBase(MenuBase):
|
||||
price: float
|
||||
price: str
|
||||
|
||||
|
||||
class Dish(DishBase, Menu):
|
||||
pass
|
||||
|
||||
|
||||
class Dish_db(MenuBase):
|
||||
price: float
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
from uuid import UUID
|
||||
|
||||
import redis.asyncio as redis # type: ignore
|
||||
from fastapi import BackgroundTasks, Depends
|
||||
|
||||
from fastfood.dbase import get_async_redis_client
|
||||
from fastfood.repository.dish import DishRepository
|
||||
from fastfood.repository.redis import RedisRepository, get_key
|
||||
from fastfood.schemas import Dish, Dish_db, DishBase
|
||||
|
||||
|
||||
class DishService:
|
||||
def __init__(
|
||||
self,
|
||||
dish_repo: DishRepository = Depends(),
|
||||
redis_client: redis.Redis = Depends(get_async_redis_client),
|
||||
background_tasks: BackgroundTasks = None,
|
||||
) -> None:
|
||||
self.dish_repo = dish_repo
|
||||
self.cache = RedisRepository(redis_client)
|
||||
self.bg_tasks = background_tasks
|
||||
self.key = get_key
|
||||
|
||||
async def read_dishes(self, menu_id: UUID, submenu_id: UUID) -> list[Dish]:
|
||||
cached_dishes = await self.cache.get(
|
||||
self.key('dishes', menu_id=str(menu_id), submenu_id=str(submenu_id))
|
||||
)
|
||||
if cached_dishes is not None:
|
||||
return cached_dishes
|
||||
|
||||
data = await self.dish_repo.get_dishes(menu_id, submenu_id)
|
||||
response = []
|
||||
for row in data:
|
||||
dish = row.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
response.append(Dish(**dish))
|
||||
await self.cache.set(
|
||||
self.key(
|
||||
'dishes',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
),
|
||||
response,
|
||||
self.bg_tasks,
|
||||
)
|
||||
return response
|
||||
|
||||
async def create_dish(
|
||||
self,
|
||||
menu_id: UUID,
|
||||
submenu_id: UUID,
|
||||
dish_data: DishBase,
|
||||
) -> Dish:
|
||||
dish_db = Dish_db(**dish_data.model_dump())
|
||||
data = await self.dish_repo.create_dish_item(
|
||||
menu_id,
|
||||
submenu_id,
|
||||
dish_db,
|
||||
)
|
||||
dish = data.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
dish = Dish(**dish)
|
||||
await self.cache.set(
|
||||
self.key('dish', menu_id=str(menu_id), submenu_id=str(submenu_id)),
|
||||
dish,
|
||||
self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return dish
|
||||
|
||||
async def read_dish(
|
||||
self, menu_id: UUID, submenu_id: UUID, dish_id: UUID
|
||||
) -> Dish | None:
|
||||
cached_dish = await self.cache.get(
|
||||
self.key(
|
||||
'dish',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
dish_id=str(dish_id),
|
||||
)
|
||||
)
|
||||
if cached_dish is not None:
|
||||
return cached_dish
|
||||
|
||||
data = await self.dish_repo.get_dish_item(menu_id, submenu_id, dish_id)
|
||||
if data is None:
|
||||
return None
|
||||
dish = data.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
dish = Dish(**dish)
|
||||
await self.cache.set(
|
||||
self.key(
|
||||
'dish',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
dish_id=str(dish_id),
|
||||
),
|
||||
dish,
|
||||
self.bg_tasks,
|
||||
)
|
||||
return dish
|
||||
|
||||
async def update_dish(
|
||||
self, menu_id: UUID, submenu_id: UUID, dish_id, dish_data: DishBase
|
||||
) -> Dish:
|
||||
dish_db = Dish_db(**dish_data.model_dump())
|
||||
data = await self.dish_repo.update_dish_item(
|
||||
menu_id, submenu_id, dish_id, dish_db
|
||||
)
|
||||
dish = data.__dict__
|
||||
dish['price'] = str(dish['price'])
|
||||
dish = Dish(**dish)
|
||||
await self.cache.set(
|
||||
self.key(
|
||||
'dish',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
dish_id=str(dish_id),
|
||||
),
|
||||
dish,
|
||||
self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return dish
|
||||
|
||||
async def del_dish(self, menu_id: UUID, submenu_id: UUID, dish_id: UUID) -> int:
|
||||
response = await self.dish_repo.delete_dish_item(
|
||||
menu_id,
|
||||
submenu_id,
|
||||
dish_id,
|
||||
)
|
||||
await self.cache.delete(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return response
|
|
@ -0,0 +1,111 @@
|
|||
from uuid import UUID
|
||||
|
||||
import redis.asyncio as redis # type: ignore
|
||||
from fastapi import BackgroundTasks, Depends
|
||||
|
||||
from fastfood.dbase import get_async_redis_client
|
||||
from fastfood.repository.menu import MenuRepository
|
||||
from fastfood.repository.redis import RedisRepository, get_key
|
||||
from fastfood.schemas import MenuBase, MenuRead
|
||||
|
||||
|
||||
class MenuService:
|
||||
def __init__(
|
||||
self,
|
||||
menu_repo: MenuRepository = Depends(),
|
||||
redis_client: redis.Redis = Depends(get_async_redis_client),
|
||||
background_tasks: BackgroundTasks = None,
|
||||
) -> None:
|
||||
self.menu_repo = menu_repo
|
||||
self.cache = RedisRepository(redis_client)
|
||||
self.key = get_key
|
||||
self.bg_tasks = background_tasks
|
||||
|
||||
async def read_menus(self) -> list[MenuRead]:
|
||||
cached_menus = await self.cache.get(self.key('menus'))
|
||||
if cached_menus is not None:
|
||||
return cached_menus
|
||||
|
||||
data = await self.menu_repo.get_menus()
|
||||
menus = []
|
||||
for r in data:
|
||||
menu = r.__dict__
|
||||
menu = {k: v for k, v in menu.items() if not k.startswith('_')}
|
||||
dishes_conter = 0
|
||||
for sub in r.submenus:
|
||||
dishes_conter += len(sub.dishes)
|
||||
|
||||
menu['submenus_count'] = len(menu.pop('submenus'))
|
||||
menu['dishes_count'] = dishes_conter
|
||||
menu = MenuRead(**menu)
|
||||
menus.append(menu)
|
||||
|
||||
await self.cache.set(self.key('menus'), menus, self.bg_tasks)
|
||||
return menus
|
||||
|
||||
async def create_menu(self, menu_data: MenuBase) -> MenuRead:
|
||||
data = await self.menu_repo.create_menu_item(menu_data)
|
||||
menu = data.__dict__
|
||||
menu = {k: v for k, v in menu.items() if not k.startswith('_')}
|
||||
dishes_conter = 0
|
||||
|
||||
for sub in data.submenus:
|
||||
dishes_conter += len(sub.dishes)
|
||||
menu['submenus_count'] = len(menu.pop('submenus'))
|
||||
menu['dishes_count'] = dishes_conter
|
||||
await self.cache.set(
|
||||
key=get_key('menu', menu_id=str(menu.get('id'))),
|
||||
value=menu,
|
||||
bg_task=self.bg_tasks,
|
||||
)
|
||||
menu = MenuRead(**menu)
|
||||
await self.cache.set(
|
||||
self.key('menu', menu_id=str(menu.id)), menu, self.bg_tasks
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu.id), bg_task=self.bg_tasks)
|
||||
return menu
|
||||
|
||||
async def read_menu(self, menu_id: UUID) -> MenuRead | None:
|
||||
cached_menu = await self.cache.get(self.key('menu', menu_id=str(menu_id)))
|
||||
if cached_menu is not None:
|
||||
return cached_menu
|
||||
|
||||
data = await self.menu_repo.get_menu_item(menu_id)
|
||||
if data is None:
|
||||
return None
|
||||
menu = data.__dict__
|
||||
menu = {k: v for k, v in menu.items() if not k.startswith('_')}
|
||||
dishes_conter = 0
|
||||
|
||||
for sub in data.submenus:
|
||||
dishes_conter += len(sub.dishes)
|
||||
menu['submenus_count'] = len(menu.pop('submenus'))
|
||||
menu['dishes_count'] = dishes_conter
|
||||
menu = MenuRead(**menu)
|
||||
await self.cache.set(
|
||||
self.key('menu', menu_id=str(menu.id)), menu, self.bg_tasks
|
||||
)
|
||||
return menu
|
||||
|
||||
async def update_menu(self, menu_id: UUID, menu_data) -> MenuRead:
|
||||
data = await self.menu_repo.update_menu_item(menu_id, menu_data)
|
||||
menu = data.__dict__
|
||||
menu = {k: v for k, v in menu.items() if not k.startswith('_')}
|
||||
dishes_conter = 0
|
||||
|
||||
for sub in data.submenus:
|
||||
dishes_conter += len(sub.dishes)
|
||||
menu['submenus_count'] = len(menu.pop('submenus'))
|
||||
menu['dishes_count'] = dishes_conter
|
||||
menu = MenuRead(**menu)
|
||||
await self.cache.set(
|
||||
self.key('menu', menu_id=str(menu.id)), menu, self.bg_tasks
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
return menu
|
||||
|
||||
async def del_menu(self, menu_id: UUID):
|
||||
data = await self.menu_repo.delete_menu_item(menu_id)
|
||||
await self.cache.delete(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
return data
|
|
@ -0,0 +1,120 @@
|
|||
from uuid import UUID
|
||||
|
||||
import redis.asyncio as redis # type: ignore
|
||||
from fastapi import BackgroundTasks, Depends
|
||||
|
||||
from fastfood.dbase import get_async_redis_client
|
||||
from fastfood.repository.redis import RedisRepository, get_key
|
||||
from fastfood.repository.submenu import SubMenuRepository
|
||||
from fastfood.schemas import MenuBase, SubMenuRead
|
||||
|
||||
|
||||
class SubmenuService:
|
||||
def __init__(
|
||||
self,
|
||||
submenu_repo: SubMenuRepository = Depends(),
|
||||
redis_client: redis.Redis = Depends(get_async_redis_client),
|
||||
background_tasks: BackgroundTasks = None,
|
||||
) -> None:
|
||||
|
||||
self.submenu_repo = submenu_repo
|
||||
self.cache = RedisRepository(redis_client)
|
||||
self.bg_tasks = background_tasks
|
||||
self.key = get_key
|
||||
|
||||
async def read_submenus(self, menu_id: UUID) -> list[SubMenuRead]:
|
||||
cached_submenus = await self.cache.get(
|
||||
self.key('submenus', menu_id=str(menu_id))
|
||||
)
|
||||
if cached_submenus is not None:
|
||||
return cached_submenus
|
||||
|
||||
data = await self.submenu_repo.get_submenus(menu_id=menu_id)
|
||||
submenus = []
|
||||
for r in data:
|
||||
submenu = r.__dict__
|
||||
subq = await self.submenu_repo.get_submenu_item(menu_id, r.id)
|
||||
if subq is not None:
|
||||
submenu['dishes_count'] = len(subq.dishes)
|
||||
submenu = SubMenuRead(**submenu)
|
||||
submenus.append(submenu)
|
||||
|
||||
await self.cache.set(
|
||||
self.key('submenus', menu_id=str(menu_id)), submenus, self.bg_tasks
|
||||
)
|
||||
return submenus
|
||||
|
||||
async def create_submenu(
|
||||
self, menu_id: UUID, submenu_data: MenuBase
|
||||
) -> SubMenuRead:
|
||||
data = await self.submenu_repo.create_submenu_item(
|
||||
menu_id,
|
||||
submenu_data,
|
||||
)
|
||||
submenu = data.__dict__
|
||||
submenu = {k: v for k, v in submenu.items() if not k.startswith('_')}
|
||||
submenu['dishes_count'] = len(submenu.pop('dishes'))
|
||||
submenu = SubMenuRead(**submenu)
|
||||
await self.cache.set(
|
||||
self.key('submenu', menu_id=str(menu_id)), submenu, self.bg_tasks
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return submenu
|
||||
|
||||
async def read_menu(self, menu_id: UUID, submenu_id: UUID) -> SubMenuRead | None:
|
||||
cached_submenu = await self.cache.get(
|
||||
self.key(
|
||||
'submenu',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
)
|
||||
)
|
||||
if cached_submenu is not None:
|
||||
return cached_submenu
|
||||
|
||||
data = await self.submenu_repo.get_submenu_item(menu_id, submenu_id)
|
||||
if data is None:
|
||||
return None
|
||||
submenu = data.__dict__
|
||||
submenu = {k: v for k, v in submenu.items() if not k.startswith('_')}
|
||||
submenu['dishes_count'] = len(submenu.pop('dishes'))
|
||||
menu = SubMenuRead(**submenu)
|
||||
await self.cache.set(
|
||||
self.key('submenu', menu_id=str(menu_id), submenu_id=str(submenu_id)),
|
||||
submenu,
|
||||
self.bg_tasks,
|
||||
)
|
||||
return menu
|
||||
|
||||
async def update_submenu(
|
||||
self, menu_id: UUID, submenu_id: UUID, submenu_data: MenuBase
|
||||
) -> SubMenuRead:
|
||||
data = await self.submenu_repo.update_submenu_item(
|
||||
menu_id, submenu_id, submenu_data
|
||||
)
|
||||
submenu = data.__dict__
|
||||
submenu = {k: v for k, v in submenu.items() if not k.startswith('_')}
|
||||
submenu['dishes_count'] = len(submenu.pop('dishes'))
|
||||
submenu = SubMenuRead(**submenu)
|
||||
await self.cache.set(
|
||||
self.key('submenu', menu_id=str(menu_id), submenu_id=str(submenu_id)),
|
||||
submenu,
|
||||
self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
|
||||
return submenu
|
||||
|
||||
async def del_menu(self, menu_id: UUID, submenu_id: UUID) -> int:
|
||||
code = await self.submenu_repo.delete_submenu_item(menu_id, submenu_id)
|
||||
await self.cache.delete(
|
||||
key=self.key(
|
||||
'submenu',
|
||||
menu_id=str(menu_id),
|
||||
submenu_id=str(submenu_id),
|
||||
),
|
||||
bg_task=self.bg_tasks,
|
||||
)
|
||||
await self.cache.invalidate(key=str(menu_id), bg_task=self.bg_tasks)
|
||||
return code
|
|
@ -1,3 +0,0 @@
|
|||
def price_converter(dish: dict) -> dict:
|
||||
dish.price = str(dish.price)
|
||||
return dish
|
12
manage.py
12
manage.py
|
@ -3,7 +3,7 @@ import sys
|
|||
|
||||
import uvicorn
|
||||
|
||||
from fastfood.cruds import create_db_and_tables
|
||||
from fastfood.repository import create_db_and_tables
|
||||
|
||||
|
||||
def run_app():
|
||||
|
@ -11,8 +11,8 @@ def run_app():
|
|||
Запуск FastAPI
|
||||
"""
|
||||
uvicorn.run(
|
||||
app="fastfood.app:create_app",
|
||||
host="0.0.0.0",
|
||||
app='fastfood.app:create_app',
|
||||
host='0.0.0.0',
|
||||
port=8000,
|
||||
reload=True,
|
||||
factory=True,
|
||||
|
@ -25,10 +25,10 @@ async def recreate():
|
|||
await create_db_and_tables()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--run-server" in sys.argv:
|
||||
if __name__ == '__main__':
|
||||
if '--run-server' in sys.argv:
|
||||
run_app()
|
||||
|
||||
if "--run-test-server" in sys.argv:
|
||||
if '--run-test-server' in sys.argv:
|
||||
asyncio.run(recreate())
|
||||
run_app()
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -103,13 +103,88 @@ test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"]
|
|||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2023.11.17"
|
||||
version = "2024.2.2"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
|
||||
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
|
||||
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
|
||||
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "1.16.0"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
|
||||
{file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
|
||||
{file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
|
||||
{file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
|
||||
{file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
|
||||
{file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
|
||||
{file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pycparser = "*"
|
||||
|
||||
[[package]]
|
||||
name = "cfgv"
|
||||
version = "3.4.0"
|
||||
description = "Validate configuration and produce human readable error messages."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
|
||||
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -204,6 +279,71 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
|
|||
[package.extras]
|
||||
toml = ["tomli"]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "42.0.2"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"},
|
||||
{file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"},
|
||||
{file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"},
|
||||
{file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"},
|
||||
{file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"},
|
||||
{file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"},
|
||||
{file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"},
|
||||
{file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"},
|
||||
{file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"},
|
||||
{file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"},
|
||||
{file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"},
|
||||
{file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
|
||||
docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
|
||||
nox = ["nox"]
|
||||
pep8test = ["check-sdist", "click", "mypy", "ruff"]
|
||||
sdist = ["build"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
|
||||
test-randomorder = ["pytest-randomly"]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.8"
|
||||
description = "Distribution utilities"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
|
||||
{file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.5.0"
|
||||
|
@ -272,6 +412,22 @@ typing-extensions = ">=4.8.0"
|
|||
[package.extras]
|
||||
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.13.1"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
|
||||
{file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
|
||||
typing = ["typing-extensions (>=4.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.0.3"
|
||||
|
@ -399,6 +555,20 @@ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
|
|||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (==1.*)"]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.5.33"
|
||||
description = "File identification library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"},
|
||||
{file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
license = ["ukkonen"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
|
@ -421,6 +591,78 @@ files = [
|
|||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.8.0"
|
||||
description = "Optional static typing for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"},
|
||||
{file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"},
|
||||
{file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"},
|
||||
{file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"},
|
||||
{file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"},
|
||||
{file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"},
|
||||
{file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"},
|
||||
{file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"},
|
||||
{file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"},
|
||||
{file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"},
|
||||
{file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"},
|
||||
{file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"},
|
||||
{file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"},
|
||||
{file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"},
|
||||
{file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"},
|
||||
{file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"},
|
||||
{file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"},
|
||||
{file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"},
|
||||
{file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"},
|
||||
{file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"},
|
||||
{file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"},
|
||||
{file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"},
|
||||
{file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"},
|
||||
{file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"},
|
||||
{file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"},
|
||||
{file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"},
|
||||
{file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mypy-extensions = ">=1.0.0"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = ">=4.1.0"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
install-types = ["pip"]
|
||||
mypyc = ["setuptools (>=50)"]
|
||||
reports = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.8.0"
|
||||
description = "Node.js virtual environment builder"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
|
||||
files = [
|
||||
{file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
|
||||
{file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
|
@ -432,35 +674,79 @@ files = [
|
|||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.2.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
|
||||
{file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
|
||||
{file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
|
||||
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
|
||||
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.6.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pre_commit-3.6.0-py2.py3-none-any.whl", hash = "sha256:c255039ef399049a5544b6ce13d135caba8f2c28c3b4033277a788f434308376"},
|
||||
{file = "pre_commit-3.6.0.tar.gz", hash = "sha256:d30bad9abf165f7785c15a21a1f46da7d0677cb00ee7ff4c579fd38922efe15d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cfgv = ">=2.0.0"
|
||||
identify = ">=1.0.0"
|
||||
nodeenv = ">=0.11.1"
|
||||
pyyaml = ">=5.1"
|
||||
virtualenv = ">=20.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.21"
|
||||
description = "C parser in Python"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
||||
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.5.3"
|
||||
version = "2.6.0"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"},
|
||||
{file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"},
|
||||
{file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"},
|
||||
{file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.4.0"
|
||||
pydantic-core = "2.14.6"
|
||||
pydantic-core = "2.16.1"
|
||||
typing-extensions = ">=4.6.1"
|
||||
|
||||
[package.extras]
|
||||
|
@ -468,116 +754,90 @@ email = ["email-validator (>=2.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.14.6"
|
||||
version = "2.16.1"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"},
|
||||
{file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"},
|
||||
{file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"},
|
||||
{file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"},
|
||||
{file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"},
|
||||
{file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"},
|
||||
{file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"},
|
||||
{file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"},
|
||||
{file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"},
|
||||
{file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"},
|
||||
{file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"},
|
||||
{file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"},
|
||||
{file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"},
|
||||
{file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"},
|
||||
{file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"},
|
||||
{file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"},
|
||||
{file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"},
|
||||
{file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"},
|
||||
{file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"},
|
||||
{file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"},
|
||||
{file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"},
|
||||
{file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"},
|
||||
{file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"},
|
||||
{file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"},
|
||||
{file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"},
|
||||
{file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"},
|
||||
{file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"},
|
||||
{file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"},
|
||||
{file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"},
|
||||
{file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"},
|
||||
{file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"},
|
||||
{file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"},
|
||||
{file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"},
|
||||
{file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"},
|
||||
{file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"},
|
||||
{file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"},
|
||||
{file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"},
|
||||
{file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"},
|
||||
{file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"},
|
||||
{file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"},
|
||||
{file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"},
|
||||
{file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"},
|
||||
{file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"},
|
||||
{file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"},
|
||||
{file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"},
|
||||
{file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"},
|
||||
{file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -622,17 +882,17 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no
|
|||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "0.23.3"
|
||||
version = "0.23.4"
|
||||
description = "Pytest support for asyncio"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-asyncio-0.23.3.tar.gz", hash = "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f"},
|
||||
{file = "pytest_asyncio-0.23.3-py3-none-any.whl", hash = "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba"},
|
||||
{file = "pytest-asyncio-0.23.4.tar.gz", hash = "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2"},
|
||||
{file = "pytest_asyncio-0.23.4-py3-none-any.whl", hash = "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=7.0.0"
|
||||
pytest = ">=7.0.0,<8"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
||||
|
@ -658,18 +918,111 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale
|
|||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
|
||||
{file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
|
||||
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
|
||||
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "4.6.0"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"},
|
||||
{file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""}
|
||||
|
||||
[package.extras]
|
||||
hiredis = ["hiredis (>=1.0.0)"]
|
||||
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "69.0.3"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"},
|
||||
{file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.0"
|
||||
|
@ -796,6 +1149,35 @@ files = [
|
|||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-pyopenssl"
|
||||
version = "24.0.0.20240130"
|
||||
description = "Typing stubs for pyOpenSSL"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "types-pyOpenSSL-24.0.0.20240130.tar.gz", hash = "sha256:c812e5c1c35249f75ef5935708b2a997d62abf9745be222e5f94b9595472ab25"},
|
||||
{file = "types_pyOpenSSL-24.0.0.20240130-py3-none-any.whl", hash = "sha256:24a255458b5b8a7fca8139cf56f2a8ad5a4f1a5f711b73a5bb9cb50dc688fab5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cryptography = ">=35.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "types-redis"
|
||||
version = "4.6.0.20240106"
|
||||
description = "Typing stubs for redis"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "types-redis-4.6.0.20240106.tar.gz", hash = "sha256:2b2fa3a78f84559616242d23f86de5f4130dfd6c3b83fb2d8ce3329e503f756e"},
|
||||
{file = "types_redis-4.6.0.20240106-py3-none-any.whl", hash = "sha256:912de6507b631934bd225cdac310b04a58def94391003ba83939e5a10e99568d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cryptography = ">=35.0.0"
|
||||
types-pyOpenSSL = "*"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.9.0"
|
||||
|
@ -826,7 +1208,27 @@ typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
|
|||
[package.extras]
|
||||
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.25.0"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"},
|
||||
{file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
distlib = ">=0.3.7,<1"
|
||||
filelock = ">=3.12.2,<4"
|
||||
platformdirs = ">=3.9.1,<5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "5bbc3cad36f6f40d10cb848918426b640f9e703bc2c6b22b5b8fe381a6251ded"
|
||||
content-hash = "106e42984de924817e2dc083ad78699b3411f9aa60de5bb5c1a95ca94a21fda1"
|
||||
|
|
|
@ -3887,4 +3887,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,4 +90,4 @@
|
|||
"_postman_variable_scope": "environment",
|
||||
"_postman_exported_at": "2023-01-12T16:22:10.333Z",
|
||||
"_postman_exported_using": "Postman/10.6.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,16 @@ asyncpg = "^0.29.0"
|
|||
pydantic-settings = "^2.1.0"
|
||||
email-validator = "^2.1.0.post1"
|
||||
pytest-asyncio = "^0.23.3"
|
||||
redis = "^4.6.0"
|
||||
types-redis = "^4.6.0.3"
|
||||
mypy = "^1.4.1"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.4"
|
||||
pytest-cov = "^4.1.0"
|
||||
httpx = "^0.26.0"
|
||||
pre-commit = "^3.6.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import asyncio
|
||||
from typing import AsyncGenerator, Dict, Generator
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
@ -20,7 +20,7 @@ async_session_maker = async_sessionmaker(
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def event_loop():
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
|
@ -30,7 +30,7 @@ def event_loop():
|
|||
loop.close()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function", autouse=True)
|
||||
@pytest_asyncio.fixture(scope='session', autouse=True)
|
||||
async def db_init(event_loop):
|
||||
async with async_engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
|
@ -45,28 +45,13 @@ async def get_test_session() -> AsyncGenerator[AsyncSession, None]:
|
|||
yield session
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def app(event_loop) -> Generator[FastAPI, None, None]:
|
||||
@pytest_asyncio.fixture(scope='session')
|
||||
async def client() -> AsyncGenerator[AsyncClient, None]:
|
||||
app: FastAPI = create_app()
|
||||
app.dependency_overrides[get_async_session] = get_test_session
|
||||
yield app
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def client(app) -> AsyncGenerator[AsyncClient, None]:
|
||||
async with AsyncClient(
|
||||
app=app,
|
||||
base_url="http://localhost:8000/api/v1/menus",
|
||||
base_url='http://localhost:8000/api/v1/menus',
|
||||
) as async_client:
|
||||
yield async_client
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def asession(event_loop) -> AsyncGenerator[AsyncSession, None]:
|
||||
async with async_session_maker() as session:
|
||||
yield session
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def session_data() -> Dict:
|
||||
return {}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
from httpx import AsyncClient, Response
|
||||
|
||||
from .urls import reverse_url
|
||||
|
||||
|
||||
class Repository:
|
||||
class Menu:
|
||||
@staticmethod
|
||||
async def read_all(ac: AsyncClient) -> tuple[int, dict]:
|
||||
"""чтение всех меню"""
|
||||
|
||||
response: Response = await ac.get(reverse_url('menus'))
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def get(ac: AsyncClient, data: dict) -> tuple[int, dict]:
|
||||
"""Получение меню по id"""
|
||||
response: Response = await ac.get(
|
||||
reverse_url('menu', menu_id=data.get('id'))
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def write(ac: AsyncClient, data: dict) -> tuple[int, dict]:
|
||||
"""создания меню"""
|
||||
response: Response = await ac.post(reverse_url('menus'), json=data)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def update(ac: AsyncClient, data: dict) -> tuple[int, dict]:
|
||||
"""Обновление меню по id"""
|
||||
response: Response = await ac.patch(
|
||||
reverse_url('menu', menu_id=data.get('id')),
|
||||
json=data,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def delete(ac: AsyncClient, data: dict) -> int:
|
||||
"""Удаление меню по id"""
|
||||
response: Response = await ac.delete(
|
||||
reverse_url('menu', menu_id=data.get('id')),
|
||||
)
|
||||
return response.status_code
|
||||
|
||||
class Submenu:
|
||||
@staticmethod
|
||||
async def read_all(ac: AsyncClient, menu: dict) -> tuple[int, dict]:
|
||||
"""чтение всех меню"""
|
||||
response: Response = await ac.get(
|
||||
reverse_url('submenus', menu_id=menu.get('id')),
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def get(
|
||||
ac: AsyncClient,
|
||||
menu: dict,
|
||||
submenu: dict,
|
||||
) -> tuple[int, dict]:
|
||||
"""Получение меню по id"""
|
||||
response: Response = await ac.get(
|
||||
reverse_url(
|
||||
'submenu',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
),
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def write(
|
||||
ac: AsyncClient,
|
||||
menu: dict,
|
||||
submenu: dict,
|
||||
) -> tuple[int, dict]:
|
||||
"""создания меню"""
|
||||
response: Response = await ac.post(
|
||||
reverse_url('submenu', menu_id=menu.get('id')),
|
||||
json=submenu,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def update(
|
||||
ac: AsyncClient, menu: dict, submenu: dict
|
||||
) -> tuple[int, dict]:
|
||||
"""Обновление меню по id"""
|
||||
response: Response = await ac.patch(
|
||||
reverse_url(
|
||||
'submenu',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
),
|
||||
json=submenu,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def delete(ac: AsyncClient, menu: dict, submenu: dict) -> int:
|
||||
"""Удаление меню по id"""
|
||||
response: Response = await ac.delete(
|
||||
reverse_url(
|
||||
'submenu',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
),
|
||||
)
|
||||
return response.status_code
|
||||
|
||||
class Dish:
|
||||
@staticmethod
|
||||
async def read_all(
|
||||
ac: AsyncClient, menu: dict, submenu: dict
|
||||
) -> tuple[int, dict]:
|
||||
"""чтение всех блюд"""
|
||||
response: Response = await ac.get(
|
||||
reverse_url(
|
||||
'dishes',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
),
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def get(
|
||||
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
|
||||
) -> tuple[int, dict]:
|
||||
"""Получение блюда по id"""
|
||||
response: Response = await ac.get(
|
||||
reverse_url(
|
||||
'dish',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
dish_id=dish.get('id'),
|
||||
),
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def write(
|
||||
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
|
||||
) -> tuple[int, dict]:
|
||||
"""создания блюда"""
|
||||
response: Response = await ac.post(
|
||||
reverse_url(
|
||||
'dishes',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
),
|
||||
json=dish,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def update(
|
||||
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
|
||||
) -> tuple[int, dict]:
|
||||
"""Обновление блюда по id"""
|
||||
response: Response = await ac.patch(
|
||||
reverse_url(
|
||||
'dish',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
dish_id=dish.get('id'),
|
||||
),
|
||||
json=dish,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def delete(
|
||||
ac: AsyncClient,
|
||||
menu: dict,
|
||||
submenu: dict,
|
||||
dish: dict,
|
||||
) -> int:
|
||||
"""Удаление блюда по id"""
|
||||
response: Response = await ac.delete(
|
||||
reverse_url(
|
||||
'dish',
|
||||
menu_id=menu.get('id'),
|
||||
submenu_id=submenu.get('id'),
|
||||
dish_id=dish.get('id'),
|
||||
),
|
||||
)
|
||||
return response.status_code
|
|
@ -1,603 +1,360 @@
|
|||
from typing import Dict, Tuple
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient, Response
|
||||
|
||||
|
||||
class TestBaseCrud:
|
||||
class Menu:
|
||||
@staticmethod
|
||||
async def read_all(ac: AsyncClient) -> Tuple[int, dict]:
|
||||
"""чтение всех меню"""
|
||||
response: Response = await ac.get("/")
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def get(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
|
||||
"""Получение меню по id"""
|
||||
response: Response = await ac.get(f"/{data.get('id')}")
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def write(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
|
||||
"""создания меню"""
|
||||
response: Response = await ac.post("/", json=data)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def update(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
|
||||
"""Обновление меню по id"""
|
||||
response: Response = await ac.patch(
|
||||
f"/{data.get('id')}",
|
||||
json=data,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def delete(ac: AsyncClient, data: dict) -> int:
|
||||
"""Удаление меню по id"""
|
||||
response: Response = await ac.delete(f"/{data.get('id')}")
|
||||
return response.status_code
|
||||
|
||||
class Submenu:
|
||||
@staticmethod
|
||||
async def read_all(ac: AsyncClient, menu: dict) -> Tuple[int, dict]:
|
||||
"""чтение всех меню"""
|
||||
response: Response = await ac.get(f"/{menu.get('id')}/submenus/")
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def get(
|
||||
ac: AsyncClient,
|
||||
menu: dict,
|
||||
submenu: dict,
|
||||
) -> Tuple[int, dict]:
|
||||
"""Получение меню по id"""
|
||||
response: Response = await ac.get(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}",
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def write(
|
||||
ac: AsyncClient,
|
||||
menu: dict,
|
||||
submenu: dict,
|
||||
) -> Tuple[int, dict]:
|
||||
"""создания меню"""
|
||||
response: Response = await ac.post(
|
||||
f"/{menu.get('id')}/submenus/",
|
||||
json=submenu,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def update(
|
||||
ac: AsyncClient, menu: dict, submenu: dict
|
||||
) -> Tuple[int, dict]:
|
||||
"""Обновление меню по id"""
|
||||
response: Response = await ac.patch(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}",
|
||||
json=submenu,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def delete(ac: AsyncClient, menu: dict, submenu: dict) -> int:
|
||||
"""Удаление меню по id"""
|
||||
response: Response = await ac.delete(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
|
||||
)
|
||||
return response.status_code
|
||||
|
||||
class Dish:
|
||||
@staticmethod
|
||||
async def read_all(
|
||||
ac: AsyncClient, menu: dict, submenu: dict
|
||||
) -> Tuple[int, dict]:
|
||||
"""чтение всех блюд"""
|
||||
response: Response = await ac.get(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}/dishes/",
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def get(
|
||||
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
|
||||
) -> Tuple[int, dict]:
|
||||
"""Получение блюда по id"""
|
||||
response: Response = await ac.get(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
|
||||
f"/dishes/{dish.get('id')}",
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def write(
|
||||
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
|
||||
) -> Tuple[int, dict]:
|
||||
"""создания блюда"""
|
||||
response: Response = await ac.post(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}/dishes/",
|
||||
json=dish,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def update(
|
||||
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
|
||||
) -> Tuple[int, dict]:
|
||||
"""Обновление блюда по id"""
|
||||
response: Response = await ac.patch(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
|
||||
f"/dishes/{dish.get('id')}",
|
||||
json=dish,
|
||||
)
|
||||
return response.status_code, response.json()
|
||||
|
||||
@staticmethod
|
||||
async def delete(ac: AsyncClient, menu: dict, submenu: dict, dish: dict) -> int:
|
||||
"""Удаление блюда по id"""
|
||||
response: Response = await ac.delete(
|
||||
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
|
||||
f"/dishes/{dish.get('id')}"
|
||||
)
|
||||
return response.status_code
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_empty(self, client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
code, rspn = await self.Menu.read_all(client)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_add(self, client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {"title": "Menu", "description": None}
|
||||
code, rspn = await self.Menu.write(client, data)
|
||||
assert code == 201
|
||||
assert rspn["title"] == "Menu"
|
||||
assert rspn["description"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_get(self, client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {"title": "Menu", "description": None}
|
||||
code, rspn = await self.Menu.write(client, data)
|
||||
code, menu = await self.Menu.get(client, {"id": rspn.get("id")})
|
||||
assert code == 200
|
||||
assert menu["title"] == rspn["title"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_update(self, client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {"title": "Menu", "description": None}
|
||||
code, rspn = await self.Menu.write(client, data)
|
||||
|
||||
upd_data = {
|
||||
"id": rspn.get("id"),
|
||||
"title": "upd Menu",
|
||||
"description": "",
|
||||
}
|
||||
code, upd_rspn = await self.Menu.update(client, upd_data)
|
||||
assert code == 200
|
||||
assert upd_rspn["title"] == "upd Menu"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_delete(self, client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {"title": "Menu", "description": None}
|
||||
code, rspn = await self.Menu.write(client, data)
|
||||
|
||||
code = await self.Menu.delete(client, rspn)
|
||||
assert code == 200
|
||||
|
||||
code, rspn = await self.Menu.get(client, {"id": rspn.get("id")})
|
||||
assert code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_get_all(self, client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
code, rspn = await self.Menu.read_all(client)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
data = {"title": "Menu", "description": None}
|
||||
code, rspn = await self.Menu.write(client, data)
|
||||
|
||||
code, upd_rspn = await self.Menu.read_all(client)
|
||||
assert code == 200
|
||||
assert upd_rspn == [rspn]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_get_all(self, client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {"title": "Menu", "description": "main menu"}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Проверяем наличие подменю
|
||||
code, rspn = await self.Submenu.read_all(client, menu)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Проверяем наличие подменю
|
||||
code, upd_rspn = await self.Submenu.read_all(client, menu)
|
||||
assert code == 200
|
||||
assert upd_rspn == [rspn]
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Submenu.delete(client, menu, submenu)
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_add(self, client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {"title": "Menu", "description": "main menu"}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
assert code == 201
|
||||
submenu.update(rspn)
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Submenu.delete(client, menu, submenu)
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_update(self, client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {"title": "Menu", "description": "main menu"}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Обновляем подменю и проверяем
|
||||
submenu["title"] = "updated_submenu"
|
||||
code, rspn = await self.Submenu.update(client, menu, submenu)
|
||||
assert code == 200
|
||||
assert submenu["title"] == rspn["title"]
|
||||
submenu.update(rspn)
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Submenu.delete(client, menu, submenu)
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_delete(self, client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {"title": "Menu", "description": "main menu"}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Удаляем подменю
|
||||
code = await self.Submenu.delete(client, menu, submenu)
|
||||
assert code == 200
|
||||
|
||||
# Проверяем удаленное подменю
|
||||
code, rspn = await self.Submenu.get(client, menu, submenu)
|
||||
assert code == 404
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_get_all(self, client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
"title": "Menu",
|
||||
"description": "main menu",
|
||||
}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Проверяем все блюда в подменю
|
||||
code, rspn = await self.Dish.read_all(client, menu, submenu)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
"title": "dish",
|
||||
"description": "some dish",
|
||||
"price": "12.5",
|
||||
"parent_submenu": submenu["id"],
|
||||
}
|
||||
code, rspn = await self.Dish.write(client, menu, submenu, dish)
|
||||
assert code == 201
|
||||
dish.update(rspn)
|
||||
|
||||
code, upd_rspn = await self.Dish.read_all(client, menu, submenu)
|
||||
|
||||
assert code == 200
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Dish.delete(client, menu, submenu, dish)
|
||||
await self.Submenu.delete(client, menu, submenu)
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_add(self, client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
"title": "Menu",
|
||||
"description": "main menu",
|
||||
}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
"title": "dish",
|
||||
"description": "some dish",
|
||||
"price": "12.5",
|
||||
"parent_submenu": submenu["id"],
|
||||
}
|
||||
code, rspn = await self.Dish.write(client, menu, submenu, dish)
|
||||
assert code == 201
|
||||
dish.update(rspn)
|
||||
|
||||
# Получаем блюдо
|
||||
code, rspn = await self.Dish.get(client, menu, submenu, dish)
|
||||
assert code == 200
|
||||
assert rspn["title"] == dish["title"]
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Dish.delete(client, menu, submenu, dish)
|
||||
await self.Submenu.delete(client, menu, submenu)
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_update(self, client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
"title": "Menu",
|
||||
"description": "main menu",
|
||||
}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
"title": "dish",
|
||||
"description": "some dish",
|
||||
"price": "12.5",
|
||||
"parent_submenu": submenu["id"],
|
||||
}
|
||||
code, rspn = await self.Dish.write(client, menu, submenu, dish)
|
||||
dish.update(rspn)
|
||||
|
||||
# Обновляем блюдо и проверяем
|
||||
dish["title"] = "updated_dish"
|
||||
code, rspn = await self.Dish.update(client, menu, submenu, dish)
|
||||
assert code == 200
|
||||
assert dish["title"] == rspn["title"]
|
||||
dish.update(rspn)
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Dish.delete(client, menu, submenu, dish)
|
||||
await self.Submenu.delete(client, menu, submenu)
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_delete(self, client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
"title": "Menu",
|
||||
"description": "main menu",
|
||||
}
|
||||
code, rspn = await self.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await self.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
"title": "dish",
|
||||
"description": "some dish",
|
||||
"price": "12.5",
|
||||
"parent_submenu": submenu["id"],
|
||||
}
|
||||
code, rspn = await self.Dish.write(client, menu, submenu, dish)
|
||||
dish.update(rspn)
|
||||
|
||||
# Удаляем подменю
|
||||
code = await self.Dish.delete(client, menu, submenu, dish)
|
||||
assert code == 200
|
||||
|
||||
# Проверяем удаленное блюдо
|
||||
code, rspn = await self.Dish.get(client, menu, submenu, dish)
|
||||
assert code == 404
|
||||
|
||||
# удаляем сопутствующее
|
||||
await self.Submenu.delete(client, menu, submenu)
|
||||
await self.Menu.delete(client, menu)
|
||||
|
||||
|
||||
class TestСontinuity:
|
||||
@pytest.mark.asyncio
|
||||
async def test_01(self, client, session_data: Dict):
|
||||
"""Проверяет создание меню"""
|
||||
data = {"title": "Menu", "description": "some"}
|
||||
code, rspn = await TestBaseCrud.Menu.write(client, data)
|
||||
|
||||
assert code == 201
|
||||
code, rspn = await TestBaseCrud.Menu.get(client, rspn)
|
||||
session_data["target_menu_id"] = rspn.get("id")
|
||||
session_data["target_menu_title"] = rspn.get("title")
|
||||
session_data["target_menu_description"] = rspn.get("description")
|
||||
|
||||
assert code == 200
|
||||
assert "id" in rspn
|
||||
assert "title" in rspn
|
||||
assert "description" in rspn
|
||||
assert "submenus_count" in rspn
|
||||
assert "dishes_count" in rspn
|
||||
|
||||
assert rspn["title"] == "Menu"
|
||||
assert rspn.get("description") == "some"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_postman_continuity(self, client):
|
||||
# Создаем меню
|
||||
menu = {
|
||||
"title": "Menu",
|
||||
"description": "main menu",
|
||||
}
|
||||
code, rspn = await TestBaseCrud.Menu.write(client, menu)
|
||||
assert code == 201
|
||||
assert "id" in rspn.keys()
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем подменю
|
||||
submenu = {
|
||||
"title": "Submenu",
|
||||
"description": "submenu",
|
||||
"parent_menu": menu["id"],
|
||||
}
|
||||
code, rspn = await TestBaseCrud.Submenu.write(client, menu, submenu)
|
||||
assert code == 201
|
||||
assert "id" in rspn.keys()
|
||||
submenu.update(rspn)
|
||||
|
||||
# Добавляем блюдо1
|
||||
dish = {
|
||||
"title": "dish1",
|
||||
"description": "some dish1",
|
||||
"price": "13.50",
|
||||
"parent_submenu": submenu["id"],
|
||||
}
|
||||
code, rspn = await TestBaseCrud.Dish.write(client, menu, submenu, dish)
|
||||
assert code == 201
|
||||
assert "id" in rspn.keys()
|
||||
dish.update(rspn)
|
||||
|
||||
# Добавляем блюдо2
|
||||
dish = {
|
||||
"title": "dish2",
|
||||
"description": "some dish2",
|
||||
"price": "12.50",
|
||||
"parent_submenu": submenu["id"],
|
||||
}
|
||||
code, rspn = await TestBaseCrud.Dish.write(client, menu, submenu, dish)
|
||||
assert code == 201
|
||||
assert "id" in rspn.keys()
|
||||
dish.update(rspn)
|
||||
|
||||
# Просматриваем конкретное меню
|
||||
code, rspn = await TestBaseCrud.Menu.get(client, menu)
|
||||
assert code == 200
|
||||
assert "id" in rspn.keys()
|
||||
assert menu["id"] == rspn["id"]
|
||||
assert "submenus_count" in rspn.keys()
|
||||
assert rspn["submenus_count"] == 1
|
||||
assert "dishes_count" in rspn.keys()
|
||||
assert rspn["dishes_count"] == 2
|
||||
|
||||
# Просматриваем конкретное подменю
|
||||
code, rspn = await TestBaseCrud.Submenu.get(client, menu, submenu)
|
||||
assert code == 200
|
||||
assert "id" in rspn.keys()
|
||||
assert "dishes_count" in rspn.keys()
|
||||
assert rspn["dishes_count"] == 2
|
||||
|
||||
# Удаляем подменю
|
||||
code = await TestBaseCrud.Submenu.delete(client, menu, submenu)
|
||||
assert code == 200
|
||||
|
||||
# Просматриваем список подменю
|
||||
code, rspn = await TestBaseCrud.Submenu.read_all(client, menu)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
# Просматриваем список блюд
|
||||
code, rspn = await TestBaseCrud.Dish.read_all(client, menu, submenu)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
# Просматриваем конкретное меню
|
||||
code, rspn = await TestBaseCrud.Menu.get(client, menu)
|
||||
assert code == 200
|
||||
assert "id" in rspn.keys()
|
||||
assert menu["id"] == rspn["id"]
|
||||
assert "submenus_count" in rspn.keys()
|
||||
assert rspn["submenus_count"] == 0
|
||||
assert "dishes_count" in rspn.keys()
|
||||
assert rspn["dishes_count"] == 0
|
||||
|
||||
# Удаляем меню
|
||||
code = await TestBaseCrud.Menu.delete(client, menu)
|
||||
assert code == 200
|
||||
|
||||
# Просматриваем все меню
|
||||
code, rspn = await TestBaseCrud.Menu.read_all(client)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .repository import Repository as Repo
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_empty(client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
code, rspn = await Repo.Menu.read_all(client)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_add(client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {'title': 'Menu', 'description': None}
|
||||
code, rspn = await Repo.Menu.write(client, data)
|
||||
assert code == 201
|
||||
assert rspn['title'] == 'Menu'
|
||||
assert rspn['description'] is None
|
||||
await Repo.Menu.delete(client, rspn)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_get(client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {'title': 'Menu', 'description': None}
|
||||
code, rspn = await Repo.Menu.write(client, data)
|
||||
code, menu = await Repo.Menu.get(client, {'id': rspn.get('id')})
|
||||
assert code == 200
|
||||
assert menu['title'] == rspn['title']
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_update(client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {'title': 'Menu', 'description': None}
|
||||
code, rspn = await Repo.Menu.write(client, data)
|
||||
|
||||
upd_data = {
|
||||
'id': rspn.get('id'),
|
||||
'title': 'upd Menu',
|
||||
'description': '',
|
||||
}
|
||||
code, upd_rspn = await Repo.Menu.update(client, upd_data)
|
||||
assert code == 200
|
||||
assert upd_rspn['title'] == 'upd Menu'
|
||||
await Repo.Menu.delete(client, upd_rspn)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_delete(client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
data = {'title': 'Menu', 'description': None}
|
||||
code, rspn = await Repo.Menu.write(client, data)
|
||||
|
||||
code = await Repo.Menu.delete(client, rspn)
|
||||
assert code == 200
|
||||
|
||||
code, rspn = await Repo.Menu.get(client, {'id': rspn.get('id')})
|
||||
assert code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu_crud_get_all(client: AsyncClient) -> None:
|
||||
"""Тестирование функций меню"""
|
||||
code, rspn = await Repo.Menu.read_all(client)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
data = {'title': 'Menu', 'description': None}
|
||||
code, rspn = await Repo.Menu.write(client, data)
|
||||
|
||||
code, upd_rspn = await Repo.Menu.read_all(client)
|
||||
assert code == 200
|
||||
assert upd_rspn == [rspn]
|
||||
await Repo.Menu.delete(client, rspn)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_get_all(client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {'title': 'Menu', 'description': 'main menu'}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
assert code == 201
|
||||
menu.update(rspn)
|
||||
|
||||
# Проверяем наличие подменю
|
||||
code, rspn = await Repo.Submenu.read_all(client, menu)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Проверяем наличие подменю
|
||||
code, upd_rspn = await Repo.Submenu.read_all(client, menu)
|
||||
assert code == 200
|
||||
assert upd_rspn == [rspn]
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Submenu.delete(client, menu, submenu)
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_add(client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {'title': 'Menu', 'description': 'main menu'}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
assert code == 201
|
||||
submenu.update(rspn)
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Submenu.delete(client, menu, submenu)
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_update(client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {'title': 'Menu', 'description': 'main menu'}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Обновляем подменю и проверяем
|
||||
submenu['title'] = 'updated_submenu'
|
||||
code, rspn = await Repo.Submenu.update(client, menu, submenu)
|
||||
assert code == 200
|
||||
assert submenu['title'] == rspn['title']
|
||||
submenu.update(rspn)
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Submenu.delete(client, menu, submenu)
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenus_delete(client) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {'title': 'Menu', 'description': 'main menu'}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Удаляем подменю
|
||||
code = await Repo.Submenu.delete(client, menu, submenu)
|
||||
assert code == 200
|
||||
|
||||
# Проверяем удаленное подменю
|
||||
code, rspn = await Repo.Submenu.get(client, menu, submenu)
|
||||
assert code == 404
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_get_all(client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
'title': 'Menu',
|
||||
'description': 'main menu',
|
||||
}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Проверяем все блюда в подменю
|
||||
code, rspn = await Repo.Dish.read_all(client, menu, submenu)
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
'title': 'dish',
|
||||
'description': 'some dish',
|
||||
'price': '12.5',
|
||||
'parent_submenu': submenu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
|
||||
assert code == 201
|
||||
dish.update(rspn)
|
||||
|
||||
code, upd_rspn = await Repo.Dish.read_all(client, menu, submenu)
|
||||
|
||||
assert code == 200
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Dish.delete(client, menu, submenu, dish)
|
||||
await Repo.Submenu.delete(client, menu, submenu)
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_add(client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
'title': 'Menu',
|
||||
'description': 'main menu',
|
||||
}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
'title': 'dish',
|
||||
'description': 'some dish',
|
||||
'price': '12.5',
|
||||
'parent_submenu': submenu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
|
||||
assert code == 201
|
||||
dish.update(rspn)
|
||||
|
||||
# Получаем блюдо
|
||||
code, rspn = await Repo.Dish.get(client, menu, submenu, dish)
|
||||
assert code == 200
|
||||
assert rspn['title'] == dish['title']
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Dish.delete(client, menu, submenu, dish)
|
||||
await Repo.Submenu.delete(client, menu, submenu)
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_update(client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
'title': 'Menu',
|
||||
'description': 'main menu',
|
||||
}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
'title': 'dish',
|
||||
'description': 'some dish',
|
||||
'price': '12.5',
|
||||
'parent_submenu': submenu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
|
||||
dish.update(rspn)
|
||||
|
||||
# Обновляем блюдо и проверяем
|
||||
dish['title'] = 'updated_dish'
|
||||
code, rspn = await Repo.Dish.update(client, menu, submenu, dish)
|
||||
assert code == 200
|
||||
assert dish['title'] == rspn['title']
|
||||
dish.update(rspn)
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Dish.delete(client, menu, submenu, dish)
|
||||
await Repo.Submenu.delete(client, menu, submenu)
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dishes_delete(client: AsyncClient) -> None:
|
||||
# Создаем меню и проверяем ответ
|
||||
menu = {
|
||||
'title': 'Menu',
|
||||
'description': 'main menu',
|
||||
}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
menu.update(rspn)
|
||||
|
||||
# Создаем и проверяем подменю
|
||||
submenu = {
|
||||
'title': 'Submenu',
|
||||
'description': 'submenu',
|
||||
'parent_menu': menu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
submenu.update(rspn)
|
||||
|
||||
# Добавляем блюдо
|
||||
dish = {
|
||||
'title': 'dish',
|
||||
'description': 'some dish',
|
||||
'price': '12.5',
|
||||
'parent_submenu': submenu['id'],
|
||||
}
|
||||
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
|
||||
dish.update(rspn)
|
||||
|
||||
# Удаляем подменю
|
||||
code = await Repo.Dish.delete(client, menu, submenu, dish)
|
||||
assert code == 200
|
||||
|
||||
# Проверяем удаленное блюдо
|
||||
code, rspn = await Repo.Dish.get(client, menu, submenu, dish)
|
||||
assert code == 404
|
||||
|
||||
# удаляем сопутствующее
|
||||
await Repo.Submenu.delete(client, menu, submenu)
|
||||
await Repo.Menu.delete(client, menu)
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from fastfood.cruds.dish import DishCrud
|
||||
from fastfood.cruds.menu import MenuCrud
|
||||
from fastfood.cruds.submenu import SubMenuCrud
|
||||
from fastfood.models import Dish, Menu, SubMenu
|
||||
from fastfood.schemas import DishBase as dishschema
|
||||
from fastfood.schemas import Menu as menuschema
|
||||
from fastfood.schemas import MenuBase as menubaseschema
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_menu(asession: AsyncSession) -> None:
|
||||
async with asession:
|
||||
# Создаем меню
|
||||
menu: Menu = Menu(title="SomeMenu", description="SomeDescription")
|
||||
menu: Menu = await MenuCrud.create_menu_item(
|
||||
menubaseschema.model_validate(menu),
|
||||
asession,
|
||||
)
|
||||
menu_id: UUID = menu.id
|
||||
|
||||
# Получаем его же
|
||||
req_menu: Menu | None = await MenuCrud.get_menu_item(menu_id, asession)
|
||||
assert menu == req_menu
|
||||
|
||||
# Получаем все меню и проверяем
|
||||
req_menus = await MenuCrud.get_menus(asession)
|
||||
assert menu == req_menus.scalars().all()[0]
|
||||
|
||||
# Обновляем
|
||||
menu.title = "updatedMenu"
|
||||
await MenuCrud.update_menu_item(
|
||||
menu.id, menuschema.model_validate(menu), asession
|
||||
)
|
||||
# И сверяем
|
||||
req_menu = await MenuCrud.get_menu_item(menu_id, asession)
|
||||
assert menu == req_menu
|
||||
|
||||
# Удаляем и проверяем
|
||||
await MenuCrud.delete_menu_item(menu_id, asession)
|
||||
req_menus = await MenuCrud.get_menus(asession)
|
||||
assert req_menus.all() == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submenu(asession: AsyncSession) -> None:
|
||||
async with asession:
|
||||
# Создаем меню напрямую
|
||||
menu: Menu = Menu(title="SomeMenu", description="SomeDescription")
|
||||
asession.add(menu)
|
||||
await asession.commit()
|
||||
await asession.refresh(menu)
|
||||
menu_id: UUID = menu.id
|
||||
|
||||
# Создаем подменю
|
||||
submenu: SubMenu = SubMenu(
|
||||
title="submenu",
|
||||
description="",
|
||||
parent_menu=menu_id,
|
||||
)
|
||||
submenu = await SubMenuCrud.create_submenu_item(
|
||||
menu_id,
|
||||
menubaseschema.model_validate(submenu),
|
||||
asession,
|
||||
)
|
||||
submenu_id = submenu.id
|
||||
|
||||
# Проверяем подменю
|
||||
req_submenu = await SubMenuCrud.get_submenu_item(
|
||||
menu_id,
|
||||
submenu.id,
|
||||
asession,
|
||||
)
|
||||
assert submenu == req_submenu
|
||||
assert submenu.dishes_count == 0
|
||||
|
||||
# Обновляем меню
|
||||
submenu.title = "UpdatedSubmenu"
|
||||
req_submenu = await SubMenuCrud.update_submenu_item(
|
||||
submenu_id,
|
||||
menubaseschema.model_validate(submenu),
|
||||
asession,
|
||||
)
|
||||
assert submenu == req_submenu.scalar_one_or_none()
|
||||
|
||||
menu = await MenuCrud.get_menu_item(menu_id, asession)
|
||||
assert 1 == menu.submenus_count
|
||||
|
||||
# Удаляем полменю
|
||||
await SubMenuCrud.delete_submenu_item(submenu_id, asession)
|
||||
|
||||
menu = await MenuCrud.get_menu_item(menu_id, asession)
|
||||
assert 0 == menu.submenus_count
|
||||
|
||||
await MenuCrud.delete_menu_item(menu_id, asession)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dish(asession: AsyncSession):
|
||||
"""Not Implemented yet"""
|
||||
async with asession:
|
||||
# Создаем меню напрямую
|
||||
menu = Menu(title="SomeMenu", description="SomeDescription")
|
||||
asession.add(menu)
|
||||
await asession.commit()
|
||||
await asession.refresh(menu)
|
||||
menu_id: UUID = menu.id
|
||||
|
||||
# Создаем подменю
|
||||
submenu: SubMenu = SubMenu(
|
||||
title="submenu",
|
||||
description="",
|
||||
parent_menu=menu_id,
|
||||
)
|
||||
asession.add(submenu)
|
||||
await asession.commit()
|
||||
await asession.refresh(submenu)
|
||||
submenu_id = submenu.id
|
||||
|
||||
# Создаем блюдо
|
||||
dish: Dish = Dish(
|
||||
title="dish1",
|
||||
description="dish number 1",
|
||||
price="12.5",
|
||||
parent_submenu=submenu_id,
|
||||
)
|
||||
dish = await DishCrud.create_dish_item(
|
||||
submenu_id,
|
||||
dishschema.model_validate(dish),
|
||||
asession,
|
||||
)
|
||||
dish_id = dish.id
|
||||
|
||||
# Проверяем блюдо
|
||||
req_dish = await DishCrud.get_dish_item(
|
||||
dish_id,
|
||||
asession,
|
||||
)
|
||||
assert dish == req_dish
|
||||
|
||||
menu = await MenuCrud.get_menu_item(menu_id, asession)
|
||||
submenu = await SubMenuCrud.get_submenu_item(
|
||||
menu_id,
|
||||
submenu.id,
|
||||
asession,
|
||||
)
|
||||
|
||||
assert menu.submenus_count == 1
|
||||
assert menu.dishes_count == 1
|
||||
assert submenu.dishes_count == 1
|
||||
|
||||
# Обновляем блюдо
|
||||
dish.price = 177
|
||||
req_dish = await DishCrud.update_dish_item(
|
||||
dish_id,
|
||||
dishschema.model_validate(dish),
|
||||
asession,
|
||||
)
|
||||
assert dish == req_dish
|
||||
|
||||
# Удаляем длюдо
|
||||
await DishCrud.delete_dish_item(dish_id, asession)
|
||||
|
||||
menu = await MenuCrud.get_menu_item(menu_id, asession)
|
||||
submenu = await SubMenuCrud.get_submenu_item(
|
||||
menu_id,
|
||||
submenu.id,
|
||||
asession,
|
||||
)
|
||||
|
||||
assert menu.dishes_count == 0
|
||||
assert submenu.dishes_count == 0
|
||||
|
||||
await SubMenuCrud.delete_submenu_item(submenu_id, asession)
|
||||
await MenuCrud.delete_menu_item(menu_id, asession)
|
|
@ -0,0 +1,238 @@
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .repository import Repository as Repo
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def session_data() -> dict:
|
||||
return {}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_01(client: AsyncClient, session_data: dict):
|
||||
"""Проверяет создание меню"""
|
||||
menu = {'title': 'Menu', 'description': 'some_menu_desc'}
|
||||
code, rspn = await Repo.Menu.write(client, menu)
|
||||
|
||||
assert code == 201
|
||||
code, rspn = await Repo.Menu.get(client, rspn)
|
||||
session_data['target_menu_id'] = rspn.get('id')
|
||||
session_data['target_menu_title'] = rspn.get('title')
|
||||
session_data['target_menu_description'] = rspn.get('description')
|
||||
|
||||
assert code == 200
|
||||
assert 'id' in rspn
|
||||
assert 'title' in rspn
|
||||
assert 'description' in rspn
|
||||
assert 'submenus_count' in rspn
|
||||
assert 'dishes_count' in rspn
|
||||
assert rspn['title'] == menu.get('title')
|
||||
assert rspn.get('description') == menu.get('description')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_02(client: AsyncClient, session_data: dict):
|
||||
submenu = {'title': 'Submenu', 'description': 'submenu_descr'}
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
|
||||
code, rspn = await Repo.Submenu.write(client, menu, submenu)
|
||||
|
||||
assert code == 201
|
||||
assert 'id' in rspn
|
||||
assert 'title' in rspn
|
||||
assert 'description' in rspn
|
||||
assert 'dishes_count' in rspn
|
||||
assert rspn['title'] == submenu.get('title')
|
||||
assert rspn.get('description') == submenu.get('description')
|
||||
|
||||
session_data['target_submenu_id'] = rspn.get('id')
|
||||
session_data['target_submenu_title'] = rspn.get('title')
|
||||
session_data['target_submenu_description'] = rspn.get('description')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_03_dish1(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
submenu = {
|
||||
'id': session_data.get('target_submenu_id'),
|
||||
'title': session_data.get('target_submenu_title'),
|
||||
'description': session_data.get('target_submenu_description'),
|
||||
}
|
||||
dish = {'title': 'dish_1', 'description': 'dish 1 descr', 'price': '12.5'}
|
||||
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
|
||||
|
||||
assert code == 201
|
||||
assert 'id' in rspn
|
||||
assert 'title' in rspn
|
||||
assert 'description' in rspn
|
||||
assert 'price' in rspn
|
||||
assert rspn['title'] == dish.get('title')
|
||||
assert rspn.get('description') == dish.get('description')
|
||||
assert rspn.get('price') == dish.get('price')
|
||||
|
||||
session_data['target_dish_id'] = rspn.get('id')
|
||||
session_data['target_dish_title'] = rspn.get('title')
|
||||
session_data['target_dish_description'] = rspn.get('description')
|
||||
session_data['target_dish_price'] = rspn.get('price')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_04_dish2(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
submenu = {
|
||||
'id': session_data.get('target_submenu_id'),
|
||||
'title': session_data.get('target_submenu_title'),
|
||||
'description': session_data.get('target_submenu_description'),
|
||||
}
|
||||
dish = {'title': 'dish_2', 'description': 'dish 2 descr', 'price': '13.5'}
|
||||
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
|
||||
|
||||
assert code == 201
|
||||
assert 'id' in rspn
|
||||
assert 'title' in rspn
|
||||
assert 'description' in rspn
|
||||
assert 'price' in rspn
|
||||
assert rspn['title'] == dish.get('title')
|
||||
assert rspn.get('description') == dish.get('description')
|
||||
assert rspn.get('price') == dish.get('price')
|
||||
|
||||
session_data['target_dish1_id'] = rspn.get('id')
|
||||
session_data['target_dish1_title'] = rspn.get('title')
|
||||
session_data['target_dish1_description'] = rspn.get('description')
|
||||
session_data['target_dish1_price'] = rspn.get('price')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_05_check_menu(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
code, rspn = await Repo.Menu.get(client, menu)
|
||||
|
||||
assert code == 200
|
||||
assert 'id' in rspn
|
||||
assert 'title' in rspn
|
||||
assert 'description' in rspn
|
||||
assert rspn.get('submenus_count') == 1
|
||||
assert rspn.get('dishes_count') == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_06_check_submenu(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
submenu = {
|
||||
'id': session_data.get('target_submenu_id'),
|
||||
'title': session_data.get('target_submenu_title'),
|
||||
'description': session_data.get('target_submenu_description'),
|
||||
}
|
||||
code, rspn = await Repo.Submenu.get(client, menu, submenu)
|
||||
|
||||
assert code == 200
|
||||
assert 'id' in rspn
|
||||
assert 'title' in rspn
|
||||
assert 'description' in rspn
|
||||
assert rspn.get('dishes_count') == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_07_del_submenu(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
submenu = {
|
||||
'id': session_data.get('target_submenu_id'),
|
||||
'title': session_data.get('target_submenu_title'),
|
||||
'description': session_data.get('target_submenu_description'),
|
||||
}
|
||||
code = await Repo.Submenu.delete(client, menu, submenu)
|
||||
|
||||
assert code == 200
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_07_check_submenus(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
code, rspn = await Repo.Submenu.read_all(client, menu)
|
||||
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_08_check_dishes(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
submenu = {
|
||||
'id': session_data.get('target_submenu_id'),
|
||||
'title': session_data.get('target_submenu_title'),
|
||||
'description': session_data.get('target_submenu_description'),
|
||||
}
|
||||
code, rspn = await Repo.Dish.read_all(client, menu, submenu)
|
||||
|
||||
assert code == 200
|
||||
assert rspn == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_09_check_menu(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
code, rspn = await Repo.Menu.get(client, menu)
|
||||
|
||||
assert code == 200
|
||||
assert 'id' in rspn
|
||||
assert 'title' in rspn
|
||||
assert 'description' in rspn
|
||||
assert rspn.get('submenus_count') == 0
|
||||
assert rspn.get('dishes_count') == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_10_del_menu(client: AsyncClient, session_data: dict):
|
||||
menu = {
|
||||
'id': session_data.get('target_menu_id'),
|
||||
'title': session_data.get('target_menu_title'),
|
||||
'description': session_data.get('target_menu_description'),
|
||||
}
|
||||
code = await Repo.Menu.delete(client, menu)
|
||||
|
||||
assert code == 200
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_11_check_menus(client: AsyncClient, session_data: dict):
|
||||
code, rspn = await Repo.Menu.read_all(client)
|
||||
|
||||
assert code == 200
|
||||
assert rspn == []
|
|
@ -0,0 +1,18 @@
|
|||
def reverse_url(loc: str, **kwargs) -> str:
|
||||
menu_pref = '/'
|
||||
submenu_pref = menu_pref + str(kwargs.get('menu_id', '')) + '/submenus/'
|
||||
dish_pref = submenu_pref + str(kwargs.get('submenu_id', '')) + '/dishes/'
|
||||
match loc:
|
||||
case 'menus':
|
||||
return menu_pref
|
||||
case 'menu':
|
||||
return menu_pref + str(kwargs.get('menu_id', ''))
|
||||
case 'submenus':
|
||||
return submenu_pref
|
||||
case 'submenu':
|
||||
return submenu_pref + str(kwargs.get('submenu_id', ''))
|
||||
case 'dishes':
|
||||
return dish_pref
|
||||
case 'dish':
|
||||
return dish_pref + str(kwargs.get('dish_id', ''))
|
||||
return menu_pref
|
Loading…
Reference in New Issue