Files
redmine-reporter/docs/CONFIG.md
Кокос Артем Николаевич 8614062ecd feat: status translation overrides via YAML config
report.status_translation in YAML config overrides/extends the builtin
STATUS_TRANSLATION dictionary; without the section behavior is unchanged.

- AppConfig.report_status_translation + _resolve_str_dict (warns and
  skips non-scalar values, resolves ${VAR} references)
- Config.get_status_translation() returns merged copy (lazy import,
  builtin dict never mutated)
- build_grouped_report() accepts optional status_translation parameter
- --init-config template gains commented example, docs/CONFIG.md updated

Closes #65
2026-07-17 15:18:27 +07:00

474 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Настройка redmine-reporter
## Источники конфигурации
Приоритет, от высшего к низшему:
```
CLI-флаги > переменные окружения > .env > YAML > кодовые дефолты
```
Если значение не задано на верхнем уровне, берётся уровень ниже. `.env` и YAML
работают одновременно — можно оставить оба, можно удалить `.env` после миграции.
## YAML-конфиг
Основной файл: `~/.config/redmine-reporter/config.yml`.
Создаётся с правами `0600` (владелец: чтение/запись, остальные: доступ запрещён).
Директория `~/.config/redmine-reporter/`с правами `0700`.
Если права файла шире `0600`, при запуске выводится предупреждение.
### Структура
```yaml
redmine:
url: https://red.eltex.loc
api_key: ${REDMINE_API_KEY}
author: "Кокос А.А."
verify_ssl: true
period:
precision: date
default_from: "2026-06-01"
# default_to можно не указывать — тогда конец периода будет сегодня
default_to: "2026-06-30"
dynamic: false
last_used:
from: "2026-06-30T09:00:00"
to: "2026-06-30T12:00:00"
output:
dir: ~/reports
filename: "{author}_{from}_{to}.{ext}"
default_format: xlsx
report:
no_time: false
email:
html: false
smtp:
host: smtp.example.com
port: 587
user: bot@example.com
password: ${SMTP_PASSWORD}
tls: true
from: bot@example.com
to:
- boss@example.com
cc: []
bcc: []
subject: "Отчёт {author} за {period}"
body_text: "Во вложении отчёт."
attach: true
```
### `redmine.verify_ssl` — проверка TLS-сертификата
Управляет проверкой TLS-сертификата Redmine. Семантика едина с переменной
окружения `REDMINE_VERIFY`:
| Значение | Поведение |
|---|---|
| `true` (по умолчанию) | Стандартная проверка TLS средствами requests (системные CA / certifi) |
| `false` | Проверка отключена — при запуске выводится предупреждение о риске MITM |
| строка с путём, например `/etc/ssl/my-ca.pem` | Путь к собственному CA-bundle, передаётся в requests как есть |
До версии с унификацией `verify_ssl: true` подставлял захардкоженный путь
`/etc/ssl/certs/ca-certificates.crt`, который отсутствует на части
дистрибутивов. Теперь `true` в YAML и `REDMINE_VERIFY=true` в env работают
одинаково — оба включают стандартную проверку без привязки к конкретному пути.
### `period.precision` — точность периода
Определяет, как вычисляется следующий период после фиксации:
- `date` (по умолчанию) — период с точностью до дня. Следующий запуск (после `--commit`) начинается со следующего дня.
- `datetime` — период с точностью до секунды. При повторном запуске time entries с `created_on` и `updated_on` ранее `last_used.to` исключаются (AND-логика: запись исключается только если **оба** поля раньше cutoff). Это предотвращает дублирование при отправке отчёта внутри рабочего дня.
`last_used.from` / `last_used.to` записываются автоматически при `--commit`. Вручную редактировать не требуется.
### `period.default_to` — необязательное окончание периода
Если `period.default_to` не задан, а `period.default_from` задан, инструмент
использует сегодняшнюю дату в качестве конца периода.
```yaml
period:
default_from: "2026-07-01"
# default_to отсутствует → конец периода = сегодня
```
Это предотвращает устаревание периода, когда отчёт генерируется автоматически
(`--send`, `--commit`) без явного `--date`.
Аналогично работает `DEFAULT_TO_DATE`: если переменная не задана, а
`DEFAULT_FROM_DATE` задана, конец периода = сегодня.
### Период по умолчанию — текущий месяц
Если период не задан ни одним из источников (`--date`, `DEFAULT_FROM_DATE`,
`period.default_from`), отчёт строится за текущий месяц: начало периода —
1-е число текущего месяца, конец — сегодняшняя дата. Период вычисляется
на момент запуска и не хранится в коде или конфиге.
### `--commit` — автофиксация периода
Флаг `--commit` сохраняет использованный период в YAML-конфиг, чтобы следующий запуск автоматически начинался с нового периода.
**Что делает:**
1. Генерирует отчёт как обычно.
2. Сохраняет отчёт в файл:
- Если указан `--output` — по явному пути.
- Если `--output` не указан — по шаблону из `output.dir` / `output.filename`.
3. Записывает `period.last_used.from` / `period.last_used.to` в YAML-конфиг.
4. При `period.precision: datetime` сохраняет текущий момент времени (ISO с секундами).
5. При `period.precision: date` сохраняет даты периода.
**Поведение `period.dynamic`:**
- `dynamic: true` — следующий запуск (без `--date`) вычисляет период от `last_used`:
- Полный календарный месяц → следующий полный месяц.
- Произвольный диапазон → та же длительность, начиная со дня после `last_used.to`.
- `dynamic: false``--commit` перезаписывает `default_from`/`default_to` на использованный период.
**Примеры:**
```bash
# Июнь 2026 → следующий запуск (без --date) → июль 2026
redmine-reporter --date 2026-06-01--2026-06-30 --commit
# Произвольный диапазон: 15-20 июня → следующий запуск → 21-26 июня
redmine-reporter --date 2026-06-15--2026-06-20 --commit
# С datetime-точностью: повторный запуск не дублирует записи
redmine-reporter --commit
```
### `email` — настройка отправки по почте
Секция `email` используется флагом `--send`. Если секция не настроена или `smtp.host`
пуст, `--send` завершится с ошибкой «Email не настроен».
**Все поля:**
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
| `smtp.host` | строка | `""` | Адрес SMTP-сервера |
| `smtp.port` | число | `587` | Порт SMTP |
| `smtp.user` | строка | `""` | Логин для аутентификации |
| `smtp.password` | строка | `""` | Пароль (рекомендуется `${SMTP_PASSWORD}`) |
| `smtp.tls` | bool | `true` | Использовать STARTTLS |
| `from` | строка | `""` | Адрес отправителя |
| `to` | список | `[]` | Основные получатели |
| `cc` | список | `[]` | Копия |
| `bcc` | список | `[]` | Скрытая копия (не отображается в заголовках письма) |
| `subject` | строка | `"Отчёт {author} за {period}"` | Тема письма |
| `body_text` | строка | `"Во вложении отчёт."` | Текст письма (plain text) |
| `attach` | bool | `true` | Прикреплять файл отчёта. Если `false` — только текст |
| `html` | bool | `false` | Добавить HTML-версию тела письма (`multipart/alternative`) |
**Подстановки в `subject` и `body_text`:**
| Плейсхолдер | Описание | Пример |
|---|---|---|
| `{author}` | Имя автора из конфига или `--author` | `Кокос А.А.` |
| `{period}` | Строка диапазона дат | `2026-06-01--2026-06-30` |
**MIME-тип вложения** определяется по расширению файла:
| Расширение | MIME-тип |
|---|---|
| `.xlsx` | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
| `.odt` | `application/vnd.oasis.opendocument.text` |
| `.csv` | `text/csv` |
| `.html` | `text/html` |
| `.json` | `application/json` |
| `.md` | `text/markdown` |
Неизвестное расширение → `application/octet-stream`.
**Пример конфигурации:**
```yaml
email:
html: false
smtp:
host: smtp.example.com
port: 587
user: bot@example.com
password: ${SMTP_PASSWORD}
tls: true
from: bot@example.com
to:
- boss@example.com
- team-lead@example.com
cc:
- manager@example.com
bcc: []
subject: "Отчёт {author} за {period}"
body_text: "Во вложении отчёт за период {period}."
attach: true
```
### `--send` — отправка отчёта по email
Флаг `--send` отправляет сгенерированный отчёт через SMTP сразу после сохранения
в файл. Требует настроенную секцию `email` в YAML-конфиге.
**Что делает:**
1. Генерирует отчёт как обычно.
2. Сохраняет отчёт в файл:
- Если указан `--output` — по явному пути.
- Если `--output` не указан — по шаблону из `output.dir` / `output.filename`.
3. Формирует MIME-письмо:
- Тема, plain-text тело и вложение (если `attach: true`).
- При `email.html: true` — дополнительно HTML-версия тела (`multipart/alternative`), сгенерированная из таблицы отчёта.
4. Отправляет через SMTP с TLS (таймаут 30 секунд).
**Файл отчёта сохраняется до попытки отправки** — при ошибке SMTP файл остаётся
на диске, данные не теряются.
**Ошибки SMTP:**
- Нет соединения → `"Не удалось подключиться к SMTP-серверу host:port"`
- Неверный логин/пароль → `"Ошибка аутентификации SMTP. Проверьте логин и пароль."`
- Таймаут → `"Таймаут соединения с SMTP-сервером."`
- Другая ошибка → `"Ошибка отправки письма: <детали>"`
Все ошибки выводятся в stderr, код возврата 1.
**Примеры:**
```bash
# Отправить отчёт за июнь (сохранится по шаблону output.filename)
redmine-reporter --date 2026-06-01--2026-06-30 --send
# С явным путём
redmine-reporter --date 2026-06-01--2026-06-30 --output ~/report.xlsx --send
# Отправить и зафиксировать период
redmine-reporter --date 2026-06-01--2026-06-30 --send --commit
```
**Совместимость с другими флагами:**
| Комбинация | Поведение |
|---|---|
| `--send` | Сохранить по шаблону → отправить |
| `--send --output X` | Сохранить в X → отправить |
| `--send --commit` | Сохранить → отправить → зафиксировать период |
| `--send` с `email.html: true` | Письмо с plain-text + HTML-таблицей |
| `--send` без `email` в конфиге | Ошибка, exit 1 |
| `--send` при ошибке SMTP | Файл сохранён, ошибка в stderr, exit 1 |
### `output` — путь и имя файла по умолчанию
Секция управляет тем, куда и с каким именем сохраняется отчёт, когда `--output` не содержит полного пути.
**Правила разрешения `--output`:**
| Аргумент `--output` | Поведение |
|---|---|
| `/полный/путь/report.xlsx` | Используется как есть, конфиг игнорируется |
| `xlsx` (bare format: `xlsx`, `odt`, `csv`, `md`, `html`, `json`) | Путь = `output.dir` + `output.filename`, расширение = bare format |
| `/tmp/report` (путь без расширения) | Дописывается `.default_format``/tmp/report.xlsx` |
**Шаблон имени файла:**
`output.filename` поддерживает подстановки:
| Плейсхолдер | Описание | Пример |
|---|---|---|
| `{author}` | Имя автора (пробелы → `_`) | `Кокос_А.А.` |
| `{from}` | Начало периода, `YYYY-MM-DD` | `2026-06-01` |
| `{to}` | Конец периода, `YYYY-MM-DD` | `2026-06-30` |
| `{date}` | Конец периода, `DD_MM_YYYY` | `30_06_2026` |
| `{ext}` | Расширение файла без точки | `xlsx` |
Неизвестные плейсхолдеры остаются в имени как есть.
Примеры:
```yaml
# По умолчанию
filename: "{author}_{from}_{to}.{ext}" # → Кокос_А.А._2026-06-01_2026-06-30.xlsx
# Русский формат даты
filename: "отчёт_{date}.{ext}" # → отчёт_30_06_2026.xlsx
# Без автора, только диапазон
filename: "report_{from}--{to}.{ext}" # → report_2026-06-01--2026-06-30.xlsx
```
### Подстановка переменных окружения
В любом строковом значении YAML можно использовать `${VAR}` — при загрузке
оно заменяется на значение переменной окружения:
```yaml
redmine:
api_key: ${REDMINE_API_KEY}
email:
smtp:
password: ${SMTP_PASSWORD}
```
Это безопаснее, чем хранить секреты plaintext в YAML. Однако plaintext-секреты
**не запрещены** — если вписать `api_key: "abc123"` напрямую, система примет.
Права `0600` — основная защита.
### `report` — настройки содержимого отчёта
Секция управляет тем, что попадает в сгенерированный отчёт.
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
| `no_time` | bool | `false` | Не включать затраченное время в файл отчёта |
| `status_translation` | map[str,str] | `{}` | Переопределение/дополнение перевода статусов |
`report.no_time` применяется только в автоматических режимах (`--commit`, `--send`).
При ручном `--output` YAML-настройка игнорируется — там работает только CLI-флаг `--no-time`.
CLI-флаг `--no-time` всегда имеет приоритет над YAML.
**Примеры:**
```yaml
report:
no_time: true
```
```bash
# Автоматический режим: время не выводится
redmine-reporter --commit
# Ручной режим: report.no_time игнорируется, время выводится
redmine-reporter --output report.odt
# Ручной режим с явным флагом: время не выводится
redmine-reporter --output report.odt --no-time
```
### `report.status_translation` — перевод статусов
По умолчанию статусы Redmine переводятся встроенным словарём
(`New``В работе`, `Closed``Закрыто` и т.д.). Секция
`report.status_translation` переопределяет отдельные переводы и/или
добавляет новые статусы; не указанные здесь статусы переводятся
встроенным словарём, а совсем неизвестные выводятся как есть.
```yaml
report:
status_translation:
"New": "Новая"
"Wait Release": "Ожидает релиза"
"Custom Status": "Кастомный статус"
```
## Разрешение выходного пути
Функция `resolve_output_path()` определяет итоговый путь к файлу:
1. `--output` не указан → консольный вывод.
2. `--output xlsx` (bare format) → путь формируется как `output.dir / output.filename` с подстановкой `{ext}` = bare format и дат из периода.
3. `--output /path/report` (без расширения) → дописывается `.output.default_format`.
4. `--output /path/report.csv` (с расширением) → используется как есть.
5. `--output /path/report.xyz` (неизвестное расширение) → используется как есть, форматтер выбирается по расширению.
## Миграция с `.env` на YAML
### Быстрый старт
```bash
# 1. Генерируем YAML из текущего .env
redmine-reporter --init-config
# 2. Проверяем, что создалось
cat ~/.config/redmine-reporter/config.yml
# 3. Редактируем под себя (шаблон имени, период, etc.)
vim ~/.config/redmine-reporter/config.yml
```
### Что делает `--init-config`
- Читает текущие значения из `.env` и переменных окружения.
- Формирует YAML со всеми секциями (`redmine`, `period`, `output`, `email`).
- Секреты (`REDMINE_API_KEY`, `SMTP_PASSWORD`) записывает как `${VAR}`, если
переменная существует, иначе — пустая строка.
- Создаёт файл с правами `0600`, директорию — с `0700`.
### Флаги миграции
| Флаг | Назначение |
|---|---|
| `--init-config` | Создать YAML и выйти |
| `--init-config --force` | Перезаписать существующий YAML |
| `--config-path PATH` | Сохранить YAML по указанному пути (по умолчанию `~/.config/redmine-reporter/config.yml`) |
Если `DEFAULT_TO_DATE` не задана, а `DEFAULT_FROM_DATE` задана, сгенерированный
YAML будет содержать пустое `default_to`, и при запуске инструмент использует
сегодняшнюю дату.
### Проверка после миграции
```bash
# Запустить без .env в текущей директории
cd /tmp
redmine-reporter --date 2026-06-01--2026-06-30
```
Если отработал — YAML-конфиг читается корректно. Если `REDMINE_URL is required`
проверь права:
```bash
ls -la ~/.config/redmine-reporter/
# config.yml должно быть -rw------- (600)
# директория должна быть drwx------ (700)
```
### Сосуществование `.env` и YAML
Можно оставить оба источника. `.env` имеет **более высокий приоритет**, чем YAML:
```
.env значения переопределяют YAML, если заданы
YAML работает как базовый слой для всего, что не в .env
```
Это safe — если с YAML что-то пойдёт не так, просто положи `.env` обратно.
### Откат
```bash
rm ~/.config/redmine-reporter/config.yml
```
## `.env` (legacy)
Для обратной совместимости `.env` продолжает работать без изменений.
```ini
REDMINE_URL=https://red.eltex.loc
REDMINE_API_KEY=your-api-key
REDMINE_AUTHOR=Кокос А.А.
DEFAULT_FROM_DATE=2026-06-01
DEFAULT_TO_DATE=2026-06-30
```
Если ни `.env`, ни YAML не заданы — используются кодовые дефолты (текущий месяц
как период, стандартная проверка TLS, пустой автор).
## Безопасность
- YAML-конфиг: права `0600`, директория `0700`.
- Права шире `0600` → warning в stderr при каждом запуске.
- Секреты рекомендуется хранить через `${VAR}`, а не plaintext.
- `.env` **не рекомендуется** для постоянных настроек — оставьте его только
для CI/CD или временных переопределений.