docs: rewrite README, add user guide, sync CONFIG.md with code
- README.md: 324 -> 106 lines, clickable TOC, quick start, formats table, full check suite + CI; flags/env/YAML reference moved to docs - docs/USER_GUIDE.md: new user guide (install, periods, users, export, email, monthly --commit cycle, 21-flag reference, troubleshooting) - docs/CONFIG.md: clickable TOC + accuracy fixes verified against code (dedup logic was described backwards, missing report section in --init-config, conditional STARTTLS, full --config-path role, datetime --date ranges, --config override nuance, comment loss on --commit, default_to without default_from is ignored) - client.py: dedup docstring/comment now match actual behavior
This commit is contained in:
314
README.md
314
README.md
@@ -2,33 +2,32 @@
|
|||||||
|
|
||||||
[](https://git.akokos.ru/artem.kokos/redmine-reporter/actions)
|
[](https://git.akokos.ru/artem.kokos/redmine-reporter/actions)
|
||||||
|
|
||||||
CLI-инструмент для генерации отчётов по задачам Redmine на основе записей о затраченном времени.
|
- [Возможности](#возможности)
|
||||||
|
- [Установка](#установка)
|
||||||
|
- [Быстрый старт](#быстрый-старт)
|
||||||
|
- [Документация](#документация)
|
||||||
|
- [Форматы вывода](#форматы-вывода)
|
||||||
|
- [Разработка](#разработка)
|
||||||
|
- [Безопасность](#безопасность)
|
||||||
|
- [Лицензия](#лицензия)
|
||||||
|
|
||||||
Проект предназначен для внутреннего использования с `https://red.eltex.loc/`.
|
CLI-инструмент для генерации отчётов по задачам Redmine на основе записей о затраченном времени. Читает time entries текущего или указанного пользователя, группирует задачи по проекту и версии, выводит отчёт в консоль или экспортирует в файл. Предназначен для внутреннего использования с `https://red.eltex.loc/`.
|
||||||
|
|
||||||
Лицензия: MIT.
|
|
||||||
|
|
||||||
## Возможности
|
## Возможности
|
||||||
|
|
||||||
- Получение time entries **текущего** или **указанного** пользователя из Redmine.
|
- Отчёт по time entries текущего или указанного пользователя (`--user-id`, `--user-login`, `--user-name`).
|
||||||
- Авторизация через Redmine API token или логин/пароль.
|
- Группировка задач по проекту и версии, перевод статусов на русский язык.
|
||||||
- Группировка задач по проекту и версии.
|
- Вывод в консоль (таблица или компактный вид) и экспорт в ODT, CSV, Markdown, HTML, JSON, XLSX.
|
||||||
- Перевод статусов задач на русский язык.
|
- Разбивка времени по типам активности (`--by-activity`), сводка (`--summary`), скрытие времени (`--no-time`).
|
||||||
- Разбивка по типам активности (`--by-activity`).
|
- Гибкий выбор периода: `--date`, переменные окружения, YAML-конфиг, по умолчанию — текущий месяц.
|
||||||
- Вывод в консоль (таблица / компактный вид).
|
- `--commit`: сохранение отчёта в файл и фиксация периода в конфиге для следующего запуска.
|
||||||
- Экспорт в ODT, CSV, Markdown, HTML, JSON и Excel (.xlsx).
|
- `--send`: отправка отчёта по email через SMTP (plain-text или HTML-письмо).
|
||||||
- Excel-отчёт с merge-ячейками по проекту/версии, итогами, автошириной, автофильтром и закреплённой шапкой.
|
- YAML-конфиг с секретами через `${VAR}`; приоритет: CLI-флаги > env > .env > YAML > дефолты (нюанс с `--config` — см. docs/CONFIG.md).
|
||||||
- Сводка по времени (`--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`.
|
|
||||||
|
|
||||||
## Установка
|
## Установка
|
||||||
|
|
||||||
|
Требуется Python >= 3.10.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.akokos.ru/artem.kokos/redmine-reporter.git
|
git clone https://git.akokos.ru/artem.kokos/redmine-reporter.git
|
||||||
cd redmine-reporter
|
cd redmine-reporter
|
||||||
@@ -44,263 +43,40 @@ pip install .
|
|||||||
pip install -e ".[dev]"
|
pip install -e ".[dev]"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Настройка
|
## Быстрый старт
|
||||||
|
|
||||||
Источники конфигурации (от высшего приоритета к низшему):
|
|
||||||
|
|
||||||
```
|
|
||||||
CLI-флаги > переменные окружения > .env > YAML-конфиг > кодовые дефолты
|
|
||||||
```
|
|
||||||
|
|
||||||
### YAML-конфиг (основной способ)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Сгенерировать YAML из текущего .env
|
# Сгенерировать конфиг ~/.config/redmine-reporter/config.yml
|
||||||
redmine-reporter --init-config
|
redmine-reporter --init-config
|
||||||
|
|
||||||
# Редактировать под себя
|
# Заполнить redmine.url, redmine.api_key (или ${REDMINE_API_KEY}), redmine.author
|
||||||
vim ~/.config/redmine-reporter/config.yml
|
vim ~/.config/redmine-reporter/config.yml
|
||||||
```
|
|
||||||
|
|
||||||
Структура:
|
# Отчёт за текущий месяц в консоль
|
||||||
|
|
||||||
```yaml
|
|
||||||
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](docs/CONFIG.md).
|
|
||||||
|
|
||||||
### `.env` (legacy)
|
|
||||||
|
|
||||||
```ini
|
|
||||||
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`). Если не задана (и нет в YAML) — 1-е число текущего месяца. |
|
|
||||||
| `DEFAULT_TO_DATE` | Нет | Конечная дата периода по умолчанию (`YYYY-MM-DD`). Если не задана, а `DEFAULT_FROM_DATE` задана — используется сегодняшняя дата. |
|
|
||||||
| `REDMINE_VERIFY` | Нет | TLS-проверка: `true` (по умолчанию — стандартная проверка средствами requests) / `false` / путь к CA bundle. Семантика совпадает с `redmine.verify_ssl` в YAML. |
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source .venv/bin/activate
|
|
||||||
```
|
|
||||||
|
|
||||||
### Основные сценарии
|
|
||||||
|
|
||||||
Отчёт за период по умолчанию — текущий месяц (с 1-го числа по сегодня),
|
|
||||||
если период не задан через `--date`, env или YAML:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter
|
|
||||||
```
|
|
||||||
|
|
||||||
Произвольный период:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --date 2026-02-01--2026-02-28
|
|
||||||
```
|
|
||||||
|
|
||||||
Другой пользователь:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --user-id 42
|
|
||||||
redmine-reporter --user-login ivanov
|
|
||||||
redmine-reporter --user-name "Иванов И.И."
|
|
||||||
```
|
|
||||||
|
|
||||||
Переопределить URL / API-ключ:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --url https://red.example.com --api-key ваш_токен
|
|
||||||
```
|
|
||||||
|
|
||||||
Альтернативный `.env`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --config /path/to/.env
|
|
||||||
```
|
|
||||||
|
|
||||||
Компактный / отладочный вывод:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --compact
|
|
||||||
redmine-reporter --debug
|
|
||||||
```
|
|
||||||
|
|
||||||
### Экспорт в файл
|
|
||||||
|
|
||||||
Явный путь:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --output report.xlsx
|
|
||||||
redmine-reporter --output /path/to/report.odt
|
|
||||||
```
|
|
||||||
|
|
||||||
Только формат (путь и имя берутся из YAML-шаблона):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --output xlsx # → output.dir/отчёт_01_07_2026.xlsx
|
|
||||||
redmine-reporter --output odt # → output.dir/отчёт_01_07_2026.odt
|
|
||||||
```
|
|
||||||
|
|
||||||
Путь без расширения (дописывается `default_format` из конфига):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --output /tmp/report # → /tmp/report.xlsx (если default_format: xlsx)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Отправка по email (`--send`)
|
|
||||||
|
|
||||||
Отправить отчёт на email, указанный в YAML-конфиге (секция `email`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Сохранить по шаблону и отправить
|
|
||||||
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`.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
email:
|
|
||||||
html: true
|
|
||||||
```
|
|
||||||
|
|
||||||
Если секция `email` не настроена — ошибка с пояснением. При ошибке SMTP файл отчёта остаётся на диске, данные не теряются. Поддерживаются `to`, `cc`, `bcc`, TLS, отключение вложения (`attach: false`).
|
|
||||||
|
|
||||||
### Фиксация периода (`--commit`)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Сгенерировать, сохранить в файл по шаблону, запомнить период
|
|
||||||
redmine-reporter --commit
|
|
||||||
|
|
||||||
# С явным путём
|
|
||||||
redmine-reporter --commit --output report.xlsx
|
|
||||||
|
|
||||||
# Следующий запуск (без --date) возьмёт следующий период автоматически
|
|
||||||
redmine-reporter
|
redmine-reporter
|
||||||
|
|
||||||
# При precision=datetime запоминает момент времени
|
# Сохранить в файл и зафиксировать период для следующего запуска
|
||||||
# (предотвращает дублирование записей внутри дня)
|
|
||||||
redmine-reporter --commit
|
redmine-reporter --commit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Сводка и опции
|
## Документация
|
||||||
|
|
||||||
Без времени / с разбивкой по активностям:
|
- [docs/USER_GUIDE.md](docs/USER_GUIDE.md) — руководство пользователя: сценарии использования, справочник всех CLI-флагов, устранение неполадок.
|
||||||
|
- [docs/CONFIG.md](docs/CONFIG.md) — справочник конфигурации: YAML-структура, переменные окружения, приоритеты, безопасность.
|
||||||
```bash
|
|
||||||
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`.
|
|
||||||
|
|
||||||
Сводка:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
redmine-reporter --summary
|
|
||||||
```
|
|
||||||
|
|
||||||
## Форматы вывода
|
## Форматы вывода
|
||||||
|
|
||||||
| Формат | Особенности |
|
| Формат | Особенности |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **ODT** | Заголовок с автором и месяцем, группировка по проекту/версии. |
|
| Консоль | Таблица или компактный вид (`--compact`). |
|
||||||
| **CSV** | UTF-8 с BOM, полные значения `project`/`version` в каждой строке. |
|
| ODT | Требуется `odfpy`; формирование по шаблону. |
|
||||||
| **Markdown** | Компактная таблица, повторяющиеся группы скрыты. |
|
| CSV | UTF-8 с BOM; полные значения `project`/`version` в каждой строке. |
|
||||||
| **HTML** | Полноценный HTML-документ с `meta charset="utf-8"`. |
|
| Markdown | Компактная таблица. |
|
||||||
| **JSON** | Массив объектов: `project`, `version`, `issue_id`, `subject`, `status`, `time`. |
|
| HTML | Полный HTML-документ; объединение ячеек групп через rowspan. |
|
||||||
| **Excel (.xlsx)** | Merge cells, колонки `Hours`/`Spent Time`, итоги, автоширина, автофильтр, freeze panes. |
|
| JSON | Объекты `project`, `version`, `issue_id`, `subject`, `status`, `time` + опционально `activities`. |
|
||||||
|
| XLSX | Объединение ячеек по проекту/версии, итоги, автоширина (максимум 80), автофильтр, freeze panes. |
|
||||||
|
|
||||||
## Полный список флагов
|
Нюанс `--no-time`: физически удаляет колонку времени только CSV; в XLSX колонки остаются, но пустыми и без итогов; в остальных форматах — пустые значения.
|
||||||
|
|
||||||
```
|
|
||||||
--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 после сохранения
|
|
||||||
```
|
|
||||||
|
|
||||||
## Разработка
|
## Разработка
|
||||||
|
|
||||||
@@ -308,17 +84,23 @@ redmine-reporter --summary
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
pytest
|
pytest
|
||||||
|
isort --check-only redmine_reporter tests
|
||||||
|
black --check redmine_reporter tests
|
||||||
ruff check redmine_reporter tests
|
ruff check redmine_reporter tests
|
||||||
ruff format --check redmine_reporter tests
|
ruff format --check redmine_reporter tests
|
||||||
mypy redmine_reporter
|
mypy redmine_reporter
|
||||||
```
|
```
|
||||||
|
|
||||||
|
CI — Gitea Actions (`.gitea/workflows/checks.yaml`): все шесть проверок на матрице Python 3.10–3.13.
|
||||||
|
|
||||||
## Безопасность
|
## Безопасность
|
||||||
|
|
||||||
- Не коммитьте `.env`, API token, пароль или логин.
|
- `REDMINE_URL` обязан использовать HTTPS: валидация отклоняет остальное, API-ключ передаётся в заголовках.
|
||||||
- YAML-конфиг имеет права `0600`, директория — `0700`.
|
- `verify_ssl` / `REDMINE_VERIFY`: `true` (по умолчанию), `false` (предупреждение о MITM-риске при старте) или путь к CA-bundle.
|
||||||
- Рекомендуется хранить секреты через `${VAR}`, а не plaintext.
|
- Конфиг создаётся с правами `0600`, директория — `0700`; при более широких правах выводится предупреждение.
|
||||||
- Используйте аккаунт с минимальными правами, достаточными для чтения time entries и задач.
|
- Секреты храните через `${VAR}` в YAML или в переменных окружения, не в открытом виде.
|
||||||
- Инструмент работает только в режиме чтения и не изменяет данные в Redmine.
|
- Инструмент только читает данные из Redmine и ничего в нём не изменяет.
|
||||||
- `REDMINE_URL` обязан использовать HTTPS: API-ключ передаётся в заголовках запроса, и без TLS он может быть перехвачен.
|
|
||||||
- `REDMINE_VERIFY=false` (или `verify_ssl: false`) отключает проверку TLS-сертификата — соединение уязвимо для MITM-атак; при старте выводится предупреждение. Используйте только в доверенной сети.
|
## Лицензия
|
||||||
|
|
||||||
|
MIT, см. [LICENSE](LICENSE).
|
||||||
|
|||||||
@@ -1,5 +1,33 @@
|
|||||||
# Настройка redmine-reporter
|
# Настройка redmine-reporter
|
||||||
|
|
||||||
|
## Оглавление
|
||||||
|
|
||||||
|
- [Источники конфигурации](#источники-конфигурации)
|
||||||
|
- [YAML-конфиг](#yaml-конфиг)
|
||||||
|
- [Структура](#структура)
|
||||||
|
- [`redmine.verify_ssl` — проверка TLS-сертификата](#redmineverify_ssl--проверка-tls-сертификата)
|
||||||
|
- [`period.precision` — точность периода](#periodprecision--точность-периода)
|
||||||
|
- [`period.default_to` — необязательное окончание периода](#perioddefault_to--необязательное-окончание-периода)
|
||||||
|
- [Период по умолчанию — текущий месяц](#период-по-умолчанию--текущий-месяц)
|
||||||
|
- [`--date` — формат диапазона](#--date--формат-диапазона)
|
||||||
|
- [`--commit` — автофиксация периода](#--commit--автофиксация-периода)
|
||||||
|
- [`email` — настройка отправки по почте](#email--настройка-отправки-по-почте)
|
||||||
|
- [`--send` — отправка отчёта по email](#--send--отправка-отчёта-по-email)
|
||||||
|
- [`output` — путь и имя файла по умолчанию](#output--путь-и-имя-файла-по-умолчанию)
|
||||||
|
- [Подстановка переменных окружения](#подстановка-переменных-окружения)
|
||||||
|
- [`report` — настройки содержимого отчёта](#report--настройки-содержимого-отчёта)
|
||||||
|
- [`report.status_translation` — перевод статусов](#reportstatus_translation--перевод-статусов)
|
||||||
|
- [Разрешение выходного пути](#разрешение-выходного-пути)
|
||||||
|
- [Миграция с `.env` на YAML](#миграция-с-env-на-yaml)
|
||||||
|
- [Быстрый старт](#быстрый-старт)
|
||||||
|
- [Что делает `--init-config`](#что-делает---init-config)
|
||||||
|
- [Флаги миграции](#флаги-миграции)
|
||||||
|
- [Проверка после миграции](#проверка-после-миграции)
|
||||||
|
- [Сосуществование `.env` и YAML](#сосуществование-env-и-yaml)
|
||||||
|
- [Откат](#откат)
|
||||||
|
- [`.env` (legacy)](#env-legacy)
|
||||||
|
- [Безопасность](#безопасность)
|
||||||
|
|
||||||
## Источники конфигурации
|
## Источники конфигурации
|
||||||
|
|
||||||
Приоритет, от высшего к низшему:
|
Приоритет, от высшего к низшему:
|
||||||
@@ -8,6 +36,9 @@
|
|||||||
CLI-флаги > переменные окружения > .env > YAML > кодовые дефолты
|
CLI-флаги > переменные окружения > .env > YAML > кодовые дефолты
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Нюанс с `override` при автозагрузке `.env` и `--config` — см.
|
||||||
|
[Сосуществование `.env` и YAML](#сосуществование-env-и-yaml).
|
||||||
|
|
||||||
Если значение не задано на верхнем уровне, берётся уровень ниже. `.env` и YAML
|
Если значение не задано на верхнем уровне, берётся уровень ниже. `.env` и YAML
|
||||||
работают одновременно — можно оставить оба, можно удалить `.env` после миграции.
|
работают одновременно — можно оставить оба, можно удалить `.env` после миграции.
|
||||||
|
|
||||||
@@ -86,7 +117,7 @@ email:
|
|||||||
Определяет, как вычисляется следующий период после фиксации:
|
Определяет, как вычисляется следующий период после фиксации:
|
||||||
|
|
||||||
- `date` (по умолчанию) — период с точностью до дня. Следующий запуск (после `--commit`) начинается со следующего дня.
|
- `date` (по умолчанию) — период с точностью до дня. Следующий запуск (после `--commit`) начинается со следующего дня.
|
||||||
- `datetime` — период с точностью до секунды. При повторном запуске time entries с `created_on` и `updated_on` ранее `last_used.to` исключаются (AND-логика: запись исключается только если **оба** поля раньше cutoff). Это предотвращает дублирование при отправке отчёта внутри рабочего дня.
|
- `datetime` — период с точностью до секунды. При повторном запуске time entries, у которых `created_on` или `updated_on` раньше `last_used.to`, исключаются: запись сохраняется, только если **оба** поля не раньше cutoff (AND-логика). Записи без дат (оба поля отсутствуют) сохраняются; если задано только одно поле, проверяется оно. Это предотвращает дублирование при отправке отчёта внутри рабочего дня. Cutoff применяется всегда, когда вычислен (`precision: datetime` и задан `last_used.to`) — в том числе при явном `--date`.
|
||||||
|
|
||||||
`last_used.from` / `last_used.to` записываются автоматически при `--commit`. Вручную редактировать не требуется.
|
`last_used.from` / `last_used.to` записываются автоматически при `--commit`. Вручную редактировать не требуется.
|
||||||
|
|
||||||
@@ -107,6 +138,10 @@ period:
|
|||||||
Аналогично работает `DEFAULT_TO_DATE`: если переменная не задана, а
|
Аналогично работает `DEFAULT_TO_DATE`: если переменная не задана, а
|
||||||
`DEFAULT_FROM_DATE` задана, конец периода = сегодня.
|
`DEFAULT_FROM_DATE` задана, конец периода = сегодня.
|
||||||
|
|
||||||
|
Обратное не работает: `period.default_to` без `period.default_from` (и
|
||||||
|
`DEFAULT_TO_DATE` без `DEFAULT_FROM_DATE`) игнорируется — период определяется
|
||||||
|
остальными источниками.
|
||||||
|
|
||||||
### Период по умолчанию — текущий месяц
|
### Период по умолчанию — текущий месяц
|
||||||
|
|
||||||
Если период не задан ни одним из источников (`--date`, `DEFAULT_FROM_DATE`,
|
Если период не задан ни одним из источников (`--date`, `DEFAULT_FROM_DATE`,
|
||||||
@@ -114,6 +149,17 @@ period:
|
|||||||
1-е число текущего месяца, конец — сегодняшняя дата. Период вычисляется
|
1-е число текущего месяца, конец — сегодняшняя дата. Период вычисляется
|
||||||
на момент запуска и не хранится в коде или конфиге.
|
на момент запуска и не хранится в коде или конфиге.
|
||||||
|
|
||||||
|
### `--date` — формат диапазона
|
||||||
|
|
||||||
|
Флаг `--date` принимает два формата:
|
||||||
|
|
||||||
|
- Даты: `YYYY-MM-DD--YYYY-MM-DD` (например, `2026-06-01--2026-06-30`).
|
||||||
|
- Datetime с секундами: `YYYY-MM-DDTHH:MM:SS--YYYY-MM-DDTHH:MM:SS`
|
||||||
|
(например, `2026-06-30T09:00:00--2026-06-30T12:00:00`).
|
||||||
|
|
||||||
|
Обе границы должны быть в одном формате — смешанная точность отвергается.
|
||||||
|
Начало позже конца (`start > end`) — ошибка.
|
||||||
|
|
||||||
### `--commit` — автофиксация периода
|
### `--commit` — автофиксация периода
|
||||||
|
|
||||||
Флаг `--commit` сохраняет использованный период в YAML-конфиг, чтобы следующий запуск автоматически начинался с нового периода.
|
Флаг `--commit` сохраняет использованный период в YAML-конфиг, чтобы следующий запуск автоматически начинался с нового периода.
|
||||||
@@ -135,6 +181,9 @@ period:
|
|||||||
- Произвольный диапазон → та же длительность, начиная со дня после `last_used.to`.
|
- Произвольный диапазон → та же длительность, начиная со дня после `last_used.to`.
|
||||||
- `dynamic: false` — `--commit` перезаписывает `default_from`/`default_to` на использованный период.
|
- `dynamic: false` — `--commit` перезаписывает `default_from`/`default_to` на использованный период.
|
||||||
|
|
||||||
|
**Нюанс:** при `--commit` YAML-файл перезаписывается целиком через `yaml.dump` —
|
||||||
|
пользовательские комментарии и ручное форматирование в файле теряются.
|
||||||
|
|
||||||
**Примеры:**
|
**Примеры:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -228,7 +277,8 @@ email:
|
|||||||
3. Формирует MIME-письмо:
|
3. Формирует MIME-письмо:
|
||||||
- Тема, plain-text тело и вложение (если `attach: true`).
|
- Тема, plain-text тело и вложение (если `attach: true`).
|
||||||
- При `email.html: true` — дополнительно HTML-версия тела (`multipart/alternative`), сгенерированная из таблицы отчёта.
|
- При `email.html: true` — дополнительно HTML-версия тела (`multipart/alternative`), сгенерированная из таблицы отчёта.
|
||||||
4. Отправляет через SMTP с TLS (таймаут 30 секунд).
|
4. Отправляет через SMTP (таймаут 30 секунд). STARTTLS включается только при
|
||||||
|
`email.smtp.tls: true`; аутентификация — только если задан `email.smtp.user`.
|
||||||
|
|
||||||
**Файл отчёта сохраняется до попытки отправки** — при ошибке SMTP файл остаётся
|
**Файл отчёта сохраняется до попытки отправки** — при ошибке SMTP файл остаётся
|
||||||
на диске, данные не теряются.
|
на диске, данные не теряются.
|
||||||
@@ -398,7 +448,8 @@ vim ~/.config/redmine-reporter/config.yml
|
|||||||
### Что делает `--init-config`
|
### Что делает `--init-config`
|
||||||
|
|
||||||
- Читает текущие значения из `.env` и переменных окружения.
|
- Читает текущие значения из `.env` и переменных окружения.
|
||||||
- Формирует YAML со всеми секциями (`redmine`, `period`, `output`, `email`).
|
- Формирует YAML со всеми секциями (`redmine`, `period`, `output`, `report`, `email`).
|
||||||
|
- В конец файла дописывает закомментированный пример `report.status_translation`.
|
||||||
- Секреты (`REDMINE_API_KEY`, `SMTP_PASSWORD`) записывает как `${VAR}`, если
|
- Секреты (`REDMINE_API_KEY`, `SMTP_PASSWORD`) записывает как `${VAR}`, если
|
||||||
переменная существует, иначе — пустая строка.
|
переменная существует, иначе — пустая строка.
|
||||||
- Создаёт файл с правами `0600`, директорию — с `0700`.
|
- Создаёт файл с правами `0600`, директорию — с `0700`.
|
||||||
@@ -409,7 +460,7 @@ vim ~/.config/redmine-reporter/config.yml
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `--init-config` | Создать YAML и выйти |
|
| `--init-config` | Создать YAML и выйти |
|
||||||
| `--init-config --force` | Перезаписать существующий YAML |
|
| `--init-config --force` | Перезаписать существующий YAML |
|
||||||
| `--config-path PATH` | Сохранить YAML по указанному пути (по умолчанию `~/.config/redmine-reporter/config.yml`) |
|
| `--config-path PATH` | Путь к YAML-конфигу: загрузка при запуске, запись при `--init-config` и `--commit` (по умолчанию `~/.config/redmine-reporter/config.yml`) |
|
||||||
|
|
||||||
Если `DEFAULT_TO_DATE` не задана, а `DEFAULT_FROM_DATE` задана, сгенерированный
|
Если `DEFAULT_TO_DATE` не задана, а `DEFAULT_FROM_DATE` задана, сгенерированный
|
||||||
YAML будет содержать пустое `default_to`, и при запуске инструмент использует
|
YAML будет содержать пустое `default_to`, и при запуске инструмент использует
|
||||||
@@ -441,6 +492,10 @@ ls -la ~/.config/redmine-reporter/
|
|||||||
YAML работает как базовый слой для всего, что не в .env
|
YAML работает как базовый слой для всего, что не в .env
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Автозагрузка `.env` (без `--config`) не перебивает реальные переменные
|
||||||
|
окружения (`override=False`). Исключение — `--config FILE`: указанный `.env`
|
||||||
|
загружается с `override=True` и перебивает переменные окружения (но не CLI-флаги).
|
||||||
|
|
||||||
Это safe — если с YAML что-то пойдёт не так, просто положи `.env` обратно.
|
Это safe — если с YAML что-то пойдёт не так, просто положи `.env` обратно.
|
||||||
|
|
||||||
### Откат
|
### Откат
|
||||||
|
|||||||
304
docs/USER_GUIDE.md
Normal file
304
docs/USER_GUIDE.md
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
# Руководство пользователя redmine-reporter
|
||||||
|
|
||||||
|
## Оглавление
|
||||||
|
|
||||||
|
- [Установка](#установка)
|
||||||
|
- [Первоначальная настройка](#первоначальная-настройка)
|
||||||
|
- [Выбор периода](#выбор-периода)
|
||||||
|
- [Отчёт за другого пользователя](#отчёт-за-другого-пользователя)
|
||||||
|
- [Экспорт в файл](#экспорт-в-файл)
|
||||||
|
- [Содержимое отчёта](#содержимое-отчёта)
|
||||||
|
- [Отправка по email](#отправка-по-email)
|
||||||
|
- [Ежемесячный цикл с --commit](#ежемесячный-цикл-с---commit)
|
||||||
|
- [Справочник флагов](#справочник-флагов)
|
||||||
|
- [Устранение неполадок](#устранение-неполадок)
|
||||||
|
- [См. также](#см-также)
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
Требуется Python >= 3.10.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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 .
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --version
|
||||||
|
```
|
||||||
|
|
||||||
|
Перед каждым запуском активируйте окружение: `source .venv/bin/activate`.
|
||||||
|
|
||||||
|
## Первоначальная настройка
|
||||||
|
|
||||||
|
1. Убедитесь, что в текущей директории есть `.env` с `REDMINE_URL` и `REDMINE_API_KEY` (или задайте эти переменные в окружении).
|
||||||
|
2. Сгенерируйте YAML-конфиг:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --init-config
|
||||||
|
```
|
||||||
|
|
||||||
|
Результат: файл `~/.config/redmine-reporter/config.yml` (права `0600`, директория `0700`) со всеми секциями и закомментированным примером `report.status_translation`. Секреты, заданные в окружении, записываются как `${VAR}`.
|
||||||
|
|
||||||
|
3. Если файл уже существует — ошибка; для перезаписи добавьте `--force`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --init-config --force
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Нестандартное расположение конфига:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --init-config --config-path /etc/redmine-reporter/config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
`--config-path` задаёт путь и для загрузки YAML, и для записи при `--init-config` и `--commit`.
|
||||||
|
|
||||||
|
5. Откройте файл и заполните минимум: `redmine.url`, `redmine.api_key`, `redmine.author`.
|
||||||
|
|
||||||
|
`--init-config` несовместим с флагами отчёта (`--date`, `--output`, `--compact`, `--summary`, `--user-id`/`--user-login`/`--user-name`, `--no-time`, `--by-activity`, `--send`) — указывайте его отдельно.
|
||||||
|
|
||||||
|
Полный референс всех секций и переменных: [CONFIG.md](CONFIG.md).
|
||||||
|
|
||||||
|
## Выбор периода
|
||||||
|
|
||||||
|
Источник периода выбирается по приоритету (от высшего к низшему):
|
||||||
|
|
||||||
|
1. `--date` — явный диапазон.
|
||||||
|
2. `DEFAULT_FROM_DATE` (env/.env) — конец: `DEFAULT_TO_DATE` или сегодня. `DEFAULT_TO_DATE` без `DEFAULT_FROM_DATE` игнорируется.
|
||||||
|
3. `period.dynamic: true` + `period.last_used` в YAML — следующий период после `last_used`:
|
||||||
|
- последний период — полный календарный месяц → следующий полный месяц;
|
||||||
|
- произвольный диапазон → та же длительность, начиная со дня после `last_used.to`;
|
||||||
|
- при `precision: datetime` → та же длительность, сдвинутая на 1 секунду после `last_used.to`.
|
||||||
|
4. `period.default_from` в YAML — конец: `default_to` или сегодня. `default_to` без `default_from` игнорируется.
|
||||||
|
5. Ничего не задано → текущий месяц: с 1-го числа по сегодня.
|
||||||
|
|
||||||
|
Форматы `--date`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Даты
|
||||||
|
redmine-reporter --date 2026-06-01--2026-06-30
|
||||||
|
|
||||||
|
# Дата-время
|
||||||
|
redmine-reporter --date 2026-06-01T09:00:00--2026-06-01T18:00:00
|
||||||
|
```
|
||||||
|
|
||||||
|
Смешанная точность (дата + дата-время в одном диапазоне) и диапазон, где начало позже конца, отвергаются с ошибкой.
|
||||||
|
|
||||||
|
## Отчёт за другого пользователя
|
||||||
|
|
||||||
|
Без флагов отчёт строится за текущего пользователя.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --user-id 42
|
||||||
|
redmine-reporter --user-login ivanov
|
||||||
|
redmine-reporter --user-name "Иванов Иван Иванович"
|
||||||
|
```
|
||||||
|
|
||||||
|
Правила:
|
||||||
|
|
||||||
|
- Флаги `--user-id`, `--user-login`, `--user-name` взаимоисключающие: укажите только один, иначе ошибка.
|
||||||
|
- Все три принимают одно значение. Число или строка из цифр → поиск по ID. Иначе — точное регистрозависимое совпадение логина.
|
||||||
|
- Если по логину совпадений нет — поиск по имени. Одно совпадение → пользователь найден.
|
||||||
|
- Несколько совпадений по имени → ошибка «Multiple users match...» со списком логинов (при дублях логина — ID).
|
||||||
|
- Ноль совпадений → ошибка «User '...' not found...».
|
||||||
|
|
||||||
|
## Экспорт в файл
|
||||||
|
|
||||||
|
Флаг `--output` определяется по четырём правилам:
|
||||||
|
|
||||||
|
| Аргумент | Результат |
|
||||||
|
| --- | --- |
|
||||||
|
| `--output xlsx` (bare-формат: `xlsx`, `odt`, `csv`, `md`, `html`, `json`, регистронезависимо) | Путь = `output.dir` + шаблон `output.filename` из YAML, расширение = указанный формат |
|
||||||
|
| `--output /tmp/report` (путь без расширения) | Дописывается `.` + `output.default_format` → `/tmp/report.xlsx` |
|
||||||
|
| `--output /tmp/report.csv` (путь с любым расширением) | Используется как есть |
|
||||||
|
| `--output /tmp/report.xyz` (неизвестное расширение) | Ошибка «Неизвестный формат файла: '.xyz'. Поддерживаются: .odt, .csv, .md, .html, .json, .xlsx», exit 1 |
|
||||||
|
|
||||||
|
Формат `.odt` требует установленного пакета `odfpy` (устанавливается по умолчанию) — иначе отдельная ошибка.
|
||||||
|
|
||||||
|
Примеры:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --output xlsx # по шаблону в output.dir
|
||||||
|
redmine-reporter --output 2026-06-report # → 2026-06-report.xlsx (если default_format: xlsx)
|
||||||
|
redmine-reporter --output reports/june.csv # как есть
|
||||||
|
```
|
||||||
|
|
||||||
|
Плейсхолдеры `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` |
|
||||||
|
|
||||||
|
Неизвестные плейсхолдеры остаются в имени как есть.
|
||||||
|
|
||||||
|
Нюансы с путями:
|
||||||
|
|
||||||
|
- `~` в явном `--output` раскрывает шелл, а не программа: `redmine-reporter --output ~/report.xlsx` работает, а в кавычках (`"~/report.xlsx"`) — нет.
|
||||||
|
- `expanduser` применяется только к `output.dir` из YAML.
|
||||||
|
|
||||||
|
## Содержимое отчёта
|
||||||
|
|
||||||
|
### `--no-time`
|
||||||
|
|
||||||
|
Убирает затраченное время из отчёта. Физически удаляет колонку времени только CSV. XLSX, HTML, ODT, Markdown, JSON и консольный вывод оставляют пустые колонки/поля; в XLSX дополнительно пропадают строки итогов по версиям/проектам/всего.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --output report.xlsx --no-time
|
||||||
|
```
|
||||||
|
|
||||||
|
YAML-настройка `report.no_time` действует только в автоматических режимах (`--commit`, `--send`). При ручном `--output` работает только CLI-флаг.
|
||||||
|
|
||||||
|
### `--by-activity`
|
||||||
|
|
||||||
|
Разбивка затраченного времени по типам активности. В JSON-отчёте добавляется поле `activities`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --by-activity
|
||||||
|
```
|
||||||
|
|
||||||
|
### `--summary`
|
||||||
|
|
||||||
|
Печатает сводку в stderr (после отчёта):
|
||||||
|
|
||||||
|
- `total` — суммарное время;
|
||||||
|
- `project:*` — время по каждому проекту;
|
||||||
|
- `activity:*` — время по каждой активности (только при одновременном `--by-activity`).
|
||||||
|
|
||||||
|
Версии в сводку не входят.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --by-activity --summary
|
||||||
|
```
|
||||||
|
|
||||||
|
### `--compact`
|
||||||
|
|
||||||
|
Компактный текстовый вывод в консоль вместо таблицы:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
redmine-reporter --compact
|
||||||
|
```
|
||||||
|
|
||||||
|
### Перевод статусов
|
||||||
|
|
||||||
|
Статусы задач переводятся встроенным словарём; секция `report.status_translation` переопределяет или дополняет переводы. Подробно: [CONFIG.md](CONFIG.md).
|
||||||
|
|
||||||
|
## Отправка по email
|
||||||
|
|
||||||
|
Требуется секция `email` в YAML-конфиге (см. [CONFIG.md](CONFIG.md)).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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 --send --commit
|
||||||
|
```
|
||||||
|
|
||||||
|
Факты:
|
||||||
|
|
||||||
|
- Файл отчёта сохраняется на диск ДО отправки: при ошибке SMTP файл остаётся на месте.
|
||||||
|
- Секция `email` отсутствует или `smtp.host` пуст → ошибка «Email не настроен. Добавьте секцию 'email' в конфиг...», exit 1.
|
||||||
|
- `email.html: true` → письмо `multipart/alternative`: plain-text + HTML-таблица отчёта в теле.
|
||||||
|
- Файл прикрепляется при `attach: true`; MIME-тип определяется по расширению, неизвестное → `application/octet-stream`.
|
||||||
|
- Получатели `bcc` указываются только в envelope (не видны в заголовках письма).
|
||||||
|
- STARTTLS применяется только при `smtp.tls: true`; аутентификация на SMTP-сервере — только если задан `smtp.user`.
|
||||||
|
- Таймаут соединения с SMTP — 30 секунд.
|
||||||
|
|
||||||
|
Точные тексты ошибок:
|
||||||
|
|
||||||
|
- «Ошибка аутентификации SMTP. Проверьте логин и пароль.»
|
||||||
|
- «Таймаут соединения с SMTP-сервером.»
|
||||||
|
- «Ошибка отправки письма: ...»
|
||||||
|
- «Не удалось подключиться к SMTP-серверу host:port»
|
||||||
|
|
||||||
|
## Ежемесячный цикл с --commit
|
||||||
|
|
||||||
|
`--commit` сохраняет отчёт в файл (по `--output` или по шаблону `output.dir`/`output.filename`) и записывает `period.last_used` в YAML-конфиг.
|
||||||
|
|
||||||
|
Что пишется:
|
||||||
|
|
||||||
|
- `precision: datetime` → `last_used.from` = `last_used.to` = текущий момент UTC.
|
||||||
|
- `precision: date` → фактические `from`/`to` использованного периода.
|
||||||
|
- `dynamic: true` → `default_from`/`default_to` не меняются.
|
||||||
|
- `dynamic: false` → дополнительно перезаписываются `default_from`/`default_to`.
|
||||||
|
|
||||||
|
Типичный цикл:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Отчёт за июнь 2026: сохранить файл, зафиксировать период
|
||||||
|
redmine-reporter --date 2026-06-01--2026-06-30 --send --commit
|
||||||
|
|
||||||
|
# 2. Следующий запуск без --date возьмёт июль 2026 автоматически
|
||||||
|
# (при dynamic: true и полном месяце)
|
||||||
|
redmine-reporter --send --commit
|
||||||
|
```
|
||||||
|
|
||||||
|
Защита от дублей при `precision: datetime`:
|
||||||
|
|
||||||
|
- Если в YAML есть `last_used.to` (cutoff), time entry сохраняется в отчёт, только если `created_on` И `updated_on` оба >= cutoff; если хотя бы одно поле раньше cutoff — запись исключается.
|
||||||
|
- Записи, у которых оба поля (`created_on`, `updated_on`) отсутствуют, сохраняются; если задано только одно поле, решает оно.
|
||||||
|
- Фильтр применяется всегда при `precision: datetime`, в том числе при явном `--date`.
|
||||||
|
|
||||||
|
Важно: `--commit` перезаписывает YAML через `yaml.dump` — пользовательские комментарии в файле теряются.
|
||||||
|
|
||||||
|
## Справочник флагов
|
||||||
|
|
||||||
|
| Флаг | Описание |
|
||||||
|
| --- | --- |
|
||||||
|
| `--date` | Диапазон периода: `YYYY-MM-DD--YYYY-MM-DD` или `YYYY-MM-DDTHH:MM:SS--YYYY-MM-DDTHH:MM:SS`. Смешанная точность и `start > end` отвергаются. |
|
||||||
|
| `--compact` | Компактный текстовый вывод в консоль вместо таблицы. |
|
||||||
|
| `--output` | Путь к файлу (`.odt`/`.csv`/`.md`/`.html`/`.json`/`.xlsx`) или bare-формат — тогда путь берётся из YAML-шаблона; без флага — вывод в консоль (stdout). |
|
||||||
|
| `--author` | Переопределить имя автора отчёта. |
|
||||||
|
| `--no-time` | Не включать затраченное время в отчёт. |
|
||||||
|
| `--url` | Переопределить URL Redmine. |
|
||||||
|
| `--api-key` | Переопределить API-ключ Redmine. |
|
||||||
|
| `--config` | Путь к альтернативному `.env`-файлу. |
|
||||||
|
| `--verbose` | Подробный вывод; показывает traceback при ошибках. |
|
||||||
|
| `--debug` | Отладочный вывод; показывает traceback при ошибках. |
|
||||||
|
| `--version` | Показать версию и выйти. |
|
||||||
|
| `--summary` | Сводка по времени в stderr: `total`, `project:*`, `activity:*` (версии не печатаются). |
|
||||||
|
| `--user-id` | Отчёт за пользователя по ID. |
|
||||||
|
| `--user-login` | Отчёт за пользователя по логину (точное регистрозависимое совпадение). |
|
||||||
|
| `--user-name` | Отчёт за пользователя по имени. |
|
||||||
|
| `--by-activity` | Разбивка времени по типам активности (в JSON — поле `activities`). |
|
||||||
|
| `--init-config` | Сгенерировать YAML-конфиг из текущего `.env`/окружения и выйти. |
|
||||||
|
| `--force` | Перезаписать существующий YAML-конфиг (только с `--init-config`). |
|
||||||
|
| `--config-path` | Путь к YAML-конфигу: загрузка, запись `--init-config` и `--commit`. По умолчанию `~/.config/redmine-reporter/config.yml`. |
|
||||||
|
| `--commit` | Сохранить отчёт в файл и зафиксировать период (`period.last_used`) в YAML. |
|
||||||
|
| `--send` | Отправить отчёт по email после сохранения файла. |
|
||||||
|
|
||||||
|
## Устранение неполадок
|
||||||
|
|
||||||
|
| Ошибка | Причина | Решение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| «REDMINE_URL must use HTTPS...» (exit 1) | URL Redmine не по HTTPS | Укажите `https://` в `redmine.url` / `REDMINE_URL` / `--url`. |
|
||||||
|
| «Authentication failed: invalid API key, login or password. Check REDMINE_API_KEY / REDMINE_USER / REDMINE_PASSWORD.» | Неверный или отсутствующий API-ключ, логин или пароль | Проверьте `redmine.api_key` / `--api-key` (или `REDMINE_USER` / `REDMINE_PASSWORD`). |
|
||||||
|
| «User '...' not found...» | Пользователь не найден ни по ID, ни по логину, ни по имени | Проверьте написание; логин сравнивается точно с учётом регистра. |
|
||||||
|
| «Multiple users match...» | По имени найдено несколько пользователей (или несколько дублей логина) | Используйте `--user-login` с логином из списка или уточните числовой ID для `--user-id`. |
|
||||||
|
| Указано несколько `--user-*` | Флаги взаимоисключающие | Оставьте только один из `--user-id` / `--user-login` / `--user-name`. |
|
||||||
|
| «Неизвестный формат файла: '.xyz'. Поддерживаются: .odt, .csv, .md, .html, .json, .xlsx» (exit 1) | `--output` с неизвестным расширением | Используйте одно из поддерживаемых расширений. |
|
||||||
|
| Ошибка про `odfpy` при `.odt` | Пакет `odfpy` не установлен | `pip install odfpy` или выберите другой формат. |
|
||||||
|
| «Email не настроен. Добавьте секцию 'email' в конфиг...» (exit 1) | `--send` без секции `email` в YAML или с пустым `smtp.host` | Добавьте секцию `email` (см. [CONFIG.md](CONFIG.md)). |
|
||||||
|
| «Ошибка аутентификации SMTP. Проверьте логин и пароль.» | Неверные `smtp.user`/`smtp.password` | Проверьте логин и пароль в конфиге. |
|
||||||
|
| «Таймаут соединения с SMTP-сервером.» | Сервер не ответил за 30 секунд | Проверьте сеть, `smtp.host` и `smtp.port`. |
|
||||||
|
| «Не удалось подключиться к SMTP-серверу host:port» | Нет соединения с SMTP | Проверьте `smtp.host`, `smtp.port` и доступность сервера. |
|
||||||
|
| «Ошибка отправки письма: ...» | Прочая ошибка SMTP | Смотрите детали после двоеточия; файл отчёта уже сохранён на диске. |
|
||||||
|
| Предупреждение о правах конфига при старте | Права `config.yml` шире `0600` | `chmod 600 ~/.config/redmine-reporter/config.yml`. |
|
||||||
|
| Предупреждение о MITM при старте | `verify_ssl: false` / `REDMINE_VERIFY=false` — проверка TLS отключена | Включите проверку или используйте только в доверенной сети. |
|
||||||
|
| Не виден traceback при ошибке | Traceback показывается только с `--verbose`/`--debug` | Повторите запуск с `--debug`. |
|
||||||
|
|
||||||
|
API-ключ в текстах ошибок маскируется (`***`).
|
||||||
|
|
||||||
|
## См. также
|
||||||
|
|
||||||
|
- [README.md](../README.md) — обзор, быстрый старт, форматы вывода.
|
||||||
|
- [CONFIG.md](CONFIG.md) — полный референс конфигурации (YAML, env, приоритеты, безопасность).
|
||||||
@@ -275,8 +275,11 @@ def fetch_issues_with_spent_time(
|
|||||||
along with total spent hours per issue.
|
along with total spent hours per issue.
|
||||||
If user_id is None, uses current user.
|
If user_id is None, uses current user.
|
||||||
If by_activity is True, returns per-activity breakdown as third tuple element.
|
If by_activity is True, returns per-activity breakdown as third tuple element.
|
||||||
If dedup_before is set, filters out time entries whose created_on AND updated_on
|
If dedup_before is set, entries with both fields set are kept only when
|
||||||
are both before dedup_before (AND logic: both must be < cutoff to exclude).
|
created_on AND updated_on are both >= dedup_before; an entry is excluded
|
||||||
|
if at least one of the fields is before the cutoff (dedup_before).
|
||||||
|
Entries with both fields missing (None) are kept;
|
||||||
|
if only one field is set, that field alone decides (>= cutoff keeps the entry).
|
||||||
Returns list of (issue, total_hours, activities) tuples.
|
Returns list of (issue, total_hours, activities) tuples.
|
||||||
Raises RedmineAPIError on API/auth/network failures.
|
Raises RedmineAPIError on API/auth/network failures.
|
||||||
"""
|
"""
|
||||||
@@ -300,8 +303,11 @@ def fetch_issues_with_spent_time(
|
|||||||
raise RedmineAPIError(_format_redmine_error(exc), original=exc) from exc
|
raise RedmineAPIError(_format_redmine_error(exc), original=exc) from exc
|
||||||
|
|
||||||
# Дедупликация: отсекаем записи, которые были учтены в предыдущем отчёте.
|
# Дедупликация: отсекаем записи, которые были учтены в предыдущем отчёте.
|
||||||
# Запись исключается, если BOTH created_on AND updated_on < dedup_before.
|
# Если оба поля заданы, запись сохраняется, только если created_on И updated_on
|
||||||
# Записи без метаданных (created_on/updated_on == None) не фильтруются.
|
# оба >= dedup_before; если хотя бы одно из полей < dedup_before,
|
||||||
|
# запись исключается.
|
||||||
|
# Если оба поля None — запись сохраняется; если задано только одно поле,
|
||||||
|
# решает оно (>= dedup_before → запись сохраняется).
|
||||||
if dedup_before is not None:
|
if dedup_before is not None:
|
||||||
# Нормализуем cutoff к aware UTC (#58): naive cutoff трактуем как UTC,
|
# Нормализуем cutoff к aware UTC (#58): naive cutoff трактуем как UTC,
|
||||||
# чтобы сравнение с нормализованными created_on/updated_on было корректным.
|
# чтобы сравнение с нормализованными created_on/updated_on было корректным.
|
||||||
|
|||||||
Reference in New Issue
Block a user