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
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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше не использовать транслитерацию для именования переменных. Используйте валидные английские слова при выборе имен переменных. Например, timestamp_ms

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

return (vremya << (NODE_ID_BITS + SEQUENCE_ID_BITS)) | (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вычисление этого сдвига встречается в коде несколько раз. Это нарушает критерий S1. Минус балл

node_id << (SEQUENCE_ID_BITS | sequence_id)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вы ошиблись с расстановкой скобок. Правильно так:

(
    (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