diff --git a/Solve task 1/constants.py b/Solve task 1/constants.py new file mode 100644 index 00000000..203dcc4c --- /dev/null +++ b/Solve task 1/constants.py @@ -0,0 +1,11 @@ +from typing import Final +import time + +EPOCH_MS_DEFAULT: Final[int] = 1288834974657 +NODE_ID_DEFAULT: Final[int] = 1 +TIMESTAMP_BITS: Final[int] = 41 +NODE_ID_BITS: Final[int] = 10 +SEQUENCE_ID_BITS: Final[int] = 12 +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 \ No newline at end of file diff --git a/Solve task 1/snowflake.py b/Solve task 1/snowflake.py new file mode 100644 index 00000000..45bf7715 --- /dev/null +++ b/Solve task 1/snowflake.py @@ -0,0 +1,32 @@ +import time + +from constants import * + +def read_current_millis(epoch_ms: int) -> int: + return int(time.time()*1000) - epoch_ms + +def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: + return int(bin(snowflake_id)[3:44], 2) + epoch_ms + +def decode_node_id(snowflake_id: int) -> int: + return int(bin(snowflake_id)[44:54], 2) + +def decode_sequence_id(snowflake_id: int) -> int: + return int(bin(snowflake_id)[54:], 2) + +def generate_snowflake_id( + sequence_id: int, + node_id: int = NODE_ID_DEFAULT, + epoch_ms: int = EPOCH_MS_DEFAULT) -> int | None: + if node_id < 0 or node_id > NODE_ID_MAX: + print (f"node_id must be in [0, {NODE_ID_MAX}]") + return None + elif sequence_id < 0 or sequence_id > SEQUENCE_ID_MAX: + print (f"sequence_id be in [0, {SEQUENCE_ID_MAX}]") + return None + else: + timestamp = time.time() + if timestamp > TIMESTAMP_MS_MAX: + print ("overflows") + return None + return int('0' + bin(int(time.time()*1000) + epoch_ms)[2:].zfill(41) + bin(node_id)[2:].zfill(10) + bin(sequence_id)[2:].zfill(12), 2) diff --git a/Solve task 2/cart.py b/Solve task 2/cart.py new file mode 100644 index 00000000..df5036d9 --- /dev/null +++ b/Solve task 2/cart.py @@ -0,0 +1,68 @@ +from typing import Final +from crud import read_product, update_product +from storage import ( + NAME_INDEX, + PRICE_INDEX, + QUANTITY_INDEX, + Product, +) + + +type CartLine = tuple[int, int] + +# TODO: задайте позиции полей внутри кортежа CartLine +LINE_PRODUCT_ID_INDEX: Final = 0 +LINE_QUANTITY_INDEX: Final = 1 + + +def add_to_cart( + storage: list[Product], + cart: list[CartLine], + product_id: int, + quantity: int, +) -> CartLine | None: + prod = read_product(storage, product_id) + if prod is None: + return None + if prod[QUANTITY_INDEX] < quantity: + print (f"not enough stock for product {product_id}: {prod[QUANTITY_INDEX]} available, {quantity} requested") + return None + storage[storage.index(prod)] = update_product(storage, product_id, (prod[NAME_INDEX], prod[PRICE_INDEX], prod[QUANTITY_INDEX] - quantity)) + for i in range(len(cart)): + if cart[i][LINE_PRODUCT_ID_INDEX] is product_id: + cart[i] = (product_id, cart[i][LINE_QUANTITY_INDEX] + quantity) + return cart[i] + cart.append(product_id, quantity) + return cart[-1] + + +def find_cart_line(cart: list[CartLine], product_id: int) -> CartLine | None: + for line in cart: + if line[LINE_PRODUCT_ID_INDEX] is product_id: + return line + return None + + +def remove_from_cart( + storage: list[Product], + cart: list[CartLine], + product_id: int, + quantity: int, +) -> CartLine | None: + prod_cart = find_cart_line(cart, product_id) + if prod_cart is None: + print (f"product {product_id} is not in the cart") + return None + if prod_cart[LINE_QUANTITY_INDEX] < quantity: + print (f"cart holds only {prod_cart[LINE_QUANTITY_INDEX]} unit(s) of product {product_id}, cannot remove {quantity}") + return None + prod = read_product(storage, product_id) + if prod is None: + return None + storage[storage.index(prod)] = update_product(storage, product_id, (prod[NAME_INDEX], prod[PRICE_INDEX], prod[QUANTITY_INDEX] + quantity)) + if prod_cart[LINE_QUANTITY_INDEX] - quantity is 0: + cart.remove(prod_cart) + return 0 + + cart [cart.index(prod_cart)] = CartLine(product_id, prod_cart[LINE_QUANTITY_INDEX] - quantity) + return cart [cart.index(prod_cart)] \ No newline at end of file diff --git a/Solve task 2/crud.py b/Solve task 2/crud.py new file mode 100644 index 00000000..5b6519d2 --- /dev/null +++ b/Solve task 2/crud.py @@ -0,0 +1,55 @@ +from decimal import Decimal + +from storage import ( + NAME_INDEX, + PRODUCT_ID_INDEX, + PRODUCT_ID_MIN, + Product, +) +from utils import normalize_price + + +def generate_product_id(storage: list[Product]) -> int: + if not storage: + return 0 + return max(storage)[PRODUCT_ID_INDEX] + 1 + + +def create_product(storage: list[Product], fields: tuple[str, Decimal, int]) -> int | None: + for prod in storage: + if fields [0] is prod[NAME_INDEX]: + print (f"product name '{fields [0]}' is already taken") + return None + new_product = (generate_product_id(storage), fields[0], normalize_price(fields[1]), fields[2]) + storage.append(new_product) + return new_product[0] + + +def read_product(storage: list[Product], product_id: int) -> Product | None: + for prod in storage: + if prod[0] is product_id: + return prod + print (f"no product with id {product_id}") + return None + + +def update_product( + storage: list[Product], + product_id: int, + fields: tuple[str, Decimal, int], +) -> Product | None: + for i in range(len(storage)): + if storage[i][PRODUCT_ID_INDEX] is product_id: + storage[i] = (product_id, fields[0], normalize_price(fields[1]), fields[2]) + return storage[i] + print (f"no product with id {product_id}") + return None + + +def delete_product(storage: list[Product], product_id: int) -> int | None: + for i in range(len(storage)): + if storage[i][PRODUCT_ID_INDEX] is product_id: + del storage[i] + return product_id + print (f"no product with id {product_id}") + return None diff --git a/Solve task 2/storage.py b/Solve task 2/storage.py new file mode 100644 index 00000000..c9c544d8 --- /dev/null +++ b/Solve task 2/storage.py @@ -0,0 +1,12 @@ +from decimal import Decimal +from typing import Final + + +type Product = tuple[int, str, Decimal, int] + +PRODUCT_ID_INDEX: Final = 0 +NAME_INDEX: Final = 1 +PRICE_INDEX: Final = 2 +QUANTITY_INDEX: Final = 3 + +PRODUCT_ID_MIN: Final[int] = 1 \ No newline at end of file diff --git a/Solve task 2/utils.py b/Solve task 2/utils.py new file mode 100644 index 00000000..67364b10 --- /dev/null +++ b/Solve task 2/utils.py @@ -0,0 +1,10 @@ +from decimal import ROUND_HALF_UP, Decimal +from typing import Final + + +PRICE_PRECISION: Final[int] = 2 +PRICE_STEP: Final = Decimal(1).scaleb(-PRICE_PRECISION) + + +def normalize_price(price: Decimal) -> Decimal: + return price.quantize(PRICE_STEP, rounding=ROUND_HALF_UP) \ No newline at end of file