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
22 KiB
Настройка redmine-reporter
Источники конфигурации
Приоритет, от высшего к низшему:
CLI-флаги > переменные окружения > .env > YAML > кодовые дефолты
Если значение не задано на верхнем уровне, берётся уровень ниже. .env и YAML
работают одновременно — можно оставить оба, можно удалить .env после миграции.
YAML-конфиг
Основной файл: ~/.config/redmine-reporter/config.yml.
Создаётся с правами 0600 (владелец: чтение/запись, остальные: доступ запрещён).
Директория ~/.config/redmine-reporter/ — с правами 0700.
Если права файла шире 0600, при запуске выводится предупреждение.
Структура
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 задан, инструмент
использует сегодняшнюю дату в качестве конца периода.
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-конфиг, чтобы следующий запуск автоматически начинался с нового периода.
Что делает:
- Генерирует отчёт как обычно.
- Сохраняет отчёт в файл:
- Если указан
--output— по явному пути. - Если
--outputне указан — по шаблону изoutput.dir/output.filename.
- Если указан
- Записывает
period.last_used.from/period.last_used.toв YAML-конфиг. - При
period.precision: datetimeсохраняет текущий момент времени (ISO с секундами). - При
period.precision: dateсохраняет даты периода.
Поведение period.dynamic:
dynamic: true— следующий запуск (без--date) вычисляет период отlast_used:- Полный календарный месяц → следующий полный месяц.
- Произвольный диапазон → та же длительность, начиная со дня после
last_used.to.
dynamic: false—--commitперезаписываетdefault_from/default_toна использованный период.
Примеры:
# Июнь 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.
Пример конфигурации:
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-конфиге.
Что делает:
- Генерирует отчёт как обычно.
- Сохраняет отчёт в файл:
- Если указан
--output— по явному пути. - Если
--outputне указан — по шаблону изoutput.dir/output.filename.
- Если указан
- Формирует MIME-письмо:
- Тема, plain-text тело и вложение (если
attach: true). - При
email.html: true— дополнительно HTML-версия тела (multipart/alternative), сгенерированная из таблицы отчёта.
- Тема, plain-text тело и вложение (если
- Отправляет через SMTP с TLS (таймаут 30 секунд).
Файл отчёта сохраняется до попытки отправки — при ошибке SMTP файл остаётся на диске, данные не теряются.
Ошибки SMTP:
- Нет соединения →
"Не удалось подключиться к SMTP-серверу host:port" - Неверный логин/пароль →
"Ошибка аутентификации SMTP. Проверьте логин и пароль." - Таймаут →
"Таймаут соединения с SMTP-сервером." - Другая ошибка →
"Ошибка отправки письма: <детали>"
Все ошибки выводятся в stderr, код возврата 1.
Примеры:
# Отправить отчёт за июнь (сохранится по шаблону 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 |
Неизвестные плейсхолдеры остаются в имени как есть.
Примеры:
# По умолчанию
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} — при загрузке
оно заменяется на значение переменной окружения:
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.
Примеры:
report:
no_time: true
# Автоматический режим: время не выводится
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 переопределяет отдельные переводы и/или
добавляет новые статусы; не указанные здесь статусы переводятся
встроенным словарём, а совсем неизвестные выводятся как есть.
report:
status_translation:
"New": "Новая"
"Wait Release": "Ожидает релиза"
"Custom Status": "Кастомный статус"
Разрешение выходного пути
Функция resolve_output_path() определяет итоговый путь к файлу:
--outputне указан → консольный вывод.--output xlsx(bare format) → путь формируется какoutput.dir / output.filenameс подстановкой{ext}= bare format и дат из периода.--output /path/report(без расширения) → дописывается.output.default_format.--output /path/report.csv(с расширением) → используется как есть.--output /path/report.xyz(неизвестное расширение) → используется как есть, форматтер выбирается по расширению.
Миграция с .env на YAML
Быстрый старт
# 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, и при запуске инструмент использует
сегодняшнюю дату.
Проверка после миграции
# Запустить без .env в текущей директории
cd /tmp
redmine-reporter --date 2026-06-01--2026-06-30
Если отработал — YAML-конфиг читается корректно. Если REDMINE_URL is required —
проверь права:
ls -la ~/.config/redmine-reporter/
# config.yml должно быть -rw------- (600)
# директория должна быть drwx------ (700)
Сосуществование .env и YAML
Можно оставить оба источника. .env имеет более высокий приоритет, чем YAML:
.env значения переопределяют YAML, если заданы
YAML работает как базовый слой для всего, что не в .env
Это safe — если с YAML что-то пойдёт не так, просто положи .env обратно.
Откат
rm ~/.config/redmine-reporter/config.yml
.env (legacy)
Для обратной совместимости .env продолжает работать без изменений.
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 или временных переопределений.