diff --git a/README.md b/README.md index 7f4cfc0..1cf982e 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ redmine: 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) @@ -115,6 +116,7 @@ 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 ``` @@ -128,7 +130,7 @@ DEFAULT_TO_DATE=2026-01-31 | `REDMINE_PASSWORD` | Да, если нет токена | Пароль Redmine. | | `REDMINE_AUTHOR` | Нет | Имя автора для отчёта. | | `DEFAULT_FROM_DATE` | Нет | Начальная дата периода по умолчанию (`YYYY-MM-DD`). | -| `DEFAULT_TO_DATE` | Нет | Конечная дата периода по умолчанию (`YYYY-MM-DD`). | +| `DEFAULT_TO_DATE` | Нет | Конечная дата периода по умолчанию (`YYYY-MM-DD`). Если не задана, а `DEFAULT_FROM_DATE` задана — используется сегодняшняя дата. | | `REDMINE_VERIFY` | Нет | TLS-проверка: `true` / `false` / путь к CA bundle. | ## Использование diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 660e66f..d181886 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -32,6 +32,7 @@ redmine: period: precision: date default_from: "2026-06-01" + # default_to можно не указывать — тогда конец периода будет сегодня default_to: "2026-06-30" dynamic: false last_used: @@ -73,6 +74,23 @@ email: `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` задана, конец периода = сегодня. + ### `--commit` — автофиксация периода Флаг `--commit` сохраняет использованный период в YAML-конфиг, чтобы следующий запуск автоматически начинался с нового периода. @@ -353,6 +371,10 @@ vim ~/.config/redmine-reporter/config.yml | `--init-config --force` | Перезаписать существующий YAML | | `--config-path PATH` | Сохранить YAML по указанному пути (по умолчанию `~/.config/redmine-reporter/config.yml`) | +Если `DEFAULT_TO_DATE` не задана, а `DEFAULT_FROM_DATE` задана, сгенерированный +YAML будет содержать пустое `default_to`, и при запуске инструмент использует +сегодняшнюю дату. + ### Проверка после миграции ```bash diff --git a/pyproject.toml b/pyproject.toml index 2d977b7..063a232 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "redmine-reporter" -version = "1.10.0" +version = "1.10.1" description = "Redmine time-entry based issue reporter for internal use" readme = "README.md" authors = [{ name = "Artem Kokos", email = "artem-kokos@mail.ru" }] diff --git a/redmine_reporter/__init__.py b/redmine_reporter/__init__.py index fcfdf38..a0865bb 100644 --- a/redmine_reporter/__init__.py +++ b/redmine_reporter/__init__.py @@ -1 +1 @@ -__version__ = "1.10.0" +__version__ = "1.10.1" diff --git a/redmine_reporter/config.py b/redmine_reporter/config.py index 11a4e62..9d3fc29 100644 --- a/redmine_reporter/config.py +++ b/redmine_reporter/config.py @@ -370,8 +370,11 @@ class Config: def get_default_date_range(cls) -> str: from_env = os.getenv("DEFAULT_FROM_DATE", "").strip() to_env = os.getenv("DEFAULT_TO_DATE", "").strip() - if from_env and to_env: - return f"{from_env}--{to_env}" + today = date.today() + today_str = today.isoformat() + + if from_env: + return f"{from_env}--{to_env or today_str}" if ( cls._app @@ -386,17 +389,12 @@ class Config: ) return f"{nf}--{nt}" - if cls._app and cls._app.period_default_from and cls._app.period_default_to: - return f"{cls._app.period_default_from}--{cls._app.period_default_to}" + if cls._app and cls._app.period_default_from: + default_to = cls._app.period_default_to or today_str + return f"{cls._app.period_default_from}--{default_to}" - today = date.today() start = today.replace(day=1) - if today.month == 12: - next_month = today.replace(year=today.year + 1, month=1, day=1) - else: - next_month = today.replace(month=today.month + 1, day=1) - end = next_month - timedelta(days=1) - return f"{start.isoformat()}--{end.isoformat()}" + return f"{start.isoformat()}--{today_str}" @classmethod def validate(cls) -> None: diff --git a/tests/test_config.py b/tests/test_config.py index 21348d5..c1feb7b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -79,21 +79,29 @@ def test_get_default_date_range_from_env(): assert Config.get_default_date_range() == "2026-01-01--2026-01-31" +@mock.patch.dict( + os.environ, + {"DEFAULT_FROM_DATE": "2026-01-01"}, + clear=True, +) +def test_get_default_date_range_from_env_without_to(): + """Если DEFAULT_TO_DATE не задан, конец периода — сегодня.""" + from datetime import date + + today = date.today().isoformat() + assert Config.get_default_date_range() == f"2026-01-01--{today}" + + @mock.patch.dict(os.environ, {}, clear=True) def test_get_default_date_range_fallback(): - """Если даты не заданы -- используется текущий месяц.""" - from datetime import date, timedelta + """Если даты не заданы -- используется текущий месяц с начала до сегодня.""" + from datetime import date today = date.today() start = today.replace(day=1) - if today.month == 12: - next_month = today.replace(year=today.year + 1, month=1, day=1) - else: - next_month = today.replace(month=today.month + 1, day=1) - end = next_month - timedelta(days=1) result = Config.get_default_date_range() - assert result == f"{start.isoformat()}--{end.isoformat()}" + assert result == f"{start.isoformat()}--{today.isoformat()}" @mock.patch.dict(os.environ, {}, clear=True) @@ -567,6 +575,24 @@ class TestDefaultDateRangeWithLastUsed: result = Config.get_default_date_range() assert result == "2026-04-01--2026-04-15" + @mock.patch.dict(os.environ, {}, clear=True) + def test_falls_back_to_today_when_default_to_missing(self): + """Если default_from задан, а default_to нет — конец периода сегодня.""" + from datetime import date + + with tempfile.TemporaryDirectory() as tmp: + yaml_path = Path(tmp) / "config.yml" + yaml_path.write_text( + "period:\n" + " precision: date\n" + " dynamic: false\n" + " default_from: '2026-04-01'\n" + ) + Config._app = AppConfig.from_yaml(yaml_path) + today = date.today().isoformat() + result = Config.get_default_date_range() + assert result == f"2026-04-01--{today}" + class TestGetEmailConfig: """Tests for Config.get_email_config()."""