From d7c88f38f9a06a724c401dbdf5e6db09bf297197 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 25 Sep 2026 21:48:48 +0300 Subject: [PATCH 01/24] Solve task 1 --- src/part1/constants.py | 48 ++++------------ src/part1/snowflake.py | 122 +++++++---------------------------------- 2 files changed, 29 insertions(+), 141 deletions(-) diff --git a/src/part1/constants.py b/src/part1/constants.py index 9abfbba2..86e0499a 100644 --- a/src/part1/constants.py +++ b/src/part1/constants.py @@ -1,38 +1,10 @@ -"""Bit layout and default epoch for the Twitter Snowflake ID generator. - -A Snowflake identifier is a 63-bit positive integer packed as follows, from -the most significant bit downwards: - - 1 bit unused sign bit, always zero - 41 bits milliseconds elapsed since a custom epoch - 10 bits node identifier - 12 bits per-millisecond sequence counter - -The shift and mask constants should be derived from the bit widths so that -changing a width keeps everything else consistent. -""" - -from typing import Final - - -# TODO: замените заглушки (0) на корректные значения, см. TASK.md. -# Все константы, зависящие от ёмкостей *_BITS, должны вычисляться из них. - -# Twitter's original Snowflake epoch: 2010-11-04 01:42:54.657 UTC. -EPOCH_MS_DEFAULT: Final[int] = 0 - -# Node identifier used when the caller does not supply one. -NODE_ID_DEFAULT: Final[int] = 0 - -# Width of each field, in bits. -TIMESTAMP_BITS: Final[int] = 0 -NODE_ID_BITS: Final[int] = 0 -SEQUENCE_ID_BITS: Final[int] = 0 - -# Largest value each field can hold. -TIMESTAMP_MS_MAX: Final[int] = 0 -NODE_ID_MAX: Final[int] = 0 -SEQUENCE_ID_MAX: Final[int] = 0 - -# Здесь можно добавить собственные вспомогательные константы -# (например, сдвиги полей при сборке идентификатора). +EPOCH_MS_DEFAULT = 1288834974657 +NODE_ID_DEFAULT = 1 +TIMESTAMP_BITS = 41 +NODE_ID_BITS = 10 +SEQUENCE_ID_BITS = 12 +TIMESTAMP_MS_MAX = 2 ** TIMESTAMP_BITS - 1 +NODE_ID_MAX = 2 ** NODE_ID_BITS - 1 +SEQUENCE_ID_MAX = 2 ** SEQUENCE_ID_BITS - 1 +NODE_ID_SHIFT = SEQUENCE_ID_BITS +TIMESTAMP_SHIFT = SEQUENCE_ID_BITS + NODE_ID_BITS \ No newline at end of file diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index d3abf050..14dca85b 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -1,114 +1,30 @@ -"""Twitter Snowflake identifier generator. - -Snowflake produces roughly time-ordered 64-bit integer identifiers without -coordination between nodes. Each identifier packs the milliseconds elapsed -since a custom epoch, a node identifier and a per-millisecond sequence counter -into a single 63-bit positive integer (see ``constants`` for the layout). - -The public entry point is :func:`generate_snowflake_id`. It is stateless: the -caller passes the sequence counter on every call and is responsible for -advancing it within a millisecond and resetting it when the clock ticks over. - -Each packed field can be read back on its own with :func:`decode_timestamp_ms`, -:func:`decode_node_id` and :func:`decode_sequence_id`. -""" - -import time # noqa: F401 - -from .constants import ( # noqa: F401 - EPOCH_MS_DEFAULT, - NODE_ID_DEFAULT, - NODE_ID_MAX, - SEQUENCE_ID_MAX, - TIMESTAMP_MS_MAX, -) +import time +from constants import EPOCH_MS_DEFAULT ,NODE_ID_DEFAULT, TIMESTAMP_BITS ,NODE_ID_BITS , \ +SEQUENCE_ID_BITS ,TIMESTAMP_MS_MAX ,NODE_ID_MAX ,SEQUENCE_ID_MAX ,NODE_ID_SHIFT ,TIMESTAMP_SHIFT def read_current_millis(epoch_ms: int) -> int: - """Read the number of milliseconds elapsed since the custom epoch. - - Args: - epoch_ms: The custom epoch expressed as Unix milliseconds. - - Returns: - The count of whole milliseconds between ``epoch_ms`` and now. May be - 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: - """Read the timestamp field out of a Snowflake identifier. - - Args: - snowflake_id: An identifier produced against the same epoch. - epoch_ms: The epoch the identifier was generated against. Defaults to - the original Twitter epoch (2010-11-04 01:42:54.657 UTC). - - Returns: - The absolute Unix time in milliseconds at which the identifier was - generated. - """ - # TODO: реализуйте функцию - return 0 + return snowflake_id // (2 ** TIMESTAMP_SHIFT) + epoch_ms -def decode_node_id(snowflake_id: int) -> int: - """Read the node identifier field out of a Snowflake identifier. - - Args: - snowflake_id: An identifier produced by :func:`generate_snowflake_id`. - - Returns: - The node identifier packed into ``snowflake_id``, in the range - ``[0, NODE_ID_MAX]``. - """ - # TODO: реализуйте функцию - return 0 +def decode_node_id(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: + return (snowflake_id - ((decode_timestamp_ms(snowflake_id, epoch_ms) - epoch_ms) * 2 ** TIMESTAMP_SHIFT)) // (2 ** NODE_ID_SHIFT) def decode_sequence_id(snowflake_id: int) -> int: - """Read the sequence counter field out of a Snowflake identifier. - - Args: - snowflake_id: An identifier produced by :func:`generate_snowflake_id`. - - Returns: - The per-millisecond sequence counter packed into ``snowflake_id``, in - the range ``[0, SEQUENCE_ID_MAX]``. - """ - # TODO: реализуйте функцию - return 0 - - -def generate_snowflake_id( - sequence_id: int, - node_id: int = NODE_ID_DEFAULT, - epoch_ms: int = EPOCH_MS_DEFAULT, -) -> int | None: - """Build and return a Snowflake identifier for the current millisecond. - - The function is stateless: the caller passes the per-millisecond sequence - counter explicitly. It is the caller's responsibility to increment - ``sequence_id`` for identifiers minted within the same millisecond and to - reset it once the clock advances. - - Args: - sequence_id: The per-millisecond sequence counter, in the range - ``[0, SEQUENCE_ID_MAX]``. - node_id: The identifier of this node, in the range ``[0, NODE_ID_MAX]``. - Optional; defaults to ``NODE_ID_DEFAULT``. - epoch_ms: The start of the epoch as Unix milliseconds. Optional; - defaults to the original Twitter epoch (2010-11-04 01:42:54.657 - UTC). - - Returns: - The Snowflake identifier as a positive 63-bit integer, or ``None`` if - ``node_id`` is outside ``[0, NODE_ID_MAX]``, ``sequence_id`` is outside - ``[0, SEQUENCE_ID_MAX]``, or the elapsed time no longer fits in the - timestamp field (roughly 69 years after ``epoch_ms``). In each of those - cases an explanatory message is printed to stdout first. - """ - # TODO: реализуйте функцию - return 0 + return snowflake_id - ((snowflake_id // 2 ** NODE_ID_SHIFT) * (2 ** NODE_ID_SHIFT)) + + +def generate_snowflake_id(sequence_id: int, node_id: int = NODE_ID_DEFAULT, epoch_ms: int = EPOCH_MS_DEFAULT) -> int | None: + current_millis = read_current_millis(epoch_ms) + if current_millis < 0 or current_millis > TIMESTAMP_MS_MAX: + raise ValueError('Превышен максимальный лимит времени') + if node_id < 0 or node_id > NODE_ID_MAX: + raise ValueError('Node Id выходит за допустимые пределы') + if sequence_id < 0 or sequence_id > SEQUENCE_ID_MAX: + raise ValueError('Sequence Id выходит за допустимые пределы') + return (current_millis * (2 ** TIMESTAMP_SHIFT)) + (node_id * (2 ** NODE_ID_SHIFT) + sequence_id) \ No newline at end of file From 86c89c4bc3d387310bc242c0af8de47940514e49 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 25 Sep 2026 22:09:56 +0300 Subject: [PATCH 02/24] Solve task 1 --- src/part1/snowflake.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index 14dca85b..0d14a824 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -1,6 +1,14 @@ import time -from constants import EPOCH_MS_DEFAULT ,NODE_ID_DEFAULT, TIMESTAMP_BITS ,NODE_ID_BITS , \ -SEQUENCE_ID_BITS ,TIMESTAMP_MS_MAX ,NODE_ID_MAX ,SEQUENCE_ID_MAX ,NODE_ID_SHIFT ,TIMESTAMP_SHIFT + +from constants import ( + EPOCH_MS_DEFAULT, + NODE_ID_DEFAULT, + NODE_ID_MAX, + NODE_ID_SHIFT, + SEQUENCE_ID_MAX, + TIMESTAMP_MS_MAX, + TIMESTAMP_SHIFT, +) def read_current_millis(epoch_ms: int) -> int: @@ -22,9 +30,9 @@ def decode_sequence_id(snowflake_id: int) -> int: def generate_snowflake_id(sequence_id: int, node_id: int = NODE_ID_DEFAULT, epoch_ms: int = EPOCH_MS_DEFAULT) -> int | None: current_millis = read_current_millis(epoch_ms) if current_millis < 0 or current_millis > TIMESTAMP_MS_MAX: - raise ValueError('Превышен максимальный лимит времени') + raise ValueError("Превышен максимальный лимит времени") if node_id < 0 or node_id > NODE_ID_MAX: - raise ValueError('Node Id выходит за допустимые пределы') + raise ValueError("Node Id выходит за допустимые пределы") if sequence_id < 0 or sequence_id > SEQUENCE_ID_MAX: - raise ValueError('Sequence Id выходит за допустимые пределы') + raise ValueError("Sequence Id выходит за допустимые пределы") return (current_millis * (2 ** TIMESTAMP_SHIFT)) + (node_id * (2 ** NODE_ID_SHIFT) + sequence_id) \ No newline at end of file From 7bab98912b921a3534229157789ad5614e12798e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 25 Sep 2026 22:17:25 +0300 Subject: [PATCH 03/24] Solve task 1 --- src/part1/constants.py | 8 ++++---- src/part1/snowflake.py | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/part1/constants.py b/src/part1/constants.py index 86e0499a..faee44a5 100644 --- a/src/part1/constants.py +++ b/src/part1/constants.py @@ -3,8 +3,8 @@ TIMESTAMP_BITS = 41 NODE_ID_BITS = 10 SEQUENCE_ID_BITS = 12 -TIMESTAMP_MS_MAX = 2 ** TIMESTAMP_BITS - 1 -NODE_ID_MAX = 2 ** NODE_ID_BITS - 1 -SEQUENCE_ID_MAX = 2 ** SEQUENCE_ID_BITS - 1 +TIMESTAMP_MS_MAX = 2**TIMESTAMP_BITS - 1 +NODE_ID_MAX = 2**NODE_ID_BITS - 1 +SEQUENCE_ID_MAX = 2**SEQUENCE_ID_BITS - 1 NODE_ID_SHIFT = SEQUENCE_ID_BITS -TIMESTAMP_SHIFT = SEQUENCE_ID_BITS + NODE_ID_BITS \ No newline at end of file +TIMESTAMP_SHIFT = SEQUENCE_ID_BITS + NODE_ID_BITS diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index 0d14a824..19a4f087 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -16,18 +16,26 @@ def read_current_millis(epoch_ms: int) -> int: def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: - return snowflake_id // (2 ** TIMESTAMP_SHIFT) + epoch_ms + return snowflake_id // (2**TIMESTAMP_SHIFT) + epoch_ms def decode_node_id(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: - return (snowflake_id - ((decode_timestamp_ms(snowflake_id, epoch_ms) - epoch_ms) * 2 ** TIMESTAMP_SHIFT)) // (2 ** NODE_ID_SHIFT) + return ( + snowflake_id + - ( + (decode_timestamp_ms(snowflake_id, epoch_ms) - epoch_ms) + * 2**TIMESTAMP_SHIFT + ) + ) // (2**NODE_ID_SHIFT) def decode_sequence_id(snowflake_id: int) -> int: - return snowflake_id - ((snowflake_id // 2 ** NODE_ID_SHIFT) * (2 ** NODE_ID_SHIFT)) + return snowflake_id - ((snowflake_id // 2**NODE_ID_SHIFT) * (2**NODE_ID_SHIFT)) -def generate_snowflake_id(sequence_id: int, node_id: int = NODE_ID_DEFAULT, epoch_ms: int = EPOCH_MS_DEFAULT) -> int | None: +def generate_snowflake_id( + sequence_id: int, node_id: int = NODE_ID_DEFAULT, epoch_ms: int = EPOCH_MS_DEFAULT +) -> int | None: current_millis = read_current_millis(epoch_ms) if current_millis < 0 or current_millis > TIMESTAMP_MS_MAX: raise ValueError("Превышен максимальный лимит времени") @@ -35,4 +43,6 @@ def generate_snowflake_id(sequence_id: int, node_id: int = NODE_ID_DEFAULT, epoc raise ValueError("Node Id выходит за допустимые пределы") if sequence_id < 0 or sequence_id > SEQUENCE_ID_MAX: raise ValueError("Sequence Id выходит за допустимые пределы") - return (current_millis * (2 ** TIMESTAMP_SHIFT)) + (node_id * (2 ** NODE_ID_SHIFT) + sequence_id) \ No newline at end of file + return (current_millis * (2**TIMESTAMP_SHIFT)) + ( + node_id * (2**NODE_ID_SHIFT) + sequence_id + ) From c552b886236370d819d7c344e0a85dc71c19724d Mon Sep 17 00:00:00 2001 From: phantom686 Date: Fri, 25 Sep 2026 22:27:53 +0300 Subject: [PATCH 04/24] Update snowflake.py --- src/part1/snowflake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index 19a4f087..ae1e7ee5 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -1,6 +1,6 @@ import time -from constants import ( +from .constants import ( EPOCH_MS_DEFAULT, NODE_ID_DEFAULT, NODE_ID_MAX, From 1e23410bc949fbef2638a3e25b21ffcf061e79a8 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Fri, 25 Sep 2026 22:35:25 +0300 Subject: [PATCH 05/24] Update constants.py --- src/part1/constants.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/part1/constants.py b/src/part1/constants.py index faee44a5..960e4b82 100644 --- a/src/part1/constants.py +++ b/src/part1/constants.py @@ -1,10 +1,10 @@ -EPOCH_MS_DEFAULT = 1288834974657 -NODE_ID_DEFAULT = 1 -TIMESTAMP_BITS = 41 -NODE_ID_BITS = 10 -SEQUENCE_ID_BITS = 12 -TIMESTAMP_MS_MAX = 2**TIMESTAMP_BITS - 1 -NODE_ID_MAX = 2**NODE_ID_BITS - 1 -SEQUENCE_ID_MAX = 2**SEQUENCE_ID_BITS - 1 -NODE_ID_SHIFT = SEQUENCE_ID_BITS -TIMESTAMP_SHIFT = SEQUENCE_ID_BITS + NODE_ID_BITS +EPOCH_MS_DEFAULT: int = 1288834974657 +NODE_ID_DEFAULT: int = 1 +TIMESTAMP_BITS: int = 41 +NODE_ID_BITS: int = 10 +SEQUENCE_ID_BITS: int = 12 +TIMESTAMP_MS_MAX: int = 2**TIMESTAMP_BITS - 1 +NODE_ID_MAX: int = 2**NODE_ID_BITS - 1 +SEQUENCE_ID_MAX: int = 2**SEQUENCE_ID_BITS - 1 +NODE_ID_SHIFT: int = SEQUENCE_ID_BITS +TIMESTAMP_SHIFT: int = SEQUENCE_ID_BITS + NODE_ID_BITS From 3a6dc3dfa928505efc4315c091d3699c0de1bf4a Mon Sep 17 00:00:00 2001 From: phantom686 Date: Fri, 25 Sep 2026 22:40:01 +0300 Subject: [PATCH 06/24] Update snowflake.py --- src/part1/snowflake.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index ae1e7ee5..80808ec6 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -12,25 +12,25 @@ def read_current_millis(epoch_ms: int) -> int: - return int(time.time() * 1000) - epoch_ms + return int(int(time.time() * 1000) - epoch_ms) def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: - return snowflake_id // (2**TIMESTAMP_SHIFT) + epoch_ms + return int(snowflake_id // (2**TIMESTAMP_SHIFT) + epoch_ms) def decode_node_id(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: - return ( + return int(( snowflake_id - ( (decode_timestamp_ms(snowflake_id, epoch_ms) - epoch_ms) * 2**TIMESTAMP_SHIFT ) - ) // (2**NODE_ID_SHIFT) + ) // (2**NODE_ID_SHIFT)) def decode_sequence_id(snowflake_id: int) -> int: - return snowflake_id - ((snowflake_id // 2**NODE_ID_SHIFT) * (2**NODE_ID_SHIFT)) + return int(snowflake_id - ((snowflake_id // 2**NODE_ID_SHIFT) * (2**NODE_ID_SHIFT))) def generate_snowflake_id( @@ -43,6 +43,6 @@ def generate_snowflake_id( raise ValueError("Node Id выходит за допустимые пределы") if sequence_id < 0 or sequence_id > SEQUENCE_ID_MAX: raise ValueError("Sequence Id выходит за допустимые пределы") - return (current_millis * (2**TIMESTAMP_SHIFT)) + ( + return int((current_millis * (2**TIMESTAMP_SHIFT)) + ( node_id * (2**NODE_ID_SHIFT) + sequence_id - ) + )) From edd79d85900a747996b88077776f8c82fac6f9c3 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 25 Sep 2026 22:49:29 +0300 Subject: [PATCH 07/24] Solve task 1 --- pl-001-python-basics-task | 1 + src/part1/snowflake.py | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) create mode 160000 pl-001-python-basics-task diff --git a/pl-001-python-basics-task b/pl-001-python-basics-task new file mode 160000 index 00000000..3a6dc3df --- /dev/null +++ b/pl-001-python-basics-task @@ -0,0 +1 @@ +Subproject commit 3a6dc3dfa928505efc4315c091d3699c0de1bf4a diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index 19a4f087..8fef484d 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -1,12 +1,14 @@ import time - -from constants import ( +from .constants import ( EPOCH_MS_DEFAULT, NODE_ID_DEFAULT, + TIMESTAMP_BITS, + NODE_ID_BITS, + SEQUENCE_ID_BITS, + TIMESTAMP_MS_MAX, NODE_ID_MAX, - NODE_ID_SHIFT, SEQUENCE_ID_MAX, - TIMESTAMP_MS_MAX, + NODE_ID_SHIFT, TIMESTAMP_SHIFT, ) From d173d13d14552d4914e10aa5deb2d83e1979a729 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Fri, 25 Sep 2026 23:16:30 +0300 Subject: [PATCH 08/24] Update snowflake.py --- src/part1/snowflake.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index e2f93e26..b3559d66 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -1,8 +1,4 @@ import time -<<<<<<< HEAD -======= - ->>>>>>> 3a6dc3dfa928505efc4315c091d3699c0de1bf4a from .constants import ( EPOCH_MS_DEFAULT, NODE_ID_DEFAULT, From d6563495528fe1bc8ceb96ec961604b3a7e32abe Mon Sep 17 00:00:00 2001 From: phantom686 Date: Fri, 25 Sep 2026 23:19:57 +0300 Subject: [PATCH 09/24] Update snowflake.py --- src/part1/snowflake.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index b3559d66..f9bdf778 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -2,9 +2,6 @@ from .constants import ( EPOCH_MS_DEFAULT, NODE_ID_DEFAULT, - TIMESTAMP_BITS, - NODE_ID_BITS, - SEQUENCE_ID_BITS, TIMESTAMP_MS_MAX, NODE_ID_MAX, SEQUENCE_ID_MAX, From ff338aabc03a2e7b958e079293064586d3c0437c Mon Sep 17 00:00:00 2001 From: phantom686 Date: Fri, 25 Sep 2026 23:25:57 +0300 Subject: [PATCH 10/24] Update snowflake.py --- src/part1/snowflake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index f9bdf778..6518cb38 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -2,10 +2,10 @@ from .constants import ( EPOCH_MS_DEFAULT, NODE_ID_DEFAULT, - TIMESTAMP_MS_MAX, NODE_ID_MAX, - SEQUENCE_ID_MAX, NODE_ID_SHIFT, + SEQUENCE_ID_MAX, + TIMESTAMP_MS_MAX, TIMESTAMP_SHIFT, ) From bf59011930c5c7b174a582391fc83a1ede03085b Mon Sep 17 00:00:00 2001 From: phantom686 Date: Fri, 25 Sep 2026 23:29:12 +0300 Subject: [PATCH 11/24] Update snowflake.py --- src/part1/snowflake.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index 6518cb38..80808ec6 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -1,4 +1,5 @@ import time + from .constants import ( EPOCH_MS_DEFAULT, NODE_ID_DEFAULT, From 66427ec52008d2bcc2c7fd329af847fcf8b63da3 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 25 Sep 2026 23:37:34 +0300 Subject: [PATCH 12/24] jj --- src/part1/snowflake.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index 80808ec6..01d46be6 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -20,13 +20,16 @@ def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> def decode_node_id(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: - return int(( - snowflake_id - - ( - (decode_timestamp_ms(snowflake_id, epoch_ms) - epoch_ms) - * 2**TIMESTAMP_SHIFT + return int( + ( + snowflake_id + - ( + (decode_timestamp_ms(snowflake_id, epoch_ms) - epoch_ms) + * 2**TIMESTAMP_SHIFT + ) ) - ) // (2**NODE_ID_SHIFT)) + // (2**NODE_ID_SHIFT) + ) def decode_sequence_id(snowflake_id: int) -> int: @@ -43,6 +46,7 @@ def generate_snowflake_id( raise ValueError("Node Id выходит за допустимые пределы") if sequence_id < 0 or sequence_id > SEQUENCE_ID_MAX: raise ValueError("Sequence Id выходит за допустимые пределы") - return int((current_millis * (2**TIMESTAMP_SHIFT)) + ( - node_id * (2**NODE_ID_SHIFT) + sequence_id - )) + return int( + (current_millis * (2**TIMESTAMP_SHIFT)) + + (node_id * (2**NODE_ID_SHIFT) + sequence_id) + ) From 1409ca9ee5e4c9ca67112e0d8cf061d9aa469fb6 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 14:21:48 +0300 Subject: [PATCH 13/24] fix --- pl-001-python-basics-task | 1 - 1 file changed, 1 deletion(-) delete mode 160000 pl-001-python-basics-task diff --git a/pl-001-python-basics-task b/pl-001-python-basics-task deleted file mode 160000 index 3a6dc3df..00000000 --- a/pl-001-python-basics-task +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3a6dc3dfa928505efc4315c091d3699c0de1bf4a From 3a4c445e2df47d5a3eba6ba3124c1c6bb035985e Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 19:56:43 +0300 Subject: [PATCH 14/24] task3 --- src/part3/crud.py | 154 ++++++++++++++--------------------------- src/part3/run_admin.py | 133 ++++++++++++++++------------------- src/part3/storage.py | 32 ++------- src/part3/utils.py | 117 ++++++++++--------------------- 4 files changed, 151 insertions(+), 285 deletions(-) diff --git a/src/part3/crud.py b/src/part3/crud.py index 68811d7e..1ae158ea 100644 --- a/src/part3/crud.py +++ b/src/part3/crud.py @@ -1,23 +1,3 @@ -"""Create/read/update/delete operations over the in-memory product store. - -Every operation takes the store -- a list of -:data:`~src.part3.storage.Product` tuples -- as its first argument and -works on it in place. The failure path never raises: the operation prints -an explanatory message to stdout and returns ``None``. - -The identifier of a new product is derived from the store itself -(:func:`generate_product_id`): one past the greatest identifier in use, or -:data:`~src.part3.storage.PRODUCT_ID_MIN` when the store is empty. - -Every product name that reaches the store is passed through -:func:`~src.part3.utils.normalize_product_name` first. A name that -normalises to an empty string is rejected -- the operation prints a -message and changes nothing. Names are also kept unique -- -:func:`create_product` refuses a normalised name that is already taken, -and :func:`update_product` refuses one that is already taken by another -product. -""" - from decimal import Decimal from .storage import ( # noqa: F401 @@ -25,63 +5,42 @@ PRODUCT_ID_INDEX, PRODUCT_ID_MIN, Product, + PRICE_INDEX, + QUANTITY_INDEX, ) -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: - """Choose the identifier for the next product added to ``storage``. - - Args: - storage: The product store to inspect. - - Returns: - One past the greatest identifier currently held in ``storage``, or - :data:`~src.part3.storage.PRODUCT_ID_MIN` when ``storage`` is - empty. - """ - # TODO: реализуйте функцию (используйте своё решение части 2) - return 0 + if not storage: + return PRODUCT_ID_MIN + return max(p[PRODUCT_ID_INDEX] for p in storage) + 1 def create_product( storage: list[Product], fields: tuple[str, Decimal, int] ) -> int | None: - """Append a new product to ``storage`` and return its new identifier. - - Args: - storage: The product store to append to; modified in place on - success. - fields: A ``(name, price, quantity)`` tuple describing the product. - ``name`` is normalised (surrounding whitespace stripped, - lower-cased) before it is stored or compared. ``price`` is a - :class:`~decimal.Decimal` amount and is rounded to the stored - money precision before it is saved. - - Returns: - The identifier generated for the new product, or ``None`` when - ``name`` is blank once normalised or a product with the same - normalised name already exists. In the ``None`` case ``storage`` - is left unchanged and an explanatory message is printed. - """ - # TODO: реализуйте функцию - return 0 + raw_name, price, qty = fields + name = normalize_product_name(raw_name) + if not name: + print("product name must not be blank") + return None + for p in storage: + if p[NAME_INDEX] == name: + print(f"product name '{name}' is already taken") + return None + new_id = generate_product_id(storage) + norm_price = normalize_price(price) + storage.append((new_id, name, norm_price, qty)) + return new_id def read_product(storage: list[Product], product_id: int) -> Product | None: - """Return the product stored under ``product_id``. - - Args: - storage: The product store to search. - product_id: The identifier to look up. - - Returns: - The matching ``(product_id, name, price, quantity)`` record, or - ``None`` when no product carries that identifier (a message is - printed in that case). - """ - # TODO: реализуйте функцию (используйте своё решение части 2) - return (0, "", Decimal(0), 0) + for p in storage: + if p[PRODUCT_ID_INDEX] == product_id: + return p + print(f"no product with id {product_id}") + return None def update_product( @@ -89,43 +48,34 @@ def update_product( product_id: int, fields: tuple[str, Decimal, int], ) -> Product | None: - """Overwrite the fields of the product stored under ``product_id``. - - The identifier itself is preserved; only ``name``, ``price`` and - ``quantity`` are replaced. - - Args: - storage: The product store to modify; the matching record is - replaced in place on success. - product_id: The identifier of the product to change. - fields: A ``(name, price, quantity)`` tuple with the new values. - ``price`` is a :class:`~decimal.Decimal` amount and is rounded - to the stored money precision before it is saved. - - Returns: - The updated ``(product_id, name, price, quantity)`` record, or - ``None`` when ``name`` is blank once normalised, no product - carries that identifier, or the normalised name is already taken - by another product. In every ``None`` case ``storage`` is left - unchanged and an explanatory message is printed. Renaming a - product to its own current name is allowed. - """ - # TODO: реализуйте функцию - return (0, "", Decimal(0), 0) + raw_name, price, qty = fields + name = normalize_product_name(raw_name) + if not name: + print("product name must not be blank") + return None + target_idx = -1 + + for i, p in enumerate(storage): + if p[PRODUCT_ID_INDEX] == product_id: + target_idx = i + break + if target_idx == -1: + print(f"no product with id {product_id}") + return None + + for p in storage: + if p[NAME_INDEX] == name and p[PRODUCT_ID_INDEX] != product_id: + print(f"product name '{name}' is already taken") + return None + norm_price = normalize_price(price) + update_product = (product_id, name, norm_price, qty) + storage[target_idx] = update_product + return update_product def delete_product(storage: list[Product], product_id: int) -> int | None: - """Remove the product stored under ``product_id`` from ``storage``. - - Args: - storage: The product store to remove from; modified in place on - success. - product_id: The identifier of the product to remove. - - Returns: - ``product_id`` when a product was removed, or ``None`` when no - product carried that identifier (``storage`` is left unchanged and - a message is printed). - """ - # TODO: реализуйте функцию (используйте своё решение части 2) - return 0 + for i, p in enumerate(storage): + if p[PRODUCT_ID_INDEX] == product_id: + storage.pop(i) + return product_id + return None diff --git a/src/part3/run_admin.py b/src/part3/run_admin.py index c5feeb0b..5221dc85 100644 --- a/src/part3/run_admin.py +++ b/src/part3/run_admin.py @@ -1,95 +1,82 @@ -"""Interactive admin console for the part3 in-memory product store. - -Running this module starts a read-eval-print loop over a single product -store that lives in memory for the lifetime of the process. Every line -typed at the prompt is stripped of surrounding whitespace and matched -against the set of known commands: - -* ``help`` -- print the list of commands and what each one does; -* ``exit`` -- leave the loop and end the program; -* ``show`` -- print the whole store as a text table; -* ``create `` -- add a product; -* ``read `` -- print one product; -* ``update `` -- overwrite a product; -* ``delete `` -- remove a product. - -For ``create`` and ``update`` the price and quantity are always the last -two words of the line; everything between the verb (and, for ``update``, -the id) and those two words is joined with single spaces into the -product name, so a multi-word name such as ``Gibson SG Junior`` needs no -quoting. - -The four CRUD commands forward to :mod:`src.part3.crud`; whatever -that call returns is printed unless it is ``None``. A line whose first -word is not a known verb, or that carries the wrong number of arguments -for its verb, prints an "is not a command" notice. A line that names a -command correctly but whose id, price or quantity argument will not -parse prints a distinct "arguments are invalid" notice. Either way the -store 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 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 add a product, print its new id + read print the product with that id + update overwrite that product's fields + delete 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: - """Print the command reference to stdout. - The text is the module-level :data:`HELP_TEXT` constant, printed as - is: a heading, one line per command with a short description, and a - note on how multi-word names are parsed. - """ - # TODO: реализуйте функцию +def show_help() -> None: + print(HELP_TEXT) def print_result(result: object) -> None: - """Print a CRUD result to stdout unless it is ``None``. - - Args: - result: The value returned by a CRUD operation. ``None`` means the - operation already reported its own failure, so nothing is - printed in that case. - """ - # TODO: реализуйте функцию + if result is not None: + print(result) def run_command(storage: list[Product], line: str) -> bool: - """Parse one console line and carry out the command it names. - - Args: - storage: The product store shared across the session; mutated in - place by the ``create``, ``update`` and ``delete`` commands. - line: One line of console input, already stripped of surrounding - whitespace. - - Returns: - ``True`` to keep the read-eval-print loop running, or ``False`` - once the ``exit`` command has been seen. A line that is not a - known command prints a notice and still returns ``True``. - - Raises: - ValueError: If an id or quantity argument of a CRUD command does - not parse as a base-10 integer. - decimal.InvalidOperation: If the price argument of ``create`` or - ``update`` does not parse as a decimal number. - """ - # TODO: реализуйте функцию - return False + parts = line.split() + if not parts: + print(f"'{line}' is not a command") + return True + cmd = parts[0] + + if cmd == "help" and len(parts) == 1: + show_help() + return True + elif cmd == "exit" and len(parts) == 1: + return False + elif cmd == "show" and len(parts) == 1: + print(get_storage_str_representation(storage)) + return True + elif cmd == "read" and len(parts) == 2: + pid = int(parts[1]) + res = read_product(storage, pid) + print_result(res) + return True + elif cmd == "delete" and len(parts) == 2: + pid = int(parts[1]) + res = delete_product(storage, pid) + print_result(res) + return True + elif cmd == "create" and len(parts) >= 4: + qty = int(parts[-1]) + price = Decimal(parts[-2]) + name = " ".join(parts[1:-2]) + res = create_product(storage, (name, price, qty)) + print_result(res) + return True + elif cmd == "update" and len(parts) >= 5: + pid = int(parts[1]) + qty = int(parts[-1]) + price = Decimal(parts[-2]) + name = " ".join(parts[2:-2]) + res = update_product(storage, pid, (name, price, qty)) + print_result(res) + return True + print(f"'{line}' is not a command") + return True def main() -> None: diff --git a/src/part3/storage.py b/src/part3/storage.py index c5e34be5..da3baee7 100644 --- a/src/part3/storage.py +++ b/src/part3/storage.py @@ -1,35 +1,11 @@ -"""In-memory product store for the shop CRUD exercise. - -The store is deliberately primitive: a plain list of tuples, with no -persistence, no indexing and no schema enforcement. Each element is a -:data:`Product` record -- a ``(product_id, name, price, quantity)`` tuple -where: - -* ``product_id`` -- unique positive integer key, assigned by the create - operation in :mod:`src.part3.crud`; -* ``name`` -- human-readable product name, unique across the store; -* ``price`` -- price of a single unit, held as a :class:`~decimal.Decimal` - amount rounded to a fixed number of decimal places (see - :func:`src.part3.utils.normalize_price`) so that money is never - subject to binary floating-point error; -* ``quantity`` -- number of units currently in stock. - -Field positions are exposed as ``*_INDEX`` constants so callers never -hard-code tuple offsets. -""" - from decimal import Decimal from typing import Final - type Product = tuple[int, str, Decimal, int] -# 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 diff --git a/src/part3/utils.py b/src/part3/utils.py index 318ded0b..77ff0af2 100644 --- a/src/part3/utils.py +++ b/src/part3/utils.py @@ -1,95 +1,48 @@ -"""Standalone helpers shared by the part3 storage and CRUD modules. - -Normalisation: - -* :func:`normalize_price` rounds a raw :class:`~decimal.Decimal` amount to - the fixed number of fractional digits (:data:`PRICE_PRECISION`) that - every stored price uses, keeping currency values free of binary - floating-point error. -* :func:`normalize_product_name` strips surrounding whitespace from a - product name, collapses every internal run of whitespace to a single - space and lower-cases it, giving every stored name a single canonical - form. - -Presentation: - -* :func:`get_storage_str_representation` renders the whole store as a - text table meant to be printed to a terminal; each column is sized to - 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 Product - +from .storage import Product, PRODUCT_ID_INDEX, NAME_INDEX, PRICE_INDEX, QUANTITY_INDEX -# Number of fractional digits every stored price is rounded to. -# Quantisation step derived from PRICE_PRECISION, e.g. Decimal("0.01"). -# TODO: задайте число знаков после запятой и шаг квантования (используйте своё -# решение части 2) -PRICE_PRECISION: Final[int] = 0 -PRICE_STEP: Final = Decimal(0) +PRICE_PRECISION: Final[int] = 2 +PRICE_STEP: Final = 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: задайте заголовки столбцов таблицы -TABLE_HEADERS: Final[tuple[str, ...]] = () +TABLE_HEADERS: Final = ("ID", "name", "price", "quantity") def normalize_price(price: Decimal) -> Decimal: - """Round a price to the precision every stored record uses. - - Args: - price: The raw price amount. - - Returns: - ``price`` quantised to :data:`PRICE_PRECISION` fractional digits, - with halves rounded up. - """ - # TODO: реализуйте функцию (используйте своё решение части 2) - return Decimal(0) + return price.quantize(PRICE_STEP, rounding=ROUND_HALF_UP) def normalize_product_name(name: str) -> str: - """Reduce a product name to the canonical form the store keeps. - - Args: - name: The raw product name. - - Returns: - ``name`` with every leading and trailing whitespace character - removed, every internal run of whitespace (spaces, tabs and the - like) collapsed to a single space, and the rest lower-cased. For - example ``"abc def\tghi\tjkl"`` becomes ``"abc def ghi jkl"``. - The result is an empty string when ``name`` holds nothing but - whitespace. - """ - # TODO: реализуйте функцию - return "" - - -# TODO: при необходимости добавьте свои вспомогательные функции + return " ".join(name.split()).lower() def get_storage_str_representation(storage: list[Product]) -> str: - """Render the product store as a text table with data-sized columns. - - The table has the columns named by :data:`TABLE_HEADERS` -- id, name, - price and quantity. Each column is made exactly as wide as the longest - value it carries in this call (its header counted), so the columns - line up when the string is printed to a terminal and no value is ever - truncated. - - Args: - storage: The product store to render. - - Returns: - A multi-line string: a header row, a dashed separator row, then - one row per product in ``storage`` in list order. When ``storage`` - is empty only the header and separator rows are returned, sized to - the header labels. - """ - # TODO: реализуйте функцию - return "" + w_id = len(TABLE_HEADERS[0]) + w_name = len(TABLE_HEADERS[1]) + w_price = len(TABLE_HEADERS[2]) + w_qty = len(TABLE_HEADERS[3]) + rows = [] + for p in storage: + pid_str = str(p[PRODUCT_ID_INDEX]) + name_str = str(p[NAME_INDEX]) + price_str = str(p[PRICE_INDEX]) + qty_str = str(p[QUANTITY_INDEX]) + rows.append((pid_str, name_str, price_str, qty_str)) + w_id = max(w_id, len(pid_str)) + w_name = max(w_name, len(name_str)) + w_price = max(w_price, len(price_str)) + w_qty = max(w_qty, len(qty_str)) + + def format_row(c1: str, c2: str, c3: str, c4: str) -> str: + return f"| {c1:<{w_id}} | {c2:<{w_name}} | {c3:<{w_price}} | {c4:<{w_qty}} |" + + lines = [] + lines.append(format_row(*TABLE_HEADERS)) + sep = f"|-{'-' * w_id}-|-{'-' * w_name}-|-{'-' * w_price}-|-{'-' * w_qty}-|" + lines.append(sep) + + for p in rows: + lines.append(format_row(*p)) + + return "\n".join(lines) From 08d9d76ea406a4b374587cfcb5e754306d90b8bb Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:11:37 +0300 Subject: [PATCH 15/24] fix --- src/part3/crud.py | 4 ++-- src/part3/storage.py | 1 + src/part3/utils.py | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/part3/crud.py b/src/part3/crud.py index 1ae158ea..eee89082 100644 --- a/src/part3/crud.py +++ b/src/part3/crud.py @@ -2,11 +2,11 @@ from .storage import ( # noqa: F401 NAME_INDEX, + PRICE_INDEX, PRODUCT_ID_INDEX, PRODUCT_ID_MIN, - Product, - PRICE_INDEX, QUANTITY_INDEX, + Product, ) from .utils import normalize_price, normalize_product_name diff --git a/src/part3/storage.py b/src/part3/storage.py index da3baee7..81651a75 100644 --- a/src/part3/storage.py +++ b/src/part3/storage.py @@ -1,6 +1,7 @@ from decimal import Decimal from typing import Final + type Product = tuple[int, str, Decimal, int] PRODUCT_ID_INDEX: Final = 0 diff --git a/src/part3/utils.py b/src/part3/utils.py index 77ff0af2..ac342f09 100644 --- a/src/part3/utils.py +++ b/src/part3/utils.py @@ -1,7 +1,8 @@ from decimal import ROUND_HALF_UP, Decimal from typing import Final -from .storage import Product, PRODUCT_ID_INDEX, NAME_INDEX, PRICE_INDEX, QUANTITY_INDEX +from .storage import NAME_INDEX, PRICE_INDEX, PRODUCT_ID_INDEX, QUANTITY_INDEX, Product + PRICE_PRECISION: Final[int] = 2 PRICE_STEP: Final = Decimal(1).scaleb(-PRICE_PRECISION) From a95ff21792702df040348f558de816bd32ba4e35 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:24:44 +0300 Subject: [PATCH 16/24] fixmypy --- src/part3/crud.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/part3/crud.py b/src/part3/crud.py index eee89082..3fde966e 100644 --- a/src/part3/crud.py +++ b/src/part3/crud.py @@ -1,11 +1,9 @@ from decimal import Decimal -from .storage import ( # noqa: F401 +from .storage import ( NAME_INDEX, - PRICE_INDEX, PRODUCT_ID_INDEX, PRODUCT_ID_MIN, - QUANTITY_INDEX, Product, ) from .utils import normalize_price, normalize_product_name @@ -68,9 +66,9 @@ def update_product( print(f"product name '{name}' is already taken") return None norm_price = normalize_price(price) - update_product = (product_id, name, norm_price, qty) - storage[target_idx] = update_product - return update_product + update_tuple = (product_id, name, norm_price, qty) + storage[target_idx] = update_tuple + return update_tuple def delete_product(storage: list[Product], product_id: int) -> int | None: From 8e6d6efd5137c177c2e62e884a3aa2c100ddcd95 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:36:30 +0300 Subject: [PATCH 17/24] fix2 --- src/part3/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/part3/utils.py b/src/part3/utils.py index ac342f09..6ff42923 100644 --- a/src/part3/utils.py +++ b/src/part3/utils.py @@ -23,7 +23,7 @@ def get_storage_str_representation(storage: list[Product]) -> str: w_name = len(TABLE_HEADERS[1]) w_price = len(TABLE_HEADERS[2]) w_qty = len(TABLE_HEADERS[3]) - rows = [] + rows: list[tuple[str, str, str,str]] = [] for p in storage: pid_str = str(p[PRODUCT_ID_INDEX]) name_str = str(p[NAME_INDEX]) @@ -43,7 +43,7 @@ def format_row(c1: str, c2: str, c3: str, c4: str) -> str: sep = f"|-{'-' * w_id}-|-{'-' * w_name}-|-{'-' * w_price}-|-{'-' * w_qty}-|" lines.append(sep) - for p in rows: - lines.append(format_row(*p)) + for row in rows: + lines.append(format_row(*row)) return "\n".join(lines) From b7f46c322735fa5c1cdc81e8c9c9d9924f25be85 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:38:31 +0300 Subject: [PATCH 18/24] fix3 --- src/part3/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/part3/utils.py b/src/part3/utils.py index 6ff42923..19acacf1 100644 --- a/src/part3/utils.py +++ b/src/part3/utils.py @@ -23,7 +23,7 @@ def get_storage_str_representation(storage: list[Product]) -> str: w_name = len(TABLE_HEADERS[1]) w_price = len(TABLE_HEADERS[2]) w_qty = len(TABLE_HEADERS[3]) - rows: list[tuple[str, str, str,str]] = [] + rows: list[tuple[str, str, str, str]] = [] for p in storage: pid_str = str(p[PRODUCT_ID_INDEX]) name_str = str(p[NAME_INDEX]) From d8b84c2c03ea58782939c1b55179f26a641e2178 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:40:41 +0300 Subject: [PATCH 19/24] fix4 --- src/part3/run_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/part3/run_admin.py b/src/part3/run_admin.py index 5221dc85..93d2317e 100644 --- a/src/part3/run_admin.py +++ b/src/part3/run_admin.py @@ -57,14 +57,14 @@ def run_command(storage: list[Product], line: str) -> bool: return True elif cmd == "delete" and len(parts) == 2: pid = int(parts[1]) - res = delete_product(storage, pid) + res: tuple[int, str, Decimal, int] | None = delete_product(storage, pid) print_result(res) return True elif cmd == "create" and len(parts) >= 4: qty = int(parts[-1]) price = Decimal(parts[-2]) name = " ".join(parts[1:-2]) - res = create_product(storage, (name, price, qty)) + res: tuple[int, str, Decimal, int] | None = create_product(storage, (name, price, qty)) print_result(res) return True elif cmd == "update" and len(parts) >= 5: From a0d126ea5249015b08b6d7a646d3a0a6d083c675 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:42:32 +0300 Subject: [PATCH 20/24] fix5 --- src/part3/run_admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/part3/run_admin.py b/src/part3/run_admin.py index 93d2317e..e8729135 100644 --- a/src/part3/run_admin.py +++ b/src/part3/run_admin.py @@ -64,7 +64,9 @@ def run_command(storage: list[Product], line: str) -> bool: qty = int(parts[-1]) price = Decimal(parts[-2]) name = " ".join(parts[1:-2]) - res: tuple[int, str, Decimal, int] | None = create_product(storage, (name, price, qty)) + res: tuple[int, str, Decimal, int] | None = create_product( + storage, (name, price, qty) + ) print_result(res) return True elif cmd == "update" and len(parts) >= 5: From 6e1116bbf4b8d5a73c4c19e3e77bc5a62ed28222 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:45:01 +0300 Subject: [PATCH 21/24] fix6 --- src/part3/run_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/part3/run_admin.py b/src/part3/run_admin.py index e8729135..bf73a79f 100644 --- a/src/part3/run_admin.py +++ b/src/part3/run_admin.py @@ -52,7 +52,7 @@ def run_command(storage: list[Product], line: str) -> bool: return True elif cmd == "read" and len(parts) == 2: pid = int(parts[1]) - res = read_product(storage, pid) + res: tuple[int, str, Decimal, int] | None = read_product(storage, pid) print_result(res) return True elif cmd == "delete" and len(parts) == 2: From 563e06e03be3446555ab73bcf3e4dc43fef040e2 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:49:19 +0300 Subject: [PATCH 22/24] fix7 --- src/part3/run_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/part3/run_admin.py b/src/part3/run_admin.py index bf73a79f..74976bda 100644 --- a/src/part3/run_admin.py +++ b/src/part3/run_admin.py @@ -57,14 +57,14 @@ def run_command(storage: list[Product], line: str) -> bool: return True elif cmd == "delete" and len(parts) == 2: pid = int(parts[1]) - res: tuple[int, str, Decimal, int] | None = delete_product(storage, pid) + res = delete_product(storage, pid) print_result(res) return True elif cmd == "create" and len(parts) >= 4: qty = int(parts[-1]) price = Decimal(parts[-2]) name = " ".join(parts[1:-2]) - res: tuple[int, str, Decimal, int] | None = create_product( + res = create_product( storage, (name, price, qty) ) print_result(res) From 23a9f6e72b6032c4bd1855360c80e7e34bfb13ff Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 20:51:19 +0300 Subject: [PATCH 23/24] fix8 --- src/part3/run_admin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/part3/run_admin.py b/src/part3/run_admin.py index 74976bda..89f07c6d 100644 --- a/src/part3/run_admin.py +++ b/src/part3/run_admin.py @@ -64,9 +64,7 @@ def run_command(storage: list[Product], line: str) -> bool: qty = int(parts[-1]) price = Decimal(parts[-2]) name = " ".join(parts[1:-2]) - res = create_product( - storage, (name, price, qty) - ) + res = create_product(storage, (name, price, qty)) print_result(res) return True elif cmd == "update" and len(parts) >= 5: From 5842a478ef32f34b7cd0eec9974047aebc898af0 Mon Sep 17 00:00:00 2001 From: phantom686 Date: Mon, 12 Oct 2026 22:10:53 +0300 Subject: [PATCH 24/24] finalfix --- src/part3/run_admin.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/part3/run_admin.py b/src/part3/run_admin.py index 89f07c6d..e54b5163 100644 --- a/src/part3/run_admin.py +++ b/src/part3/run_admin.py @@ -52,28 +52,28 @@ def run_command(storage: list[Product], line: str) -> bool: return True elif cmd == "read" and len(parts) == 2: pid = int(parts[1]) - res: tuple[int, str, Decimal, int] | None = read_product(storage, pid) + res = read_product(storage, pid) print_result(res) return True elif cmd == "delete" and len(parts) == 2: pid = int(parts[1]) - res = delete_product(storage, pid) - print_result(res) + res1 = delete_product(storage, pid) + print_result(res1) return True elif cmd == "create" and len(parts) >= 4: qty = int(parts[-1]) price = Decimal(parts[-2]) name = " ".join(parts[1:-2]) - res = create_product(storage, (name, price, qty)) - print_result(res) + res2 = create_product(storage, (name, price, qty)) + print_result(res2) return True elif cmd == "update" and len(parts) >= 5: pid = int(parts[1]) qty = int(parts[-1]) price = Decimal(parts[-2]) name = " ".join(parts[2:-2]) - res = update_product(storage, pid, (name, price, qty)) - print_result(res) + res3 = update_product(storage, pid, (name, price, qty)) + print_result(res3) return True print(f"'{line}' is not a command") return True