# Мастерская — прикладной проект на YETI³ Core

Небольшая локальная система заявок: создание, фильтрация, завершение и возврат
в работу. Заявки сохраняются в SQLite и остаются после перезапуска. Внутри —
два модуля, сборка решения, HTML-шаблон, ассет-пак, HTTP-интерфейс и тесты.
Python 3.12+. Docker, Node.js, Django и сервер базы данных не нужны.

## 1. Запуск (macOS / Linux)

Откройте терминал в распакованной папке `workshop`:

```sh
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python app.py
```

Windows PowerShell:

```powershell
py -3 -m venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txt
.venv\Scripts\python.exe app.py
```

Откройте http://127.0.0.1:8088. Добавьте «Отремонтировать светильник», нажмите
«Завершить», выберите «Готово», затем «Вернуть в работу». Остановите сервер
Ctrl+C и запустите снова: заявка останется. Для второго запуска повторная
установка не нужна. Если порт занят: `python app.py --port 8090`.

## 2. Как устроены четыре части

| Часть | Файл | Ответственность |
|---|---|---|
| Модуль | `modules/requests.py` | Хранение и правила работы с заявками |
| Второй модуль | `modules/activity.py` | Подписка на событие создания заявки |
| Решение | `solution.py` | Собирает модули, запускает Engine, получает сервис |
| Шаблон | `templates/index.html` | Разметка страницы, подписи, расположение блоков |
| Подготовка HTML | `presentation.py` | Экранирует данные и передаёт их в шаблон |
| Ассет-пак | `asset_pack/static/` | CSS и SVG, общие для страниц |
| Веб-интерфейс | `app.py` | Обрабатывает формы, отдаёт страницу и файлы |

Решение здесь — сборка приложения из модулей. Это рабочий исходный проект,
а не опубликованный товар GoChain. Шаблон здесь — HTML, а не механизм
создания проектов из шаблонов. Ассет-пак содержит только ресурсы оформления.
Ядро не создаёт веб-сервер или базу автоматически: эти части явно реализованы
в проекте стандартными библиотеками Python.

## 3. Модуль отдельно от сайта

```sh
python run_module.py
```

Результат: `Отремонтировать светильник → done`. Пример использует временную БД,
не трогает ваши заявки и показывает, что бизнес-логика не зависит от HTTP.
В `RequestsModule`:

- `manifest` описывает модуль и предоставляемый сервис;
- `boot()` подготавливает таблицу и вызывает `engine.provide()`;
- `create()` проверяет название и записывает заявку;
- `set_status()` меняет статус;
- `list()` возвращает обычные словари Python.

`ActivityModule` указывает зависимость через `requires`, подписывается на событие
в `boot()` и отписывается в `shutdown()`. Сообщение о новой заявке видно в терминале.
Этот вывод не является постоянным журналом аудита.

## 4. Собственное решение

`solution.py` — точка сборки. Создайте свой класс по образцу `RequestsModule`,
задайте уникальный `id`, зарегистрируйте его до `engine.boot()`. Если модулю
нужны заявки, укажите `requires=("workshop.requests",)` и получите сервис через
`engine.capabilities.get("workshop.requests")`. Не импортируйте веб-сервер в модуль.

## 5. Изменение шаблона

Поменяйте заголовок в `templates/index.html` и обновите страницу — файл читается
на каждый запрос. Переменные `$title`, `$cards`, `$total` заполняет `presentation.py`.
Для буквального знака доллара используйте `$$`. Не вставляйте пользовательский
текст в HTML без `html.escape()`. Карточки создаются только в `presentation.py`.

Первое упражнение: замените «Мастерская» в `presentation.py` на имя проекта,
а текст главного заголовка — в HTML. Данные и бизнес-модуль менять не нужно.

## 6. Свой ассет-пак

Измените `--accent` в `asset_pack/static/workshop.css`, обновите страницу.
Замените `mark.svg` своим SVG с тем же именем. HTML подключает файлы через `/static/`.
Стандартный запуск читает эти файлы напрямую; для знакомства с установкой через SDK:

```sh
python -m pip install --index-url https://engine.yeti3.ru/sdk/simple/ yeti3-assets==0.3.1
python install_assets.py
```

Получите `installed-assets/static/workshop.css` и `mark.svg`, а также запись
владельца в `.yeti3/ownership`. Манифест — `asset_pack/yeti3-module.toml`.
Повторная установка в ту же папку завершится отказом: пример специально
демонстрирует защиту от перезаписи. Для повторного опыта измените папку назначения
в скрипте на новую пустую. Это вызов файлового адаптера, не полный цикл покупки,
доставки, проверки лицензии или активации GoChain. Исходные CSS и SVG не меняются.

## 7. Проверка

```sh
python -m unittest discover -s tests -v
```

Проверяется сохранение после перезапуска, фильтры и статусы, события, защита форм,
экранирование HTML, SQL-параметры и доступ только к разрешённым статическим файлам.
Проверка адаптера ресурсов пропускается без необязательного `yeti3-assets`.
После его установки проходят все 11 проверок.

## 8. Данные и следующий шаг

База находится в `data/workshop.sqlite3`. Для резервной копии остановите приложение
и скопируйте файл; восстановление — при остановленном приложении. Не удаляйте файл,
если хотите сохранить заявки. Свой путь: `python app.py --database /path/to/app.sqlite3`.
После изменения Python-файлов перезапустите приложение; HTML/CSS достаточно обновить.

Это готовое локальное приложение для одного пользователя. Оно слушает только
127.0.0.1; аккаунтов и разделения пользователей здесь нет. Перед публикацией сайта
добавьте авторизацию и права на заявки, миграции схемы, резервное копирование и
боевой HTTP-сервер с HTTPS. Не открывайте этот учебный http.server в интернет.
Для нового поля в существующей БД требуется миграция: CREATE TABLE IF NOT EXISTS
не добавляет столбцы автоматически.

Если `ModuleNotFoundError`: проверьте, что установка и запуск используют один Python.
Если форма просит обновить страницу: сервер перезапущен, откройте страницу снова.
Если название отклонено: сервер принимает от 3 до 120 символов, включая пробелы внутри.
