Кокос Артем Николаевич 46674ba926 fix: normalize datetimes to aware UTC in dedup
Dedup with precision=datetime crashed with TypeError: redminelib returns
naive created_on/updated_on while the cutoff from _compute_dedup_cutoff
is aware UTC. Normalize at a single point: _parse_datetime now always
returns an aware datetime (naive treated as UTC), matching its docstring.
The dedup cutoff is normalized the same way, and any residual comparison
TypeError is wrapped in RedmineAPIError instead of leaking raw.

Also fix --commit saving last_used.to as naive local time; it now stores
aware UTC (datetime.now(timezone.utc)) so the next run computes a correct
aware cutoff.

Closes #58
2026-07-17 12:11:35 +07:00
2026-01-21 10:44:53 +07:00

redmine-reporter

CLI-инструмент для генерации отчётов по задачам Redmine на основе записей о затраченном времени.

Проект предназначен для внутреннего использования с https://red.eltex.loc/.

Лицензия: MIT.

Возможности

  • Получение time entries текущего или указанного пользователя из Redmine.
  • Авторизация через Redmine API token или логин/пароль.
  • Группировка задач по проекту и версии.
  • Перевод статусов задач на русский язык.
  • Разбивка по типам активности (--by-activity).
  • Вывод в консоль (таблица / компактный вид).
  • Экспорт в ODT, CSV, Markdown, HTML, JSON и Excel (.xlsx).
  • Excel-отчёт с merge-ячейками по проекту/версии, итогами, автошириной, автофильтром и закреплённой шапкой.
  • Сводка по времени (--summary).
  • YAML-конфиг (~/.config/redmine-reporter/config.yml): шаблон имени файла, путь по умолчанию, период, email, настройки содержимого отчёта (report.no_time).
  • Умное разрешение --output: bare-формат (xlsx) → путь по шаблону, без расширения → автодописывание.
  • --commit: автосохранение отчёта в файл + фиксация периода в YAML-конфиге для следующего запуска.
  • --send: отправка отчёта по email через SMTP сразу после генерации.
  • HTML-версия тела письма при --send, если включено в YAML-конфиге (email.html: true).
  • Понятные сообщения об ошибках Redmine API, SMTP и файловой системы.
  • Загрузка альтернативного .env через --config.

Установка

git clone https://git.akokos.ru/artem.kokos/redmine-reporter.git
cd redmine-reporter
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install .

Для разработки:

pip install -e ".[dev]"

Настройка

Источники конфигурации (от высшего приоритета к низшему):

CLI-флаги  >  переменные окружения  >  .env  >  YAML-конфиг  >  кодовые дефолты

YAML-конфиг (основной способ)

# Сгенерировать YAML из текущего .env
redmine-reporter --init-config

# Редактировать под себя
vim ~/.config/redmine-reporter/config.yml

Структура:

redmine:
  url: https://red.eltex.loc
  api_key: ${REDMINE_API_KEY}
  author: "Кокос А.А."
  verify_ssl: true

period:
  precision: date                   # date | datetime
  default_from: "2026-06-01"
  # default_to можно не указывать — конец периода будет сегодня
  default_to: "2026-06-30"
  dynamic: false
  # last_used заполняется --commit (см. docs/CONFIG.md)

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

Шаблон output.filename поддерживает {author}, {from}, {to}, {date} (DD_MM_YYYY), {ext}.

Шаблоны email.subject и email.body_text поддерживают {author}, {period} (строка диапазона, например 2026-06-01--2026-06-30).

Подробнее: docs/CONFIG.md.

.env (legacy)

REDMINE_URL=https://red.eltex.loc/
REDMINE_API_KEY=ваш_api_token
REDMINE_AUTHOR=Иванов Иван Иванович
DEFAULT_FROM_DATE=2026-01-01
# DEFAULT_TO_DATE можно не задавать — тогда конец периода будет сегодня
DEFAULT_TO_DATE=2026-01-31

Переменные окружения:

Переменная Обязательность Описание
REDMINE_URL Да URL Redmine.
REDMINE_API_KEY Да, если нет логина и пароля Redmine API token.
REDMINE_USER Да, если нет токена Логин Redmine.
REDMINE_PASSWORD Да, если нет токена Пароль Redmine.
REDMINE_AUTHOR Нет Имя автора для отчёта.
DEFAULT_FROM_DATE Нет Начальная дата периода по умолчанию (YYYY-MM-DD).
DEFAULT_TO_DATE Нет Конечная дата периода по умолчанию (YYYY-MM-DD). Если не задана, а DEFAULT_FROM_DATE задана — используется сегодняшняя дата.
REDMINE_VERIFY Нет TLS-проверка: true / false / путь к CA bundle.

Использование

source .venv/bin/activate

Основные сценарии

Отчёт за период по умолчанию:

redmine-reporter

Произвольный период:

redmine-reporter --date 2026-02-01--2026-02-28

Другой пользователь:

redmine-reporter --user-id 42
redmine-reporter --user-login ivanov
redmine-reporter --user-name "Иванов И.И."

Переопределить URL / API-ключ:

redmine-reporter --url https://red.example.com --api-key ваш_токен

Альтернативный .env:

redmine-reporter --config /path/to/.env

Компактный / отладочный вывод:

redmine-reporter --compact
redmine-reporter --debug

Экспорт в файл

Явный путь:

redmine-reporter --output report.xlsx
redmine-reporter --output /path/to/report.odt

Только формат (путь и имя берутся из YAML-шаблона):

redmine-reporter --output xlsx          # → output.dir/отчёт_01_07_2026.xlsx
redmine-reporter --output odt           # → output.dir/отчёт_01_07_2026.odt

Путь без расширения (дописывается default_format из конфига):

redmine-reporter --output /tmp/report   # → /tmp/report.xlsx (если default_format: xlsx)

Отправка по email (--send)

Отправить отчёт на email, указанный в YAML-конфиге (секция email):

# Сохранить по шаблону и отправить
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

Если в секции email установить html: true, письмо будет отправлено в двух версиях: plain-text и HTML (таблица отчёта прямо в теле письма). Файл отчёта всё равно прикрепляется, если attach: true.

email:
  html: true

Если секция email не настроена — ошибка с пояснением. При ошибке SMTP файл отчёта остаётся на диске, данные не теряются. Поддерживаются to, cc, bcc, TLS, отключение вложения (attach: false).

Фиксация периода (--commit)

# Сгенерировать, сохранить в файл по шаблону, запомнить период
redmine-reporter --commit

# С явным путём
redmine-reporter --commit --output report.xlsx

# Следующий запуск (без --date) возьмёт следующий период автоматически
redmine-reporter

# При precision=datetime запоминает момент времени
# (предотвращает дублирование записей внутри дня)
redmine-reporter --commit

Сводка и опции

Без времени / с разбивкой по активностям:

redmine-reporter --no-time
redmine-reporter --by-activity
redmine-reporter --by-activity --summary

--no-time можно задать в YAML-конфиге (report.no_time: true), чтобы автоматические режимы (--commit, --send) не включали затраченное время без явного флага. При ручном --output YAML-значение не применяется — только CLI-флаг --no-time.

Сводка:

redmine-reporter --summary

Форматы вывода

Формат Особенности
ODT Заголовок с автором и месяцем, группировка по проекту/версии.
CSV UTF-8 с BOM, полные значения project/version в каждой строке.
Markdown Компактная таблица, повторяющиеся группы скрыты.
HTML Полноценный HTML-документ с meta charset="utf-8".
JSON Массив объектов: project, version, issue_id, subject, status, time.
Excel (.xlsx) Merge cells, колонки Hours/Spent Time, итоги, автоширина, автофильтр, freeze panes.

Полный список флагов

--date DATE          Диапазон дат: YYYY-MM-DD--YYYY-MM-DD
--compact            Компактный текстовый вывод вместо таблицы
--output PATH/FMT    Путь к файлу (.odt/.csv/.md/.html/.json/.xlsx)
                     или bare-формат (xlsx/odt/...) — путь из конфига
--author NAME        Переопределить имя автора
--no-time            Не включать затраченное время в таблицу
--url URL            Переопределить Redmine URL
--api-key KEY        Переопределить Redmine API key
--config PATH        Путь к альтернативному .env-файлу
--verbose            Подробный вывод
--debug              Отладочный вывод
--version            Показать версию и выйти
--summary            Вывести сводку по времени в stderr
--user-id ID         Redmine ID пользователя для отчёта
--user-login LOGIN   Логин пользователя Redmine
--user-name NAME     Полное имя пользователя Redmine
--by-activity        Разбить время по типам активности
--init-config        Сгенерировать YAML-конфиг и выйти
--force              Перезаписать существующий конфиг (с --init-config)
--config-path PATH   Путь к YAML-конфигу (по умолчанию ~/.config/redmine-reporter/config.yml)
--commit             Сохранить отчёт в файл и зафиксировать период в конфиге
--send               Отправить отчёт по email после сохранения

Разработка

Проверки перед коммитом:

pytest
ruff check redmine_reporter tests
ruff format --check redmine_reporter tests
mypy redmine_reporter

Безопасность

  • Не коммитьте .env, API token, пароль или логин.
  • YAML-конфиг имеет права 0600, директория — 0700.
  • Рекомендуется хранить секреты через ${VAR}, а не plaintext.
  • Используйте аккаунт с минимальными правами, достаточными для чтения time entries и задач.
  • Инструмент работает только в режиме чтения и не изменяет данные в Redmine.
Description
Инструмент для генерации отчётов по задачам в Redmine на основе ваших записей о затраченном времени.
Readme MIT 1.1 MiB
2026-07-17 18:39:53 +07:00
Languages
Python 100%