Skip to content
Open
1 change: 1 addition & 0 deletions pl-001-python-basics-task
Submodule pl-001-python-basics-task added at 339b23
16 changes: 8 additions & 8 deletions src/part1/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@
# Все константы, зависящие от ёмкостей *_BITS, должны вычисляться из них.

# Twitter's original Snowflake epoch: 2010-11-04 01:42:54.657 UTC.
EPOCH_MS_DEFAULT: Final[int] = 0
EPOCH_MS_DEFAULT: Final[int] = 1288834974657

# Node identifier used when the caller does not supply one.
NODE_ID_DEFAULT: Final[int] = 0
NODE_ID_DEFAULT: Final[int] = 1

# Width of each field, in bits.
TIMESTAMP_BITS: Final[int] = 0
NODE_ID_BITS: Final[int] = 0
SEQUENCE_ID_BITS: Final[int] = 0
TIMESTAMP_BITS: Final[int] = 41
NODE_ID_BITS: Final[int] = 10
SEQUENCE_ID_BITS: Final[int] = 12

# Largest value each field can hold.
TIMESTAMP_MS_MAX: Final[int] = 0
NODE_ID_MAX: Final[int] = 0
SEQUENCE_ID_MAX: Final[int] = 0
TIMESTAMP_MS_MAX: Final[int] = (2**TIMESTAMP_BITS) - 1
NODE_ID_MAX: Final[int] = (2**NODE_ID_BITS) - 1
SEQUENCE_ID_MAX: Final[int] = (2**SEQUENCE_ID_BITS) - 1

# Здесь можно добавить собственные вспомогательные константы
# (например, сдвиги полей при сборке идентификатора).
30 changes: 24 additions & 6 deletions src/part1/snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
:func:`decode_node_id` and :func:`decode_sequence_id`.
"""

import time # noqa: F401
import time

from .constants import ( # noqa: F401
EPOCH_MS_DEFAULT,
NODE_ID_BITS,
NODE_ID_DEFAULT,
NODE_ID_MAX,
SEQUENCE_ID_BITS,
SEQUENCE_ID_MAX,
TIMESTAMP_BITS,
TIMESTAMP_MS_MAX,
)

Expand All @@ -35,7 +38,7 @@ def read_current_millis(epoch_ms: int) -> int:
negative if ``epoch_ms`` lies in the future.
"""
# TODO: реализуйте функцию
return 0
return int(time.time() * 1000) - epoch_ms


def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int:
Expand All @@ -51,7 +54,7 @@ def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) ->
generated.
"""
# TODO: реализуйте функцию
return 0
return epoch_ms + (snowflake_id >> (NODE_ID_BITS + SEQUENCE_ID_BITS))


def decode_node_id(snowflake_id: int) -> int:
Expand All @@ -65,7 +68,7 @@ def decode_node_id(snowflake_id: int) -> int:
``[0, NODE_ID_MAX]``.
"""
# TODO: реализуйте функцию
return 0
return (snowflake_id >> SEQUENCE_ID_BITS) & NODE_ID_MAX


def decode_sequence_id(snowflake_id: int) -> int:
Expand All @@ -79,7 +82,7 @@ def decode_sequence_id(snowflake_id: int) -> int:
the range ``[0, SEQUENCE_ID_MAX]``.
"""
# TODO: реализуйте функцию
return 0
return snowflake_id & SEQUENCE_ID_MAX


def generate_snowflake_id(
Expand Down Expand Up @@ -111,4 +114,19 @@ def generate_snowflake_id(
cases an explanatory message is printed to stdout first.
"""
# TODO: реализуйте функцию
return 0
if not (0 <= node_id <= NODE_ID_MAX):
print(f"node_id must be in [0, {NODE_ID_MAX}], have {node_id}")
return None

if not (0 <= sequence_id <= SEQUENCE_ID_MAX):
print(f"sequence_id must be in [0; {SEQUENCE_ID_MAX}], have {sequence_id}")
return None

vremya = read_current_millis(epoch_ms)
if vremya > TIMESTAMP_MS_MAX:
print("Timestamps overflows")
return None

return (vremya << (NODE_ID_BITS + SEQUENCE_ID_BITS)) | (
node_id << (SEQUENCE_ID_BITS | sequence_id)
)
1 change: 1 addition & 0 deletions src/part2/TASK.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
построим на нём серверную часть простого интернет-магазина: каталог товаров и
корзину покупателя.


Если писать такой код «в лоб», быстро возникают проблемы:

- записи о товарах разбросаны по программе, и каждый кусок кода сам решает, как
Expand Down
83 changes: 78 additions & 5 deletions src/part2/cart.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

from typing import Final

from .crud import read_product, update_product # noqa: F401
from .storage import ( # noqa: F401
from .crud import read_product, update_product
from .storage import (
NAME_INDEX,
PRICE_INDEX,
QUANTITY_INDEX,
Expand All @@ -24,7 +24,33 @@

# TODO: задайте позиции полей внутри кортежа CartLine
LINE_PRODUCT_ID_INDEX: Final = 0
LINE_QUANTITY_INDEX: Final = 0
LINE_QUANTITY_INDEX: Final = 1


def find_cart_line(cart: list[CartLine], product_id: int) -> CartLine | None:

for line in cart:
if line[LINE_PRODUCT_ID_INDEX] == product_id:
return line
return None


def change_cart_quantity(
cart: list[CartLine], product_id: int, delta: int
) -> CartLine | None:

for i, line in enumerate(cart):
if line[LINE_PRODUCT_ID_INDEX] == product_id:
new_quantity = line[LINE_QUANTITY_INDEX] + delta

if new_quantity == 0:
del cart[i]
return None
else:
new_line = (product_id, new_quantity)
cart[i] = new_line
return new_line
return None


def add_to_cart(
Expand Down Expand Up @@ -54,7 +80,29 @@ def add_to_cart(
when the product is unknown or the store cannot cover the request.
"""
# TODO: реализуйте функцию
return (0, 0)
product = read_product(storage, product_id)
if product is None:
return None

stock_quantity = product[QUANTITY_INDEX]
if stock_quantity < quantity:
print(
f"not enough stock for product {product_id}: "
f"{stock_quantity} available, {quantity} requested"
)
return None

new_stock_quantity = stock_quantity - quantity
fields = (product[NAME_INDEX], product[PRICE_INDEX], new_stock_quantity)
update_product(storage, product_id, fields)

existing_line = find_cart_line(cart, product_id)
if existing_line:
return change_cart_quantity(cart, product_id, quantity)
else:
new_line = (product_id, quantity)
cart.append(new_line)
return new_line


def remove_from_cart(
Expand Down Expand Up @@ -86,4 +134,29 @@ def remove_from_cart(
line for the product or holds too few units.
"""
# TODO: реализуйте функцию
return (0, 0)
cart_line = find_cart_line(cart, product_id)
if cart_line is None:
print(f"product {product_id} is not in the cart")
return None

cart_quantity = cart_line[LINE_QUANTITY_INDEX]
if cart_quantity < quantity:
print(
f"cart holds only {cart_quantity} unit(s) of product "
f"{product_id}, cannot remove {quantity}"
)
return None

product = read_product(storage, product_id)
if product is None:
return None

new_stock_quantity = product[QUANTITY_INDEX] + quantity
fields = (product[NAME_INDEX], product[PRICE_INDEX], new_stock_quantity)
update_product(storage, product_id, fields)

result = change_cart_quantity(cart, product_id, -quantity)

if result is None:
return (product_id, 0)
return result
49 changes: 42 additions & 7 deletions src/part2/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@

from decimal import Decimal

from .storage import ( # noqa: F401
from .storage import (
NAME_INDEX,
PRODUCT_ID_INDEX,
PRODUCT_ID_MIN,
Product,
)
from .utils import normalize_price # noqa: F401
from .utils import normalize_price


def generate_product_id(storage: list[Product]) -> int:
Expand All @@ -35,7 +35,9 @@ def generate_product_id(storage: list[Product]) -> int:
empty.
"""
# TODO: реализуйте функцию
return 0
if not storage:
return PRODUCT_ID_MIN
return max(product[PRODUCT_ID_INDEX] for product in storage) + 1


def create_product(
Expand All @@ -57,7 +59,19 @@ def create_product(
name is printed.
"""
# TODO: реализуйте функцию
return 0
name, price, quantity = fields

for product in storage:
if product[NAME_INDEX] == name:
print(f"product name '{name}' is already taken")
return None

product_id = generate_product_id(storage)
normalized_price = normalize_price(price)

new_product = (product_id, name, normalized_price, quantity)
storage.append(new_product)
return product_id


def read_product(storage: list[Product], product_id: int) -> Product | None:
Expand All @@ -73,7 +87,12 @@ def read_product(storage: list[Product], product_id: int) -> Product | None:
printed in that case).
"""
# TODO: реализуйте функцию
return (0, "", Decimal(0), 0)
for product in storage:
if product[PRODUCT_ID_INDEX] == product_id:
return product

print(f"no product with id {product_id}")
return None


def update_product(
Expand All @@ -100,7 +119,17 @@ def update_product(
left unchanged and a message is printed).
"""
# TODO: реализуйте функцию
return (0, "", Decimal(0), 0)
name, price, quantity = fields
normalized_price = normalize_price(price)

for i, product in enumerate(storage):
if product[PRODUCT_ID_INDEX] == product_id:
updated_product = (product_id, name, normalized_price, quantity)
storage[i] = updated_product
return updated_product

print(f"no product with id {product_id}")
return None


def delete_product(storage: list[Product], product_id: int) -> int | None:
Expand All @@ -117,4 +146,10 @@ def delete_product(storage: list[Product], product_id: int) -> int | None:
a message is printed).
"""
# TODO: реализуйте функцию
return 0
for i, product in enumerate(storage):
if product[PRODUCT_ID_INDEX] == product_id:
del storage[i]
return product_id

print(f"no product with id {product_id}")
return None
8 changes: 4 additions & 4 deletions src/part2/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@

# TODO: задайте позиции полей внутри кортежа Product
PRODUCT_ID_INDEX: Final = 0
NAME_INDEX: Final = 0
PRICE_INDEX: Final = 0
QUANTITY_INDEX: Final = 0
NAME_INDEX: Final = 1
PRICE_INDEX: Final = 2
QUANTITY_INDEX: Final = 3

# TODO: задайте идентификатор первого товара в пустом хранилище
PRODUCT_ID_MIN: Final[int] = 0
PRODUCT_ID_MIN: Final[int] = 1
8 changes: 4 additions & 4 deletions src/part2/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
currency values free of binary floating-point error.
"""

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


# TODO: задайте число знаков после запятой и шаг квантования
PRICE_PRECISION: Final[int] = 0
PRICE_STEP: Final = Decimal(0)
PRICE_PRECISION: Final[int] = 2
PRICE_STEP: Final = Decimal(1).scaleb(-PRICE_PRECISION)


def normalize_price(price: Decimal) -> Decimal:
Expand All @@ -26,4 +26,4 @@ def normalize_price(price: Decimal) -> Decimal:
with halves rounded up.
"""
# TODO: реализуйте функцию
return Decimal(0)
return price.quantize(PRICE_STEP, rounding=ROUND_HALF_UP)
1 change: 1 addition & 0 deletions src/part3/TASK.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ CRUD-операции над ним. Корзина (`cart.py`) в этой ча
`"abc def\tghi\tjkl" → "abc def ghi jkl"`, `" \t\n " → ""`,
`"claw hammer" → "claw hammer"` (уже канонично — не меняется).


### `get_storage_str_representation(storage: list[Product]) -> str`

Отрисовывает всё хранилище одной мультистрокой — выровненной таблицей,
Expand Down
Loading
Loading