Проверка на данном этапе

This commit is contained in:
vladslav064 committed 2026-11-12 08:02:52 +03:00
commit 626ac21b41
9 files changed
+1986

No files matched your search

+37
View File
@@ -0,0 +1,37 @@
# Журнал работы с ИИ
## 12 ноября 2026 — осмотр проекта
**Запрос пользователя.** Подготовить по этапам ETL-пайплайн для очистки CSV и агрегации выручки по дням и категориям в SQLite. Сохранять исходный CSV, учитывать причины отбраковки, проверить идемпотентность, подготовить три аналитических запроса с результатами, README и готовую базу. Вести журнал по фактически выполненной работе. Проверки писать на pytest. Оставить `main.py` в корне для объединения этапов, функции разместить в `src/`, пути вычислять от корня проекта.
**Проверки.** Осмотр корня проекта через PowerShell показал `.git/` и `.gitignore`. Прочитан `.gitignore`, проверено состояние Git. Исходный CSV, код и тесты отсутствовали. Файл `AGENTS.md` в корне проекта и родительских каталогах не обнаружен. Дополнительная команда `rg --files` завершилась ошибкой канала запуска. Список содержимого корня получен через `Get-ChildItem -Force`.
**Решение.** Первый этап реализации пользователь пока не указал. Работа ограничена осмотром и созданием журнала. Схема CSV и правила расчёта выручки пока не определены. Тесты не запускались.
## 12 ноября 2026 — план реализации
**Запрос пользователя.** Изучить проект и подготовить краткий план без кода пайплайна. Зафиксировать заданную модель заказа, денежные ограничения, порядок обработки дублей, нумерацию записей CSV, интерфейсы модулей, полную транзакционную перезагрузку, критерии готовности и неоднозначности.
**Принятые решения.** В [плане реализации](implementation_plan.md) описаны пять полей заказа, точный разбор цены через Decimal, хранение денег в копейках и проверка диапазона INTEGER. Точные дубли определяются по исходным полям до валидации. Конфликты ID проверяются по всей группе валидных заказов до выбора представителя. Для отчёта у каждой отброшенной записи одна причина и логический номер после заголовка, начиная с единицы.
**Разделение работы.** Чтение, очистка и загрузка распределены между тремя модулями в src/. Дневные агрегаты строятся в SQLite при полной перезагрузке двух таблиц в одной транзакции. В плане отдельно отмечены предлагаемые параметры CSV и определения аналитических запросов.
**Фактические проверки и изменения.** Повторный осмотр через rg и Git показал только .gitignore и ранее созданный журнал. Прочитан журнал, проверено отсутствие AGENTS.md в каталогах проекта и документации и по пути к корню диска. Добавлен план и обновлён журнал. Код пайплайна не создавался, pytest не запускался.
## 12 ноября 2026 — генератор синтетических заказов
**Запрос пользователя.** Создать воспроизводимый CSV на 1500 записей с seed=42, заданными дублями и ошибками. Запустить генератор и тесты количества записей и воспроизводимости на временных файлах.
**Решения.** В `src/generate_data.py` используется локальный `Random(42)`. Корректные даты выбираются из диапазона 01.01.2024–31.03.2024 включительно. Цены формируются из целых копеек, категории выбираются из фиксированного списка. Для трёх заказов добавляется по одной копии — точной, с другой записью той же даты и с изменённым количеством. Для остальных заданных случаев изменено по одному полю в отдельных заказах. Записи перемешиваются тем же генератором. CSV синтетический, без персональных данных. Кодировка UTF-8 и перевод строки LF заданы явно.
**Окружение.** В доступном Python 3.14.4 отсутствовал pytest. Создана локальная .venv. Первая попытка установки не нашла дистрибутив, затем из PyPI установлен pytest 9.2.0.
**Проверки.** Команда `python -m src.generate_data` выполнена дважды. SHA-256 файла `data/raw_orders.csv` после обоих запусков совпал — `c4b9ffd2eb24c6c79b6a3e2436f3bd348ba86696a1aeb9b7e3083af65c799c67`. Чтение через csv.DictReader подтвердило пять заданных колонок, 1500 записей, один точный дубль и по одному пустому значению в каждом поле. ID 1, 2 и 3 встретились по два раза. Запуск `.\.venv\Scripts\python.exe -m pytest -q` завершился результатом `2 passed in 0.08s`. Оба теста используют tmp_path.
## 12 ноября 2026 — чтение и очистка
**Запрос пользователя.** Реализовать extract.py и transform.py, сохранить номер и исходные значения отклонённой записи, вернуть все обнаруженные ошибки полей, проверить дубли, конфликты, точность расчётов и сохранность CSV.
**Решения и изменения.** csv.reader в строгом режиме читает UTF-8 с поддержкой BOM и нумерует логические записи после заголовка. Нечитаемый файл, ошибка разбора CSV или неверный заголовок прерывают обработку. Для читаемой записи неверной ширины сохраняются все исходные поля. Точный дубль получает только причину повтора. Для остальных структурно корректных записей собираются ошибки независимых полей. Выручка проверяется при пригодных для расчёта цене и количестве. Среди валидных заказов сначала определяется наличие конфликта во всей группе ID. Номер источника сохраняется и у принятого заказа, но исключён из сравнения нормализованных значений. План обновлён согласно уточнению о нескольких причинах.
**Фактические проверки.** Полный запуск pytest дал 32 passed. На синтетическом raw_orders.csv обработаны 1500 записей, приняты 1482, отброшены 18. Из них 14 имели ошибки значений, одна оказалась точным дублем, одна — нормализованным, две отклонены из-за конфликта ID. Проверено, что номера от 1 до 1500 встречаются в совокупном результате ровно по одному разу. Байты CSV до и после обработки совпали, SHA-256 — `c4b9ffd2eb24c6c79b6a3e2436f3bd348ba86696a1aeb9b7e3083af65c799c67`.
+51
View File
@@ -0,0 +1,51 @@
# План реализации ETL
## Модель данных
Одна запись CSV описывает один завершённый заказ с одним товаром. Возвраты, скидки, бесплатные товары и несколько валют вне модели. Все пять колонок обязательны в заголовке. Пустое значение допустимо только в `category`.
| Поле | Принятое решение |
|---|---|
| `order_id` | Положительное целое число. После разбора ведущие нули не влияют на идентичность заказа. |
| `order_date` | Только календарно корректные даты строго вида `YYYY-MM-DD` или `DD.MM.YYYY`, включая ведущие нули. В базе хранить текст `YYYY-MM-DD`. |
| `category` | Удалить внешние пробельные символы, каждую внутреннюю последовательность пробельных символов заменить одним пробелом. Первую букву строки привести к верхнему регистру, остальные к нижнему. Пустое значение заменить на «Не указана». |
| `quantity` | Строго положительное целое количество единиц товара. |
| `unit_price` | Строго положительная цена единицы в рублях. Принимать целую часть из цифр и необязательную дробную часть из одной или двух цифр после точки или запятой. Разбирать через `Decimal`, переводить в целые копейки точно, без округления. |
Для числовых полей принимаем десятичные цифры ASCII, для всех полей при нормализации убираем внешние пробелы. Знаки, экспоненциальная запись, разделители тысяч и неконечные значения, включая NaN и Infinity, отклоняются. Цена `1,230` отклоняется даже при нуле в третьем разряде. Выручка заказа в копейках равна количеству, умноженному на цену единицы в копейках. ID, количество, цена в копейках и произведение должны умещаться в знаковый INTEGER SQLite с максимумом 9 223 372 036 854 775 807. Переполнение отдельного заказа является причиной отбраковки записи.
## Порядок обработки
1. Читать CSV стандартным модулем `csv`, открывая файл с `newline=''`. Считать логические записи после заголовка с единицы, включая отклонённые записи. Поле в кавычках с переводом строки входит в одну запись. Сохранять номер у принятого заказа и в отчёте для каждой отброшенной записи.
2. До очистки сравнить кортежи исходных значений полей, полученных от CSV-парсера. Для каждого кортежа оставить первое вхождение, последующие отметить как точные дубли. Это правило действует и для повторов невалидного заказа.
3. Проверить число полей. Для записи правильной ширины собрать все ошибки независимых полей в порядке таблицы. Проверить произведение, только если количество и цена прошли проверку. Сохранять один итоговый статус и список фактически установленных причин на запись.
4. Сгруппировать только валидные записи по нормализованному ID. Сравнивать дату, категорию, количество и цену в копейках. Если значения совпадают у всей группы, принять запись с наименьшим номером, остальные отметить как нормализованные дубли. Если есть хотя бы два варианта значений, отклонить всю валидную группу как конфликт ID, включая повторившиеся варианты. Уже отброшенные точные дубли сохраняют исходную причину.
5. Вывести количество прочитанных, принятых и отброшенных записей, разбивку по причинам и номера отклонённых записей. Число прочитанных должно равняться сумме принятых и отброшенных. Сумма счётчиков причин может быть больше числа отброшенных записей.
## Модули и загрузка
| Модуль | Предлагаемый интерфейс и ответственность |
|---|---|
| `src/extract.py` | `read_records(path) -> Iterator[RawRecord]`. Передаёт номер записи и исходные значения, проверяет заголовок и ширину записи. |
| `src/transform.py` | `clean_orders(records) -> tuple[list[Order], list[RejectedRecord]]`. Рассчитывает выручку одного заказа и возвращает принятые заказы и отбраковку с причинами. |
| `src/storage.py` | `reload_database(path, orders) -> None`. Обновляет обе таблицы атомарно и рассчитывает дневные агрегаты в SQLite. |
| `main.py` | Определяет корень по расположению самого файла, собирает пути к ресурсам, вызывает этапы и выводит отчёт. |
В `orders` хранить одну принятую запись на `order_id` (первичный ключ), включая номер записи, дату, категорию, количество, цену и выручку в копейках. В `daily_revenue` хранить сумму выручки в копейках с составным первичным ключом из даты и категории.
В одной явной транзакции создать таблицы при первом запуске, очистить обе, загрузить принятые заказы и заполнить `daily_revenue` запросом с `SUM` и `GROUP BY order_date, category` из `orders`. Это единственное место построения дневной витрины. Зафиксировать транзакцию после успешного завершения всех операций. Ошибка загрузки или переполнение суммы группы отменяет транзакцию целиком, сохраняя предыдущие данные.
## Неоднозначности и рабочие допущения
- Исходного CSV пока нет. Предлагаемый формат — UTF-8 с поддержкой BOM, запятая между полями, двойные кавычки для значений с запятой или переводом строки. Для синтетического набора явно указать происхождение в README. Исходный файл открывать только на чтение.
- Колонки сопоставлять по именам. Отсутствующее, повторное или лишнее имя в заголовке считать ошибкой файла. Неверное число полей в читаемой записи считать ошибкой записи. Если из-за повреждения CSV границы записей определить невозможно, остановить запуск до загрузки.
- Файл с корректным заголовком и нулём принятых заказов считать пустым снимком. При полной перезагрузке обе таблицы станут пустыми.
- Для аналитики предлагаются три запроса к витрине. Топ — пять категорий по суммарной выручке с сортировкой по имени при равенстве. Динамика — сумма по дате и изменение в копейках к предыдущей доступной дате. Аномалия — дневная выручка категории более чем вдвое выше предыдущего наблюдения той же категории. Первое наблюдение пропускается, пропущенные даты нулями не заполняются. Порог — рабочее допущение для тестового задания.
## Критерии готовности реализации
- На pytest проверить оба формата дат, невозможные даты, пробелы, пустые поля, дробное количество, варианты цены и переполнение на границе INTEGER. Проверить нумерацию на CSV с многострочным полем.
- На размеченном примере сверить статус каждой исходной записи, причины и баланс счётчиков. Включить повтор невалидной записи, одинаковые нормализованные заказы, конфликт из трёх записей с двумя совпадающими вариантами и валидную запись рядом с невалидной того же ID.
- Сверить копейки по известному примеру. Две единицы по 10,25 рубля и одна за 0,50 рубля в тот же день и в той же категории должны дать одну строку витрины на 2100 копеек.
- Запустить пайплайн дважды с тем же CSV, в том числе из другой рабочей папки. Сравнить содержимое обеих таблиц, отчёты и контрольную сумму исходного файла. На изменённом снимке проверить удаление устаревших заказов и агрегатов. На переполнении суммы группы проверить откат обеих таблиц.
- Перед сдачей подготовить готовую SQLite-базу, три SQL-запроса с фактическими результатами, README с командами запуска и тестов и журнал фактически выполненных проверок.