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)
)
58 changes: 51 additions & 7 deletions src/part3/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,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, normalize_product_name # noqa: F401
from .utils import normalize_price, normalize_product_name


def generate_product_id(storage: list[Product]) -> int:
Expand All @@ -41,7 +41,9 @@ def generate_product_id(storage: list[Product]) -> int:
empty.
"""
# TODO: реализуйте функцию (используйте своё решение части 2)
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 @@ -65,7 +67,20 @@ def create_product(
is left unchanged and an explanatory message is printed.
"""
# TODO: реализуйте функцию
return 0
name, price, quantity = fields
name = normalize_product_name(name)
if not name:
print("product name must not be blank")
return None
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)
price = normalize_price(price)
new_product = (product_id, name, price, quantity)
storage.append(new_product)
return product_id


def read_product(storage: list[Product], product_id: int) -> Product | None:
Expand All @@ -81,7 +96,11 @@ def read_product(storage: list[Product], product_id: int) -> Product | None:
printed in that case).
"""
# TODO: реализуйте функцию (используйте своё решение части 2)
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 Down Expand Up @@ -111,7 +130,27 @@ def update_product(
product to its own current name is allowed.
"""
# TODO: реализуйте функцию
return (0, "", Decimal(0), 0)
name, price, quantity = fields
name = normalize_product_name(name)
if not name:
print("product name must not be blank")
return None
target_index = None
for i, product in enumerate(storage):
if product[PRODUCT_ID_INDEX] == product_id:
target_index = i
break
if target_index is None:
print(f"no product with id {product_id}")
return None
for product in storage:
if product[NAME_INDEX] == name and product[PRODUCT_ID_INDEX] != product_id:
print(f"product name '{name}' is already taken")
return None
price = normalize_price(price)
updated_product = (product_id, name, price, quantity)
storage[target_index] = updated_product
return updated_product


def delete_product(storage: list[Product], product_id: int) -> int | None:
Expand All @@ -128,4 +167,9 @@ def delete_product(storage: list[Product], product_id: int) -> int | None:
a message is printed).
"""
# TODO: реализуйте функцию (используйте своё решение части 2)
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
64 changes: 58 additions & 6 deletions src/part3/run_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
it.
"""

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

from .crud import ( # noqa: F401
Expand All @@ -39,12 +39,23 @@
update_product,
)
from .storage import Product
from .utils import get_storage_str_representation # noqa: F401
from .utils import get_storage_str_representation


# TODO: задайте приглашение и текст справки
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 @@ -55,6 +66,7 @@ def show_help() -> None:
note on how multi-word names are parsed.
"""
# TODO: реализуйте функцию
print(HELP_TEXT)


def print_result(result: object) -> None:
Expand All @@ -66,6 +78,8 @@ def print_result(result: object) -> None:
printed in that case.
"""
# TODO: реализуйте функцию
if result is not None:
print(result)


def run_command(storage: list[Product], line: str) -> bool:
Expand All @@ -89,13 +103,51 @@ def run_command(storage: list[Product], line: str) -> bool:
``update`` does not parse as a decimal number.
"""
# TODO: реализуйте функцию
return False
parts = line.split()
if not parts:
return True
command = parts[0]
if command == "help":
show_help()
elif command == "exit":
return False
elif command == "show":
print(get_storage_str_representation(storage))
elif command == "read" or command == "delete":
if len(parts) != 2:
print(f"'{line}' is not a command")
return True
product_id = int(parts[1])
print_result(read_product(storage, product_id))
elif command == "create":
if len(parts) < 4:
print(f"'{line}' is not a command")
return True
name = " ".join(parts[1:-2])
price = Decimal(parts[-2])
quantity = int(parts[-1])
result = create_product(storage, (name, price, quantity))
print_result(result)
elif command == "update":
if len(parts) < 5:
print(f"'{line}' is not a command")
return True
product_id = int(parts[1])
name = " ".join(parts[2:-2])
price = Decimal(parts[-2])
quantity = int(parts[-1])
updated = update_product(storage, product_id, (name, price, quantity))
print_result(updated)
else:
print(f"'{line}' is not a command")

return True


def main() -> None:
"""Run the admin console until the ``exit`` command is entered.

A single product store is created empty and kept in memory for the
A sngle product store is created empty and kept in memory for the
whole session. Each iteration reads one line from stdin, strips its
surrounding whitespace and hands it to :func:`run_command`. A line
that names a command but whose id, price or quantity argument does
Expand Down
8 changes: 4 additions & 4 deletions src/part3/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
# TODO: задайте позиции полей внутри кортежа Product (используйте своё решение
# части 2)
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
Loading
Loading