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
102 changes: 12 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,98 +11,20 @@

- [Часть 1. Генератор идентификаторов Twitter Snowflake](src/part1/TASK.md) — нужно написать генератор 64-битных числовых идентификаторов, в которые упакованы метка времени, номер узла и счётчик последовательности. Такие идентификаторы уникальны и примерно упорядочены по времени, а из самого идентификатора можно узнать, когда и на каком узле он был создан.
- [Часть 2. CRUD и корзина для интернет-магазина](src/part2/TASK.md) — нужно реализовать хранилище товаров в памяти (список кортежей), операции создания, чтения, изменения и удаления записей, аккуратную работу с ценами через `Decimal` и корзину покупателя, в которую товары переносятся со склада и обратно без потери количества.
- [Часть 3. Нормализация имён и консоль администратора](src/part3/TASK.md) — нужно привести названия товаров к единому написанию и следить за их уникальностью, научить хранилище печатать себя выровненной текстовой таблицей и написать интерактивную консоль администратора, через которую можно создавать, просматривать, изменять и удалять товары.
- [Часть 4. Хранилище-словарь и UUID-идентификаторы](src/part4/TASK.md) — нужно перевести хранилище со списка кортежей на словарь, выдавать товарам идентификаторы в виде UUID, завести индекс имён для быстрой проверки уникальности, научить `update_product` менять только часть полей и оформить продуктовую корзину в виде словаря.
- [Часть 5. Snowflake-идентификаторы для интернет-магазина](src/part5/TASK.md) — нужно вернуться к Snowflake из части 1 и оформить его как генератор со своим счётчиком, перевести товары с UUID на целочисленные идентификаторы и написать параметрический декоратор, который считает успешные и неуспешные вызовы операций хранилища, а консоль администратора научить печатать эту статистику командой `stats`.

## Критерии оценивания

Максимальный балл за каждое задание — **10**. Правильность решения проверяется закрытыми тестами.

### Условия принятия работы

Работа принимается к оцениванию, только если выполнены **все** условия:

- код проходит проверки [ruff](https://docs.astral.sh/ruff/) и [MyPy](https://mypy-lang.org/) (запустить их можно [локально](manuals/local_checks.md));
- работа сдана до дедлайна.

Если хотя бы одно условие не выполнено, работа не принимается и оценивается в 0 баллов.

### Как считается балл за правильность

Тесты разбиты на функциональные блоки: каждый блок соответствует функции или классу из задания и имеет свой вес (более сложные блоки весят больше). Внутри блока тесты делятся на две группы:

| Группа тестов | Доля веса блока | Что проверяет |
|---|---|---|
| Базовые (типичные входные данные) | 70% | Основная логика реализована верно |
| Граничные (пустой ввод, нулевые и предельные значения, повторы и т. п.) | 30% | Обработка особых случаев |

Формулы:

```
балл_блока = 0.7 · доля_пройденных_базовых + 0.3 · доля_пройденных_граничных
балл_за_правильность = 10 · Σ(вес_блока · балл_блока) / Σ(вес_блока)
```

Дополнительные правила:

- **Нереализованный функционал.** Блок, в котором функционал не реализован (заглушка, `pass`, `NotImplementedError`), получает 0 баллов. Остальные блоки оцениваются как обычно, дополнительного штрафа за нереализованное нет.
- **Ошибки выполнения.** Тест, в котором возникло необработанное исключение, считается проваленным.
- **Зависание.** У каждого теста есть ограничение по времени выполнения. Если решение не укладывается в него (например, из-за бесконечного цикла), тест считается проваленным.

### Штрафы за оформление
## Дедлайны

Кроме правильности, оценивается качество кода. За нарушения ниже из балла за правильность вычитаются штрафы. Каждое нарушение штрафуется **один раз**, сколько бы раз оно ни встретилось в работе.

У каждого нарушения есть уникальный код: `S` — серьёзные, `M` — средние, `T` — типовые. При проверке преподаватель указывает в комментарии к работе только коды (например, `S1, M2, T1`), а расшифровку и штраф можно найти в таблицах ниже.

#### Серьёзные нарушения (−1 балл за каждое)

| Код | Нарушение | Что имеется в виду |
|---|---|---|
| `S1` | Копипаста | Вместо вынесения повторяющейся логики в функцию, метод или цикл один и тот же (или почти тот же) код скопирован несколько раз. Мешает расширяемости: любое изменение приходится вносить во все копии |
| `S2` | Слишком длинные функции | Функция делает много разных вещей одновременно (вычисления, проверки, преобразования, форматирование в одном месте) и не читается целиком. Ориентир: более 30–40 строк или несколько несвязанных обязанностей. Такой код тяжело читать и поддерживать |
| `S3` | Магические значения | Числа и строки с неочевидным смыслом прописаны прямо в коде, а не вынесены в именованные константы. Как и копипаста, мешает расширяемости: значение приходится искать и менять по всему коду |
| `S4` | Слишком широкий перехват исключений | Голый `except:` или `except Exception:` без необходимости; молча проглоченные ошибки (`except ...: pass`). Так можно пропустить что-то важное |
| `S5` | Изменение входных данных | Функция неожиданно изменяет переданные ей аргументы, хотя из названия и описания это не следует. Неожиданное поведение, источник серьёзных ошибок |

#### Средние нарушения (−0.5 балла за каждое)

| Код | Нарушение | Что имеется в виду |
| Задание | Дедлайн (включительно) | Статус |
|---|---|---|
| `M1` | Большая вложенность | Более трёх уровней вложенности (`if`, `for`, `while`, `try`, `with`) внутри одной функции. Лечится ранним возвратом (`return`, `continue`) и выносом логики в отдельные функции |
| `M2` | Неинформативные имена | Имена вроде `a`, `tmp`, `data2`, `x1`, а также имена, не отражающие суть, или не соответствующие PEP 8 (например, `camelCase` для функций и переменных) |
| `M3` | Избыточные глобальные переменные | Изменяемое глобальное состояние там, где достаточно параметров, возвращаемых значений или атрибутов класса |
| `M4` | Мёртвый код | Код, который нигде не используется: неиспользуемые функции, классы, переменные, константы, импорты, недостижимые ветки. Сюда же относятся крупные куски закомментированного кода «на будущее» и оставленный отладочный `print`. Затрудняет чтение, но на работу программы не влияет |

#### Типовые нарушения хорошего тона (−0.25 балла за каждое)

| Код | Нарушение |
|---|---|
| `T1` | `else` или `elif` после `return`, `raise`, `continue` или `break` |
| `T2` | Явное приведение к `bool` в условиях (`if bool(x):`) и сравнение с `True` / `False` (`if x == True:`) |
| `T3` | Сравнение с синглтонами и членами enum'ов через `==` / `!=` вместо `is` / `is not` |
| `T4` | Проверка пустоты коллекции через `len(x) == 0` вместо `if not x` |
| `T5` | Обход через `for i in range(len(items))`, когда индекс не нужен (вместо `for item in items` или `enumerate`) |
| `T6` | `if cond: return True else: return False` вместо `return cond` |
| `T7` | Изменяемое значение по умолчанию у аргумента (`def f(x=[])`) |
| `T8` | Ручная конкатенация строк вместо f-строк |
| `T9` | Избыточные комментарии, пересказывающие очевидный код |
| [Часть 1. Генератор идентификаторов Twitter Snowflake](src/part1/TASK.md) | 25.09.2026 | дедлайн истек |
| [Часть 2. CRUD и корзина для интернет-магазина](src/part2/TASK.md) | 02.10.2026 | дедлайн истек |
| [Часть 3. Нормализация имён и консоль администратора](src/part3/TASK.md) | 12.10.2026 | дедлайн истек |
| [Часть 4. Хранилище-словарь и UUID-идентификаторы](src/part4/TASK.md) | 01.11.2026 | дедлайн истек |
| [Часть 5. Snowflake-идентификаторы для интернет-магазина](src/part5/TASK.md) | 15.11.2026 | активно |

### Штраф за списывание

Если работа подозревается в списывании, студент получает **−5 баллов**. Эти баллы не вычитаются из оценки за правильность, а **заменяют** итоговую оценку: итоговой оценкой становится −5, независимо от результата тестов.

Студент может оспорить этот балл на занятии, доказав, что не списывал, а написал код самостоятельно (например, объяснив решение и ответив на вопросы по нему). Если подозрение снимается, работа оценивается по обычным критериям.

### Итоговая оценка

```
если есть подозрение в списывании:
итоговая_оценка = −5
иначе:
итоговая_оценка = max(0, балл_за_правильность − штрафы_за_оформление)
```

Итоговая оценка за домашнее задание округляется до ближайшего целого по правилам математики: дробная часть от 0.5 и выше округляется вверх, меньше 0.5 — вниз (например, 7.5 → 8, 7.4 → 7).

### Как оспорить оценку
## Критерии оценивания

Если вы не согласны с оценкой, напишите преподавателю. Укажите, с каким блоком или с какой частью критериев вы не согласны, и приведите аргументы. Оценка пересматривается по тем же критериям, которые описаны выше.
Критерии оценивания можно узнать [тут](docs/grading_criteria.md).
Empty file added __init__.py
Empty file.
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ avoid-escape = true
[tool.ruff.lint.isort]
lines-after-imports = 2
split-on-trailing-comma = true

[tool.mypy]
strict = true
mypy_path = "src"
namespace_packages = true
explicit_package_bases = true

2 changes: 1 addition & 1 deletion src/part1/TASK.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Начнём с фундамента: у каждой записи должен быть уникальный идентификатор.

Казалось бы, можно просто нумеровать записи 1, 2, 3, … Пока хранилище живёт на
одной машине, так и происходит. Но реальные системы почти всегда распределены:
одной машине, так и происходит. Но реальные системы почти всегда распределены:
записи создаются на десятках серверов одновременно. Тогда возникают проблемы:

- единый счётчик становится «бутылочным горлышком»: каждому серверу приходится
Expand Down
1 change: 0 additions & 1 deletion src/part1/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
"""Homework 3: a Twitter Snowflake identifier generator and its bit layout."""
45 changes: 10 additions & 35 deletions src/part1/constants.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
"""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: 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
NODE_ID_SHIFT: Final[int] = SEQUENCE_ID_BITS
TIMESTAMP_SHIFT: Final[int] = SEQUENCE_ID_BITS + NODE_ID_BITS
Loading
Loading