Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions src/part4/cart.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
an explanatory message to stdout and returns ``None``.
"""

from .storage import Product
from .storage import QUANTITY_KEY, Product


# TODO: при необходимости добавьте свои вспомогательные функции
Expand Down Expand Up @@ -47,7 +47,22 @@ def add_to_cart(
request.
"""
# TODO: реализуйте функцию
return 0
if product_id not in storage:
print(f"no product with id {product_id}")
return None
quantity_old = storage[product_id][QUANTITY_KEY]
if quantity_old < quantity:
print(
f"not enough stock for product {product_id}: {quantity_old} available, {quantity} requested"
)
return None
storage[product_id][QUANTITY_KEY] = quantity_old - quantity
if product_id in cart:
cart[product_id] += quantity
else:
cart[product_id] = quantity

return cart[product_id]


def remove_from_cart(
Expand Down Expand Up @@ -84,4 +99,21 @@ def remove_from_cart(
the cart does not hold the product or holds too few units.
"""
# TODO: реализуйте функцию
return 0
if product_id not in cart:
print(f"product {product_id} is not in the cart")
return None
if cart[product_id] < quantity:
print(
f"cart holds only {cart[product_id]} unit(s) of product {product_id}, "
f"cannot remove {quantity}"
)
return None
if product_id not in storage:
print(f"no product with id {product_id}")
return None
cart[product_id] -= quantity
storage[product_id][QUANTITY_KEY] += quantity
if cart[product_id] == 0:
del cart[product_id]
return 0
return cart[product_id]
64 changes: 55 additions & 9 deletions src/part4/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@
than a scan over ``storage.values()``.
"""

import uuid # noqa: F401
import uuid
from typing import Any

from .storage import ( # noqa: F401
from .storage import (
NAME_KEY,
PRICE_KEY,
QUANTITY_KEY,
Product,
)
from .utils import normalize_price, normalize_product_name # noqa: F401
from .utils import normalize_price, normalize_product_name


def generate_product_id() -> str:
Expand All @@ -42,7 +42,7 @@ def generate_product_id() -> str:
A freshly generated UUID, stringified.
"""
# TODO: реализуйте функцию
return ""
return str(uuid.uuid4())


def create_product(
Expand Down Expand Up @@ -74,7 +74,22 @@ def create_product(
message is printed.
"""
# TODO: реализуйте функцию
return ""
name, price, quantity = fields[NAME_KEY], fields[PRICE_KEY], fields[QUANTITY_KEY]
normalized_name = normalize_product_name(name)
if not normalized_name:
print("product name must not be blank")
return None
if normalized_name in product_names:
print(f"product name '{normalized_name}' is already taken")
return None
newuuid = generate_product_id()
storage[newuuid] = {
NAME_KEY: normalized_name,
PRICE_KEY: normalize_price(price),
QUANTITY_KEY: quantity,
}
product_names.add(normalized_name)
return newuuid


def read_product(storage: dict[str, Product], product_id: str) -> Product | None:
Expand All @@ -89,7 +104,10 @@ def read_product(storage: dict[str, Product], product_id: str) -> Product | None
that identifier (a message is printed in that case).
"""
# TODO: реализуйте функцию
return {}
if product_id not in storage:
print(f"no product with id {product_id}")
return None
return storage[product_id]


def update_product(
Expand Down Expand Up @@ -129,8 +147,30 @@ def update_product(
and an explanatory message is printed. Renaming a product to its
own current name is allowed.
"""
# TODO: реализуйте функцию
return {}
if product_id not in storage:
print(f"no product with id {product_id}")
return None
name_old = storage[product_id][NAME_KEY]
if NAME_KEY in fields:
normalized_name = normalize_product_name(fields[NAME_KEY])
if not normalized_name:
print("product name must not be blank")
return None
if normalized_name != name_old and normalized_name in product_names:
print(f"product name '{normalized_name}' is already taken")
return None
storage[product_id][NAME_KEY] = normalized_name
fields[NAME_KEY] = normalized_name
if name_old != normalized_name:
product_names.remove(name_old)
product_names.add(normalized_name)
if PRICE_KEY in fields:
normalized_price = normalize_price(fields[PRICE_KEY])
fields[PRICE_KEY] = normalized_price
storage[product_id][PRICE_KEY] = normalized_price
if QUANTITY_KEY in fields:
storage[product_id][QUANTITY_KEY] = fields[QUANTITY_KEY]
return storage[product_id]


def delete_product(
Expand All @@ -154,4 +194,10 @@ def delete_product(
are left unchanged and a message is printed).
"""
# TODO: реализуйте функцию
return ""
if product_id not in storage:
print(f"no product with id {product_id}")
return None
name = storage[product_id][NAME_KEY]
del storage[product_id]
product_names.remove(name)
return product_id
66 changes: 57 additions & 9 deletions src/part4/run_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,36 @@
is left untouched and the loop keeps running; only ``exit`` stops it.
"""

from decimal import Decimal, InvalidOperation # noqa: F401
from decimal import Decimal, InvalidOperation
from typing import Final

from .crud import ( # noqa: F401
from .crud import (
create_product,
delete_product,
read_product,
update_product,
)
from .storage import NAME_KEY, PRICE_KEY, QUANTITY_KEY, Product # noqa: F401
from .utils import get_storage_str_representation # noqa: F401
from .storage import NAME_KEY, PRICE_KEY, QUANTITY_KEY, Product
from .utils import get_storage_str_representation


# TODO: задайте приглашение и текст справки (используйте своё решение части 3)
PROMPT: Final[str] = ""
HELP_TEXT: Final[str] = ""


PROMPT: Final[str] = "admin> "

HELP_TEXT: Final[str] = """Available commands:
help show this message
exit leave the console
show print the whole store as a table
create <name...> <price> <quantity> add a product, print its new id
read <id> print the product with that id
update <id> <name...> <price> <quantity> overwrite that product's fields
delete <id> remove the product with that id

For create and update the price and quantity are the last two words of the
line; everything before them is the product name, so it may contain spaces
(for example "Gibson SG Junior") and needs no quoting."""


def show_help() -> None:
Expand All @@ -53,7 +67,7 @@ def show_help() -> None:
is: a heading, one line per command with a short description, and a
note on how multi-word names are parsed.
"""
# TODO: реализуйте функцию (используйте своё решение части 3)
print(HELP_TEXT)


def print_result(result: object) -> None:
Expand All @@ -64,7 +78,8 @@ def print_result(result: object) -> None:
operation already reported its own failure, so nothing is
printed in that case.
"""
# TODO: реализуйте функцию (используйте своё решение части 3)
if result is not None:
print(result)


def run_command(
Expand Down Expand Up @@ -92,7 +107,40 @@ def run_command(
``update`` does not parse as a decimal number.
"""
# TODO: реализуйте функцию
return False
match line.split():
case ["read", product_id]:
print_result(read_product(storage=storage, product_id=product_id))
case ["delete", product_id]:
print_result(
delete_product(
storage=storage, product_names=product_names, product_id=product_id
)
)
case ["create", *name, price, quantity] if name:
product_name = " ".join(name)
fields = {
NAME_KEY: product_name,
PRICE_KEY: Decimal(price),
QUANTITY_KEY: int(quantity),
}
print_result(create_product(storage, product_names, fields))
case ["update", product_id, *name, price, quantity] if name:
product_name = " ".join(name)
fields = {
NAME_KEY: product_name,
PRICE_KEY: Decimal(price),
QUANTITY_KEY: int(quantity),
}
print_result(update_product(storage, product_names, product_id, fields))
case ["show"]:
print(get_storage_str_representation(storage))
case ["exit"]:
return False
case ["help"]:
show_help()
case _:
print(f"'{line}' is not a command")
return True


def main() -> None:
Expand Down
6 changes: 3 additions & 3 deletions src/part4/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@

# Name of each field inside a Product record.
# TODO: задайте имена полей записи
NAME_KEY: Final = ""
PRICE_KEY: Final = ""
QUANTITY_KEY: Final = ""
NAME_KEY: Final = "name"
PRICE_KEY: Final = "price"
QUANTITY_KEY: Final = "quantity"
40 changes: 30 additions & 10 deletions src/part4/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
the longest value it holds in that particular call.
"""

from decimal import ROUND_HALF_UP, Decimal # noqa: F401
from decimal import ROUND_HALF_UP, Decimal
from typing import Final

from .storage import ( # noqa: F401
from .storage import (
NAME_KEY,
PRICE_KEY,
QUANTITY_KEY,
Expand All @@ -33,14 +33,14 @@
# Quantisation step derived from PRICE_PRECISION, e.g. Decimal("0.01").
# TODO: задайте число знаков после запятой и шаг квантования (используйте своё
# решение части 3)
PRICE_PRECISION: Final[int] = 0
PRICE_STEP: Final = Decimal(0)
PRICE_PRECISION: Final[int] = 2
PRICE_STEP: Final[Decimal] = Decimal(1).scaleb(-PRICE_PRECISION)

# Column headers of the table produced by get_storage_str_representation,
# left to right. The width of each column is not fixed here -- it is
# measured per call from the data (see the function).
# TODO: задайте заголовки столбцов таблицы (используйте своё решение части 3)
TABLE_HEADERS: Final[tuple[str, ...]] = ()
TABLE_HEADERS: Final[tuple[str, ...]] = ("ID", "name", "price", "quantity")


def normalize_price(price: Decimal) -> Decimal:
Expand All @@ -53,8 +53,7 @@ def normalize_price(price: Decimal) -> Decimal:
``price`` quantised to :data:`PRICE_PRECISION` fractional digits,
with halves rounded up.
"""
# TODO: реализуйте функцию (используйте своё решение части 3)
return Decimal(0)
return price.quantize(PRICE_STEP, rounding=ROUND_HALF_UP)


def normalize_product_name(name: str) -> str:
Expand All @@ -71,8 +70,10 @@ def normalize_product_name(name: str) -> str:
The result is an empty string when ``name`` holds nothing but
whitespace.
"""
# TODO: реализуйте функцию (используйте своё решение части 3)
return ""
list_of_words = name.split()
name = " ".join(list_of_words)
name = name.lower()
return name


# TODO: при необходимости добавьте свои вспомогательные функции
Expand All @@ -97,4 +98,23 @@ def get_storage_str_representation(storage: dict[str, Product]) -> str:
returned, sized to the header labels.
"""
# TODO: реализуйте функцию (используйте своё решение части 3)
return ""
mxlen1 = len(TABLE_HEADERS[0])
mxlen2 = len(TABLE_HEADERS[1])
mxlen3 = len(TABLE_HEADERS[2])
mxlen4 = len(TABLE_HEADERS[3])
for item_id, product in storage.items():
name = str(product[NAME_KEY])
price = str(product[PRICE_KEY])
quantity = str(product[QUANTITY_KEY])
mxlen1 = max(mxlen1, len(item_id))
mxlen2 = max(mxlen2, len(name))
mxlen3 = max(mxlen3, len(price))
mxlen4 = max(mxlen4, len(quantity))
s1 = f"| {TABLE_HEADERS[0].ljust(mxlen1)} | {TABLE_HEADERS[1].ljust(mxlen2)} | {TABLE_HEADERS[2].ljust(mxlen3)} | {TABLE_HEADERS[3].ljust(mxlen4)} |"
s2 = f"|{'-' * (mxlen1 + 2)}|{'-' * (mxlen2 + 2)}|{'-' * (mxlen3 + 2)}|{'-' * (mxlen4 + 2)}|"
snext = [
f"| {str(item_id).ljust(mxlen1)} | {str(product[NAME_KEY]).ljust(mxlen2)} | {str(product[PRICE_KEY]).ljust(mxlen3)} | {str(product[QUANTITY_KEY]).ljust(mxlen4)} |"
for item_id, product in storage.items()
]
rows = [s1, s2, *snext]
return "\n".join(rows)
Loading