# Домашнє завдання · CSV і JSON

> Лекція: [lecture.html](lecture.html) · Практика: [practice.ipynb](practice.ipynb) · Тест: [quiz.html](quiz.html)

Усі файли створюй у тимчасовій теці (`tempfile.mkdtemp()`) або в окремій теці `data/`
поруч зі скриптом — і прибирай за собою. Дані вигадуй сам, з мережі нічого не тягни.

---

## 🟢 Рівень 1 — База

Візьми свій набір даних — не кавʼярню з лекції. Наприклад: домашня бібліотека
(назва, автор, рік, нотатка), список фільмів, витрати за тиждень. Потрібно **5-7 записів**,
і хоча б в одному полі мусить бути **кома всередині значення** (нотатка, опис, адреса).

1. Запиши дані у CSV через `csv.DictWriter` з `newline=""` і `encoding="utf-8"`.
2. Надрукуй файл як звичайний текст і подивись, де модуль сам поставив лапки.
3. Прочитай файл двома способами: через `рядок.split(",")` і через `csv.DictReader`.
4. Порівняй кількість полів в обох випадках.

**Зроблено, якщо:** у виводі видно рядок із лапками, надруковано кількість полів
для обох способів, і проходить `assert` про те, що поле з комою після `DictReader`
повернулось цілим одним значенням.

---

## 🟡 Рівень 2 — Плюс

Ті самі дані, але з вкладеністю: додай кожному запису список (жанри книжки,
теги витрати, актори фільму) — різної довжини в різних записів.

1. Запиши це у JSON з `ensure_ascii=False` та `indent=2`.
2. Запиши те саме у CSV, склеївши список в одне поле через `|`, і напиши функцію,
   яка розклеює його назад.
3. Порівняй розміри обох файлів через `.stat().st_size`.
4. Спробуй записати той самий обʼєкт двічі: з `ensure_ascii=False` і без нього.
   Порівняй розміри й переконайся `assert`-ом, що `json.loads` з обох рядків
   дає **однаковий** словник.

**Зроблено, якщо:** обидва файли створено, надруковано їхні розміри, `assert` про
однаковість даних проходить — і в комірці-markdown (або в коментарі) написано
2-3 речення про те, який формат ти обрав би для цих даних і **чому саме форма
даних**, а не розмір, вирішує справу.

---

## 🔴 Рівень 3 — Виклик

Напиши модуль-валідатор `перевір_csv(шлях, схема)`, який читає файл і повертає
список знайдених проблем. `схема` — словник «назва стовпця → тип», наприклад
`{"назва": str, "ціна": int, "рік": int}`.

Валідатор має знаходити щонайменше чотири види проблем і для кожної повертати
**номер рядка у файлі**:

1. рядок коротший за шапку (у полі опинився `None`);
2. рядок довший за шапку (зайві поля поїхали під ключ `None`);
3. значення не перетворюється на потрібний тип (`int("сорок")`);
4. обовʼязкове поле порожнє (`""` — це не те саме, що `None`).

Далі зроби зворотну задачу: напиши `csv_у_json(шлях_csv, шлях_json, схема)`, яка
читає CSV, застосовує типи зі схеми й записує коректний JSON — з `ensure_ascii=False`.
Якщо валідатор знайшов проблеми, JSON не створюється, а функція повертає список проблем.

**Зроблено, якщо:** на навмисно зіпсованому файлі (усі чотири поломки одночасно)
валідатор повертає чотири записи з правильними номерами рядків; на чистому файлі
повертає порожній список і створює JSON, який успішно читається `json.load`,
а ціни в ньому — числа, а не рядки.

---

## Підказки

* Порожнє поле — це `""`, відсутнє — це `None`. Це різні випадки, і плутати їх
  не можна: перше означає «людина не заповнила», друге — «стовпця взагалі не було».
* Не пиши `if` для перевірки «чи це число»: спробуй `int(значення)` всередині `try`
  і злови `ValueError`. Так само з `float` — і памʼятай про кому замість крапки
  в українській локалі.
* Номер рядка зручно брати з `enumerate(читач, start=2)`: двійка тому, що перший
  рядок файлу забрала шапка, і рядок даних №1 фізично лежить другим.
* Якщо валідатор виходить довгим — розбий його на маленькі функції, кожна з яких
  перевіряє одну річ і повертає список претензій. Так їх легше тестувати окремо.
