diff --git a/pizza_project/README.md b/pizza_project/README.md new file mode 100644 index 0000000..489cf41 --- /dev/null +++ b/pizza_project/README.md @@ -0,0 +1,43 @@ +# Pizza project + + Для работы в файле cli.py нужно установить пакет **click** в терминале: ```pip install click``` + +Для того чтобы вывести с его помощью меню пишем: ```python cli.py menu``` + +Получаем: + +```- Margherita 🧀: tomato sauce, mozzarella, tomatoes``` + +```- Pepperoni 🍕: tomato sauce, mozzarella, pepperoni``` + +```- Hawaiian 🍍: tomato sauce, mozzarella, chicken, pineapples``` + +------ +Чтобы посмотреть на этапы приготовления нашей пиццы: ```python cli.py order peperoni --delivery``` + +Например, можем получить вывод такого типы (но время может быть другое) + +```🔪 приготовили за 4 минут!``` + +```🛵 доставили за 9 минут!``` + +------ +Переходим к тестированию нашего проекта. + +Для запска тестовых функций в терпинале запускаем: ```python -m pytest -v testing.py``` + +Получем: + +PS D:\pythonProject1> python -m pytest -v testing.py + +=================================== test session starts=================================== + +platform win32 -- Python 3.10.7, pytest-7.4.3, pluggy-1.3.0 -- D:\python\python.exe + +cachedir: .pytest_cache + +rootdir: D:\pythonProject1 + +plugins: cov-4.1.0 + +=================================== 10 passed in 0.09s =================================== diff --git a/pizza_project/cli.py b/pizza_project/cli.py new file mode 100644 index 0000000..270b63c --- /dev/null +++ b/pizza_project/cli.py @@ -0,0 +1,43 @@ +import click +from services import bake, deliver, pickup +from pizza import Pizza + + +@click.group() +def cli(): + pass + + +@cli.command() +@click.option("--delivery", default=False, is_flag=True) +@click.argument("pizza", nargs=1) +def order(pizza: str, delivery: bool) -> None: + """ + Функция позволяет через терминал осуществить приготовление и доставку пиццы + с указанием временных затрат на каждом этапе + :param pizza: str + :param delivery: bool + :return: None + """ + for pizza_name in Pizza.__subclasses__(): + if pizza_name().__class__.__name__.lower() == pizza: + bake(pizza_name()) + if delivery: + deliver(pizza_name()) + else: + pickup(pizza_name()) + + +@cli.command() +def menu() -> None: + """ + Функция позволяет через терминал осуществить посмотреть на меню + :return: None + """ + for pizza in Pizza.__subclasses__(): + recipe = pizza().dict() + print('- ', *recipe.keys(), ': ', *recipe.values(), sep='') + + +if __name__ == '__main__': + cli() diff --git a/pizza_project/pizza.py b/pizza_project/pizza.py new file mode 100644 index 0000000..1c52d72 --- /dev/null +++ b/pizza_project/pizza.py @@ -0,0 +1,54 @@ +class Pizza: + """Базовый класс для всех видов пицц""" + + def __init__(self, size: str = 'L', name: str = None, extra_ingredients: list = []): + if size not in ['L', 'XL']: + raise ValueError("У нас нет пиццы такого размера. " + "Размер может быть только L или XL.") + self.size = size + self.name = name + self.ingredients = ["tomato sauce", "mozzarella"] + self.ingredients.extend(extra_ingredients) + + def dict(self) -> dict[str]: + """ + Метод выводит рецепт в виде словаря + :return: dict[str] + """ + return {self.__dict__['name']: ', '.join(self.__dict__['ingredients'])} + + def __eq__(self, other) -> bool: + """ + Метод позволяет сравнить две пиццы на равенство + :param self: Pizza + :param other: Pizza + :return: bool + """ + return self.ingredients == other.ingredients and self.size == other.size + + +class Margherita(Pizza): + """Класс пиццы Маргарита""" + + def __init__(self, size: str = "L"): + self.name = "Margherita 🧀" + self.extra_ingredients = ["tomatoes"] + super().__init__(size, self.name, self.extra_ingredients) + + +class Pepperoni(Pizza): + """Класс пиццы Пепперони""" + + def __init__(self, size: str = "L"): + self.name = "Pepperoni 🍕" + self.extra_ingredients = ["pepperoni"] + super().__init__(size, self.name, self.extra_ingredients) + + +class Hawaiian(Pizza): + """Класс Гавайской пиццы""" + + def __init__(self, size: str = "L"): + self.name = "Hawaiian 🍍" + self.extra_ingredients = ["chicken", "pineapples"] + super().__init__(size, self.name, self.extra_ingredients) diff --git a/pizza_project/services.py b/pizza_project/services.py new file mode 100644 index 0000000..46c3df9 --- /dev/null +++ b/pizza_project/services.py @@ -0,0 +1,46 @@ +import random +from typing import Callable +from pizza import Pizza, Margherita, Pepperoni, Hawaiian + + +def log(params: str) -> Callable: + """ + Декоратор логирует время исполнения функции. + Принимает шаблон и выводит случайное значение. + :param params: str + :return: Callable + """ + + def decorator(func: Callable) -> Callable: + nonlocal params + if not isinstance(params, str): + params = params.__name__ + ' - {}c!' + print(params.format(random.randint(1, 10))) + + def wrapper(*args, **kwargs): + print(params.format(random.randint(1, 10))) + return func(*args, **kwargs) + + return wrapper + + return decorator + + +@log +def bake(pizza: Pizza): + """Готовит пиццу""" + + +@log('🛵 доставили за {} минут!') +def deliver(pizza: Pizza): + """Доставляет пиццу""" + + +@log('🏠 забрали за {} минут!') +def pickup(pizza: Pizza): + """Самовывоз""" + + +if __name__ == '__main__': + bake(Margherita()) + deliver(Hawaiian()) diff --git a/pizza_project/testing.py b/pizza_project/testing.py new file mode 100644 index 0000000..b718b69 --- /dev/null +++ b/pizza_project/testing.py @@ -0,0 +1,124 @@ +import pytest +import random +from pizza import Pizza, Margherita, Pepperoni, Hawaiian +from services import bake, deliver, pickup +from cli import order, menu +from click.testing import CliRunner + + +def test_equality(): + """ + Проверка пицц на равенство/ + проверка корректность работы магического метода __eq__ + :return: None + """ + assert Hawaiian() == Hawaiian() + assert Hawaiian(size='XL') != Hawaiian(size='L') + assert Hawaiian() != Pepperoni() + + +def test_pizza_dict(): + """ + Проверка на корректность работы функции dict + :return: None + """ + assert Pizza().dict() == {None: 'tomato sauce, mozzarella'} + assert Margherita().dict() == {'Margherita 🧀': 'tomato sauce, mozzarella, tomatoes'} + assert Pepperoni().dict() == {'Pepperoni 🍕': 'tomato sauce, mozzarella, pepperoni'} + assert Hawaiian().dict() == {'Hawaiian 🍍': 'tomato sauce, mozzarella, chicken, pineapples'} + assert Margherita().__class__.__name__ != str(*Hawaiian().dict().keys()) + + +def test_size_exceptions(): + """ + Провека корректность вызова исключений при некорректном вводе размера пиццы + :return: None + """ + with pytest.raises(ValueError): + Pizza(size='l') + with pytest.raises(ValueError): + Pizza(size=30) + + +def test_with_decorator(): + """ + Проверка выходов функций услуг + :return: None + """ + assert deliver(Margherita()) is None + assert pickup(Margherita()) is None + + +def test_bake(capsys): + """ + Проверка работы функции bake вместе с декоратором + :return: None + """ + random.seed(42) + bake(Margherita()) + out = capsys.readouterr().out.split("\n") + assert out[0] == 'bake - 2c!' + + +def test_deliver(capsys): + """ + Проверка работы функции deliver вместе с декоратором + :return: None + """ + result = deliver(Margherita()) + out, err = capsys.readouterr().out.split("\n") + assert result is None + assert out.startswith("🛵 доставили за") + + +def test_pickup(capsys): + """ + Проверка работы функции pickup вместе с декоратором + :return: None + """ + result = pickup(Margherita()) + out, err = capsys.readouterr().out.split("\n") + assert result is None + assert out.startswith("🏠 забрали за") + + +def test_menu(): + """ + Проверка корректности выводимого меню + :return: None + """ + runner = CliRunner() + result = runner.invoke(menu) + assert result.output == "- Margherita 🧀: tomato sauce, mozzarella, tomatoes\n" \ + "- Pepperoni 🍕: tomato sauce, mozzarella, pepperoni\n" \ + "- Hawaiian 🍍: tomato sauce, mozzarella, chicken, pineapples\n" + + +def test_invalid_order(): + """ + Проверка работы терминальных функций при некорректном наименовании пиццы + :return: None + """ + runner = CliRunner() + result = runner.invoke(order, ["margarita"]) + assert result.output.strip() == '' + + +def test_order_no_delivery_flag(): + """ + Проверка работы терминальных функций заказа без использования флага доставки + :return: None + """ + runner = CliRunner() + result = runner.invoke(order, ["pepperoni"]) + assert " забрали за" in result.output.strip() + + +def test_order_no_delivery_flag(): + """ + Проверка работы терминальных функций заказа с использованием флага доставки + :return: None + """ + runner = CliRunner() + result = runner.invoke(order, ["pepperoni", "--delivery"]) + assert " доставили за" in result.output.strip()