Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
fc51f36
feat: add constants.py
lev100908 Sep 21, 2026
21eb845
feat: function read_current_millis
lev100908 Sep 21, 2026
bfba356
fix: change time() to time_ns()
lev100908 Sep 21, 2026
48fa5be
feat: add constants for shift abd function decode_timestamp_ms
lev100908 Sep 21, 2026
b72c5db
feat: add decode_node_id and decode_sequence_id
lev100908 Sep 21, 2026
373adc2
feat: add function generate_snowflake_id
lev100908 Sep 21, 2026
a253f2c
refactor: improve code style
lev100908 Sep 21, 2026
aa8546c
Merge branch 'python-department:main' into main
lev100908 Sep 27, 2026
cae10c0
feat: add constants in storage.py
lev100908 Sep 27, 2026
740c25c
feat: add function normalize_price and constants in utils.py
lev100908 Sep 27, 2026
825f77d
feat: add function generate_product_id in crud.py
lev100908 Sep 27, 2026
115190b
feat: add function create_product to crud.py
lev100908 Sep 27, 2026
a4bc22a
feat: add function read_product to crud.py
lev100908 Sep 27, 2026
c7c2d7b
feat: add function update_product to crud.py
lev100908 Sep 27, 2026
0b55970
feat: add function delete_product to crud.py
lev100908 Sep 27, 2026
c87a973
feat: add constants and function add_to_cart to cart.py
lev100908 Sep 27, 2026
4a81729
feat: add find_cart_line to cart.py
lev100908 Sep 27, 2026
23f6759
refactor: updated_cartline to updated_cart_line
lev100908 Sep 27, 2026
fdfd991
feat: change return value in find_cart_line
lev100908 Sep 27, 2026
23ed763
refactor move find_cart_line and change_cart_line
lev100908 Sep 27, 2026
188b347
feat: add functionality of find_cart_line and change_cart_quantity to…
lev100908 Sep 27, 2026
c64bb77
fix: rewrite condition of checking cart line in line (include index_c…
lev100908 Sep 27, 2026
0f47b48
fix: fix deleting an empty cart line
lev100908 Sep 27, 2026
86e851d
refactor: improve code style
lev100908 Sep 28, 2026
c1ddeba
Merge branch 'python-department:main' into main
lev100908 Oct 8, 2026
90d7375
feat: add several functions from task2 to crud.py storage.py and util…
lev100908 Oct 8, 2026
8d1af88
style: delete useless comments
lev100908 Oct 8, 2026
e1ae299
feat: add function normalize_product_name to utils.py
lev100908 Oct 8, 2026
258cb47
fix: moved normalizing name before checking it
lev100908 Oct 8, 2026
66030fc
feat: redone function update_product in crud.py
lev100908 Oct 8, 2026
2ef476c
feat: add function get_storage_str_representation and helper functions
lev100908 Oct 8, 2026
d0acfaa
feat: add constants and function show_help to run_admin.py
lev100908 Oct 8, 2026
52db6e4
feat: add function print_result and run_command to run_admin.py
lev100908 Oct 8, 2026
0eb4524
refactor: improve code style
lev100908 Oct 8, 2026
1fefaff
fix: drop trailing newline from show table
lev100908 Oct 11, 2026
ff068da
Merge branch 'python-department:main' into main
lev100908 Oct 27, 2026
a0d9113
feat: add constants to storage.py
lev100908 Oct 27, 2026
e708d84
feat: add functionality to utils.py
lev100908 Oct 27, 2026
157edcf
feat: add generate_product_id and create_product
lev100908 Oct 27, 2026
f8e685a
feat: add read_product and update_product
lev100908 Oct 27, 2026
df2e19e
feat: add delete_product
lev100908 Oct 27, 2026
bc91843
feat: add functions add_to_cart and remove_from_cart
lev100908 Oct 27, 2026
95d0917
feat: rewrite run_admin.py and add several versions of update in console
lev100908 Oct 28, 2026
45b76d3
fix: solve some problems
lev100908 Oct 28, 2026
19a2fb1
fix: solve some problems
lev100908 Oct 28, 2026
2df0700
style: fix style
lev100908 Oct 28, 2026
d765570
Merge branch 'python-department:main' into main
lev100908 Nov 11, 2026
75eece5
Merge branch 'task4'
lev100908 Nov 12, 2026
326c05e
fix: fix constants
lev100908 Nov 12, 2026
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
20 changes: 12 additions & 8 deletions src/part1/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,24 @@
# Все константы, зависящие от ёмкостей *_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] = (1 << TIMESTAMP_BITS) - 1
NODE_ID_MAX: Final[int] = (1 << NODE_ID_BITS) - 1
SEQUENCE_ID_MAX: Final[int] = (1 << SEQUENCE_ID_BITS) - 1

# Здесь можно добавить собственные вспомогательные константы
# (например, сдвиги полей при сборке идентификатора).

SEQUENCE_SHIFT = 0
NODE_SHIFT = SEQUENCE_ID_BITS
TIMESTAMP_SHIFT = SEQUENCE_ID_BITS + NODE_ID_BITS
44 changes: 32 additions & 12 deletions src/part1/snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@
:func:`decode_node_id` and :func:`decode_sequence_id`.
"""

import time # noqa: F401
import time

from .constants import ( # noqa: F401
from .constants import (
EPOCH_MS_DEFAULT,
NODE_ID_DEFAULT,
NODE_ID_MAX,
NODE_SHIFT,
SEQUENCE_ID_MAX,
SEQUENCE_SHIFT,
TIMESTAMP_MS_MAX,
TIMESTAMP_SHIFT,
)


Expand All @@ -34,8 +37,8 @@ def read_current_millis(epoch_ms: int) -> int:
The count of whole milliseconds between ``epoch_ms`` and now. May be
negative if ``epoch_ms`` lies in the future.
"""
# TODO: реализуйте функцию
return 0
now_ms = time.time_ns() // 1_000_000
return now_ms - epoch_ms


def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int:
Expand All @@ -50,8 +53,8 @@ def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) ->
The absolute Unix time in milliseconds at which the identifier was
generated.
"""
# TODO: реализуйте функцию
return 0
current_millis = snowflake_id >> TIMESTAMP_SHIFT
return epoch_ms + current_millis


def decode_node_id(snowflake_id: int) -> int:
Expand All @@ -64,8 +67,7 @@ def decode_node_id(snowflake_id: int) -> int:
The node identifier packed into ``snowflake_id``, in the range
``[0, NODE_ID_MAX]``.
"""
# TODO: реализуйте функцию
return 0
return snowflake_id >> NODE_SHIFT & NODE_ID_MAX


def decode_sequence_id(snowflake_id: int) -> int:
Expand All @@ -78,8 +80,7 @@ def decode_sequence_id(snowflake_id: int) -> int:
The per-millisecond sequence counter packed into ``snowflake_id``, in
the range ``[0, SEQUENCE_ID_MAX]``.
"""
# TODO: реализуйте функцию
return 0
return snowflake_id >> SEQUENCE_SHIFT & SEQUENCE_ID_MAX


def generate_snowflake_id(
Expand Down Expand Up @@ -110,5 +111,24 @@ def generate_snowflake_id(
timestamp field (roughly 69 years after ``epoch_ms``). In each of those
cases an explanatory message is printed to stdout first.
"""
# TODO: реализуйте функцию
return 0
if node_id > NODE_ID_MAX or node_id < 0:
print(f"node_id must be in [0, {NODE_ID_MAX}], got {node_id}")
return None

if sequence_id > SEQUENCE_ID_MAX or sequence_id < 0:
print(f"sequence_id must be in [0, {SEQUENCE_ID_MAX}], got {sequence_id}")
return None

current_millis = read_current_millis(epoch_ms)

if current_millis > TIMESTAMP_MS_MAX:
print("overflows")
return None

snowflake_id = (
current_millis << TIMESTAMP_SHIFT
| node_id << NODE_SHIFT
| sequence_id << SEQUENCE_SHIFT
)

return snowflake_id
123 changes: 115 additions & 8 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 @@ -22,9 +22,58 @@

type CartLine = tuple[int, int]

# 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,
) -> int | None:
"""Check cart for cart line.

The cart line for ``product_id`` is looked up. If there is none, return None
else return index of required cart line in cart.

Args:
cart: The cart to take the units from; modified in place on
success.
product_id: The identifier of the product to remove.

Returns:
True if cart line was found in cart, or False
"""
for i, cart_line in enumerate(cart):
if cart_line[LINE_PRODUCT_ID_INDEX] == product_id:
return i
return None


def change_cart_quantity(
cart: list[CartLine],
index: int,
quantity: int,
) -> CartLine | None:
"""Change cart quantity in cart line at ``index`` to ``quantity``.

Args:
cart: The cart to take the units from; modified in place on
success.
index: The index of cart line in cart (as index of list).
quantity: Number of units.

Returns:
The cart line for ``product_id`` after the changing quantity.
None if quantity = 0.
"""

if quantity:
updated_cart_line = (cart[index][LINE_PRODUCT_ID_INDEX], quantity)
cart[index] = updated_cart_line
return updated_cart_line

cart = cart[:index] + cart[index + 1 :]
return None


def add_to_cart(
Expand Down Expand Up @@ -53,8 +102,39 @@ def add_to_cart(
The cart line for ``product_id`` after the addition, or ``None``
when the product is unknown or the store cannot cover the request.
"""
# TODO: реализуйте функцию
return (0, 0)
search_product = read_product(storage, product_id)

if not search_product:
return None

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

update_product(
storage,
product_id,
(
search_product[NAME_INDEX],
search_product[PRICE_INDEX],
search_product[QUANTITY_INDEX] - quantity,
),
)

index_cart_line = find_cart_line(cart, product_id)

if not index_cart_line is None:
return change_cart_quantity(
cart, index_cart_line, cart[index_cart_line][LINE_QUANTITY_INDEX] + quantity
)

updated_cart_line = (product_id, quantity)

cart.append(updated_cart_line)

return updated_cart_line


def remove_from_cart(
Expand Down Expand Up @@ -85,5 +165,32 @@ def remove_from_cart(
zero means the line was dropped), or ``None`` when the cart has no
line for the product or holds too few units.
"""
# TODO: реализуйте функцию
return (0, 0)
index_cart_line = find_cart_line(cart, product_id)

if index_cart_line is None:
print(f"product {product_id} is not in the cart")
return None

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

search_product = read_product(storage, product_id)

if not search_product:
return None

update_product(
storage,
product_id,
(
search_product[NAME_INDEX],
search_product[PRICE_INDEX],
search_product[QUANTITY_INDEX] + quantity,
),
)

return change_cart_quantity(cart, index_cart_line, cart_line_quantity - quantity)
53 changes: 41 additions & 12 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 @@ -34,8 +34,10 @@ def generate_product_id(storage: list[Product]) -> int:
:data:`~src.part2.storage.PRODUCT_ID_MIN` when ``storage`` is
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 @@ -56,8 +58,18 @@ def create_product(
``storage`` is left unchanged and a message naming the clashing
name is printed.
"""
# TODO: реализуйте функцию
return 0
name, price, quantity = fields
product_names = [product[NAME_INDEX] for product in storage]

if name in product_names:
print(f"product name {name} is already taken.")
return None

product_id = generate_product_id(storage)

storage.append((product_id, name, normalize_price(price), quantity))

return product_id


def read_product(storage: list[Product], product_id: int) -> Product | None:
Expand All @@ -72,8 +84,12 @@ def read_product(storage: list[Product], product_id: int) -> Product | None:
``None`` when no product carries that identifier (a message is
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 @@ -99,8 +115,16 @@ def update_product(
``None`` when no product carries that identifier (``storage`` is
left unchanged and a message is printed).
"""
# TODO: реализуйте функцию
return (0, "", Decimal(0), 0)
name, price, quantity = fields

for i, product in enumerate(storage):
if product[PRODUCT_ID_INDEX] == product_id:
updated_product = (product_id, name, normalize_price(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 @@ -116,5 +140,10 @@ def delete_product(storage: list[Product], product_id: int) -> int | None:
product carried that identifier (``storage`` is left unchanged and
a message is printed).
"""
# TODO: реализуйте функцию
return 0
for i, product in enumerate(storage):
if product[PRODUCT_ID_INDEX] == product_id:
storage = storage[:i] + storage[i + 1 :]
return product_id

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

type Product = tuple[int, str, Decimal, int]

# 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
Loading
Loading