123 lines
8.3 KiB
Markdown
123 lines
8.3 KiB
Markdown
# Схема Опуса — генератор + симулятор (v1.0)
|
||
|
||
Ответ Опуса от 25.06.2026 на запрос `History/opus-generator-request.md`.
|
||
|
||
---
|
||
|
||
## Оценка схемы
|
||
|
||
### Сильные стороны
|
||
|
||
1. **v1→v2 diff-пары** — ключевая идея. Генератор закладывает конкретные изменения между версиями спецификаций (цена, объём, ADD/DELETE/UPDATE), и верификатор проверяет что compare нашёл именно их. Это даёт детерминированную проверку самой важной функции сервиса.
|
||
|
||
2. **manifest.json как ground truth** — каждая проверка сверяется с эталоном, а не с «примерно ожидаемым». Никакой неоднозначности.
|
||
|
||
3. **Пул услуг из реальных данных** — стойко-место, PDU, L2-канал, cross-connect, Cloud, IPv4. Не выдуманные, а те что в реальных файлах.
|
||
|
||
4. **17 ошибочных кейсов** — покрытие крайне широкое: от битого XML до сиротских спецификаций.
|
||
|
||
5. **Структура папок** — `testgen/` + `sim/` с чётким разделением генерации и симуляции.
|
||
|
||
### Что нужно уточнить/доработать
|
||
|
||
1. **Симулятор: API или браузер?** Опус описывает «реальное приложение (API/UI)». Два варианта:
|
||
- **curl-симулятор** (только API): дёргаем `/upload`, `/api/classify-batch`, `/api/groups`, `/process-v2`. Быстро, дёшево, но не проверяет фронтенд (duplicate confirm, renderFiles, SSE в UI).
|
||
- **Playwright** (полный UI): открывает браузер, кликает кнопки, читает DOM. Медленно, но проверяет ВСЁ включая confirm-диалоги.
|
||
- **Рекомендация**: curl для генерации/классификации/групп + выборочно Playwright для UI-специфичных сценариев (дубликаты, confirm, удаление).
|
||
|
||
2. **.doc файлы** — парсинг `допник-1-XXX002-01200_3.doc` не удался (старый формат). Нужен LibreOffice для конвертации, либо исключить .doc из генерации и использовать только .docx.
|
||
|
||
3. **Стресс-сценарий F** — rapid-fire загрузки во время classify. Это может быть сложно воспроизвести и проверить. Вероятно, отложить на потом.
|
||
|
||
4. **Количество файлов** — раскладка даёт ~92 файла + edge cases. Можно докрутить до ровно 100 добавив ещё дубликатов или мусора.
|
||
|
||
---
|
||
|
||
## Исходная схема Опуса
|
||
|
||
### Часть 1 — Генерация ~100 файлов
|
||
|
||
**Базовая структура:** 10 компаний × 10 договоров × (спецификация v1 + v2 + 1-3 допника)
|
||
|
||
**Раскладка:**
|
||
| Категория | Кол-во | Зачем |
|
||
|---|---|---|
|
||
| Договоры (валидные) | 10 | по 1 на компанию |
|
||
| Спецификации v1 | 10 | базовая версия |
|
||
| Спецификации v2 (ревизия) | 10 | diff для сравнения |
|
||
| Допники | 18 | 1–3 на договор |
|
||
| Битые/edge .docx | 15 | ошибки парсинга/классификации |
|
||
| Форматы .doc/.pdf | 12 | ветка конвертера |
|
||
| ZIP-архивы | 5 | внутри — наборы файлов |
|
||
| Дубликаты (имя/контент) | 10 | upload-логика |
|
||
| Мусор (не договор) | 10 | негативная классификация |
|
||
| **Итого** | **~100** | |
|
||
|
||
**Оси вариативности:**
|
||
- Компания: ЗАО/ООО/АО/ПАО/ИП, латиница/кириллица
|
||
- Номер: 5 цифр, опц. суффикс `_N`, edge — буквы/длинный/пустой
|
||
- Дата: 12.2025–06.2026, форматы `01.02.2026` и «01 февраля 2026»
|
||
- Цены: базовые ±20%, разрядность с пробелом
|
||
- Услуги (пул): стойко-место, PDU, L2-канал, IPv4 /30 и /32, cross-connect ВОЛС/UTP, Cloud Intel/AMD, Интернет, порт, подсеть
|
||
- Таблица: 3–12 строк, 6 или 7 колонок
|
||
|
||
**Различия v1→v2 (для компаратора):** изменение цены, изменение объёма, добавленная услуга, удалённая услуга, переименование, сдвиг даты, пересчёт итога.
|
||
|
||
**Ошибочные кейсы (17 шт):** нет номера, нестандартный заголовок, битый XML, пустой файл, только таблицы, только текст, .doc формат, битый PDF, дубликат имени, дубликат контента, гигант (500 строк), не-договор, сирота (спека без договора), рассинхрон (имя файла ≠ номер в тексте).
|
||
|
||
**Parent-child:** ключ — НОМЕР. Генератор связывает договор→спеку/допник по НОМЕРу.
|
||
|
||
**Шаблоны python-docx:**
|
||
- Билдеры: `build_contract(ctx)`, `build_spec(ctx)`, `build_addendum(ctx)`
|
||
- ctx = {company, number, date, parties, services[], totals}
|
||
- Хелперы: `add_title`, `add_section`, `add_services_table`
|
||
- Битый XML: валидный docx → распаковать zip → испортить document.xml → запаковать
|
||
- .doc: docx → libreoffice --convert-to doc
|
||
- Битый PDF: docx → pdf → обрезать байты
|
||
|
||
### Часть 2 — Симулятор
|
||
|
||
**Словарь действий:** upload, delete, classify, groups, compare (SSE), confirm/cancel, reset.
|
||
|
||
**Сценарии:**
|
||
- **A. Хэппи-путь:** договор + спека v1 + v2 → classify → группа → compare → diff совпал
|
||
- **B. Зигзаг:** загрузил 3 → удалил 1 → добавил 2 → classify → проверить консистентность
|
||
- **C. Bulk:** все ~100 разом → classify → число групп, битые ✗
|
||
- **D. Тупые действия:** дубликат (OK/Отмена), удалить всё, смешать форматы, classify на нуле, compare с одной версией
|
||
- **E. ZIP:** валидный, с дубликатами, с битым файлом внутри
|
||
- **F. Стресс:** случайные паузы, rapid-fire, загрузка во время classify
|
||
|
||
**Тайминги:** think-time (random 0.1-3с), classify — поллинг, compare — SSE до done/таймаут.
|
||
|
||
### Часть 3 — Верификация
|
||
|
||
После каждого шага:
|
||
- `state.files` count == ожидаемого
|
||
- Статусы ✓/✗ соответствуют (битые → ✗)
|
||
- После classify: группы есть, состав по НОМЕРу верный
|
||
- Сироты без краша
|
||
- Compare: diff == заложенному (ground truth из manifest.json)
|
||
- Дубликаты: счётчик после OK ≠ после Отмена
|
||
- Нет 500-х, нет необработанных исключений
|
||
- Идемпотентность classify
|
||
|
||
**Ground truth:** `manifest.json` — что сгенерировано + ожидаемые группы и diff-ы.
|
||
|
||
### Структура папок
|
||
|
||
```
|
||
testgen/
|
||
pools.py # услуги, компании, цены
|
||
templates.py # build_contract / build_spec / build_addendum
|
||
corrupt.py # порча docx/pdf, конвертация .doc
|
||
generate.py # оркестратор → out/ + manifest.json
|
||
out/{valid,errors,formats,zips,duplicates}/
|
||
manifest.json # ground truth
|
||
|
||
sim/
|
||
actions.py # обёртки над API
|
||
scenarios.py # A..F
|
||
verify.py # чек-лист
|
||
run.py
|
||
```
|