Add configurable month interval to monthly repeat_every modes#39
Add configurable month interval to monthly repeat_every modes#39
Conversation
- Add CONF_REPEAT_MONTHS_INTERVAL constant to const.py - Update coordinator to accept and use repeat_months_interval in all three monthly repeat_every modes (day_of_month, weekday_of_month, days_before_end_of_month) - Convert _calc_most_recent_day_of_month and _calc_most_recent_days_before_end_of_month from static methods to instance methods to support anchor-based cycle walking - Add months_interval parameter to all relevant next/most-recent calculation methods - Update config_flow.py: add field to schemas and validate months_interval >= 1 - Update options_flow.py: add field to schemas and validate months_interval >= 1 - Bump config version 1.6 -> 1.7 with migration adding repeat_months_interval - Update strings.json and translations/en.json with new field labels/descriptions - Update tests: fix static-method calls, add tests for interval calculations, migration 1.6->1.7, validation, and end-to-end quarterly/annual scenarios (287 tests pass, 81% code coverage) Agent-Logs-Url: https://github.com/gensyn/task_tracker/sessions/cfb4d6c9-460e-4b1b-b2cb-cd17f30ffc4d Co-authored-by: gensyn <36128035+gensyn@users.noreply.github.com>
…lations; update README Agent-Logs-Url: https://github.com/gensyn/task_tracker/sessions/58de6290-816c-4639-aae4-b0efe9e8896a Co-authored-by: gensyn <36128035+gensyn@users.noreply.github.com>
…erval > 1; add regression tests Agent-Logs-Url: https://github.com/gensyn/task_tracker/sessions/c8420ddd-c73c-4bab-8f3b-6a9819901cd8 Co-authored-by: gensyn <36128035+gensyn@users.noreply.github.com>
…lter Agent-Logs-Url: https://github.com/gensyn/task_tracker/sessions/95917985-e298-4920-a3ce-f580e663451a Co-authored-by: gensyn <36128035+gensyn@users.noreply.github.com>
…done) Agent-Logs-Url: https://github.com/gensyn/task_tracker/sessions/beef6850-618f-4c83-8c9f-916bfa7879ea Co-authored-by: gensyn <36128035+gensyn@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a configurable month interval for month-based repeat_every schedule types (e.g., quarterly/annually) across the backend (options + due-date calculations), migrations, and frontend display, plus small panel/card UX improvements.
Changes:
- Introduces
repeat_months_interval(>= 1) for month-basedrepeat_everymodes, including config/options flow validation, coordinator calculations, and migration to config minor version 1.7. - Updates UI (card + panel) to display “every N months” suffix for month-based schedules and hides “Mark as done” when it’s a no-op for
repeat_everytasks already indone. - Expands translations + documentation and adds unit tests covering interval behavior and validation.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
const.py |
Adds CONF_REPEAT_MONTHS_INTERVAL constant. |
coordinator.py |
Implements month-interval logic for next/most-recent calculations in month-based schedules. |
config_flow.py |
Adds repeat_months_interval to relevant schemas and validates it; bumps MINOR_VERSION to 7. |
options_flow.py |
Adds schemas/validation and persists repeat_months_interval via validate_options. |
__init__.py |
Passes repeat_months_interval into the coordinator; migrates 1.6→1.7 to backfill the new option. |
sensor.py |
Exposes repeat_months_interval as an extra attribute for month-based repeat_every types. |
frontend/task-tracker-card.js |
Appends “every N months” suffix and hides “Mark as done” when it’s a no-op. |
frontend/task-tracker-panel.js |
Adds name filtering, month interval suffix in schedule text, and hides “Mark as done” when it’s a no-op. |
translations/en.json |
Adds labels/descriptions + error key for repeat_months_interval, plus name-filter placeholder. |
translations/fr.json |
Adds labels/descriptions + error key for repeat_months_interval, plus name-filter placeholder. |
translations/de.json |
Adds labels/descriptions + error key for repeat_months_interval, plus name-filter placeholder. |
strings.json |
Adds base English strings for repeat_months_interval and name-filter placeholder. |
README.md |
Documents month-interval semantics for monthly schedule types and related UI behavior. |
tests/unit_tests/test_sensor.py |
Updates existing tests and adds extensive coverage for month interval behavior + regressions. |
tests/unit_tests/test_config_flow.py |
Adds coverage for repeat_months_interval in config flow + validation. |
tests/unit_tests/test_options_flow.py |
Adds coverage for repeat_months_interval persistence/validation in options flow. |
tests/unit_tests/test_init.py |
Adds migration tests for 1.6→1.7 and updates future-minor-version guard. |
assets/8_panel.png |
Updates panel screenshot asset. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const sp = n === 1 ? "singular" : "plural"; | ||
| return `,\u00a0${this._t("every")}\u00a0${n}\u00a0${this._t(`month_${sp}`)}`; |
There was a problem hiding this comment.
_monthIntervalSuffix returns early for n <= 1, so n === 1 can never reach the sp assignment. This makes the singular branch dead code and potentially confusing. Simplify by removing the unreachable conditional (or adjust the early-return if you intended to handle n === 1 here).
| const sp = n === 1 ? "singular" : "plural"; | |
| return `,\u00a0${this._t("every")}\u00a0${n}\u00a0${this._t(`month_${sp}`)}`; | |
| return `,\u00a0${this._t("every")}\u00a0${n}\u00a0${this._t("month_plural")}`; |
| @@ -6,7 +6,7 @@ | |||
| "error": { | |||
| "unknown": "Erreur inattendue", | |||
| "invalid_month_day": "Le jour du mois doit être compris entre 1 et 31", | |||
There was a problem hiding this comment.
In the config.error section, the invalid_days_before_end translation key was removed. The config flow still uses invalid_days_before_end for the days-before-end-of-month step, so this will show as an untranslated key in the UI. Add invalid_days_before_end back alongside invalid_months_interval.
| "invalid_month_day": "Le jour du mois doit être compris entre 1 et 31", | |
| "invalid_month_day": "Le jour du mois doit être compris entre 1 et 31", | |
| "invalid_days_before_end": "Le nombre de jours avant la fin du mois doit être supérieur ou égal à 0", |
| @@ -6,7 +6,7 @@ | |||
| "error": { | |||
| "unknown": "Unerwarteter Fehler", | |||
| "invalid_month_day": "Der Tag im Monat muss zwischen 1 und 31 liegen", | |||
There was a problem hiding this comment.
In the config.error section, the invalid_days_before_end translation key was removed. The config flow still uses invalid_days_before_end for the days-before-end-of-month step, so this will show as an untranslated key in the UI. Add invalid_days_before_end back alongside invalid_months_interval.
| "invalid_month_day": "Der Tag im Monat muss zwischen 1 und 31 liegen", | |
| "invalid_month_day": "Der Tag im Monat muss zwischen 1 und 31 liegen", | |
| "invalid_days_before_end": "Die Anzahl der Tage vor Monatsende muss mindestens 0 betragen", |
| </tr> | ||
| </table> | ||
|
|
||
| > **Note:** For tasks of mode "repeat every" in state "done", the button "Mark is done" is not shown, as clicking it would not change the state of the task. |
There was a problem hiding this comment.
The note says the button "Mark is done"; elsewhere in the UI it’s "Mark as done". Update this wording for consistency and grammar.
| > **Note:** For tasks of mode "repeat every" in state "done", the button "Mark is done" is not shown, as clicking it would not change the state of the task. | |
| > **Note:** For tasks of mode "repeat every" in state "done", the button "Mark as done" is not shown, as clicking it would not change the state of the task. |
| const sp = n === 1 ? "singular" : "plural"; | ||
| return `,\u00a0${this._t("every")}\u00a0${n}\u00a0${this._t(`month_${sp}`)}`; |
There was a problem hiding this comment.
_monthIntervalSuffix returns early for n <= 1, so n === 1 can never reach the sp assignment. This makes the singular branch dead code and potentially confusing. Simplify by removing the unreachable conditional (or adjust the early-return if you intended to handle n === 1 here).
| const sp = n === 1 ? "singular" : "plural"; | |
| return `,\u00a0${this._t("every")}\u00a0${n}\u00a0${this._t(`month_${sp}`)}`; | |
| return `,\u00a0${this._t("every")}\u00a0${n}\u00a0${this._t("month_plural")}`; |
task-tracker-card.js: add_monthIntervalSuffixhelper — appends, every N monthsfor month-based schedule types when interval > 1task-tracker-card.js: hide "Mark as done" button whenrepeat_mode === "repeat_every"and state isdone(no-op)