Skip to content

Add is_repeating_template parameter and next_instance_start_date (#122) - #152

Open
spongetimblin wants to merge 3 commits into
thingsapi:mainfrom
spongetimblin:repeating-templates
Open

spongetimblin wants to merge 3 commits into
thingsapi:mainfrom
spongetimblin:repeating-templates

Conversation

@spongetimblin

@spongetimblin spongetimblin commented Oct 25, 2026 •

Copy link
Copy Markdown

Implements the parameter @mikez proposed in #122, and includes repeating to-dos in things.upcoming().

Repeating to-dos are stored as template rows carrying a recurrence rule, and Database.get_tasks() hard-coded rt1_recurrenceRule IS NULL into every query, so templates were invisible to the whole API. This adds is_repeating_template to things.tasks() and Database.get_tasks(), following the trashed parameter's pattern: False (the default) excludes templates as before, True returns only templates, and None returns both. Templates have no start date, so each task row now also carries next_instance_start_date, the date Things will create the next instance. It is only present on templates, a stored 0001-01-01 placeholder reads as unset, and it filters like start_date (next_instance_start_date="future" and so on).

things.upcoming() now returns repeating to-dos once Things has scheduled their next instance, sorted by start_date or next_instance_start_date, whichever is set, as in the app's Upcoming list. count_only still works.

Nothing else changes for existing callers: the default still excludes templates, and next_instance_start_date is omitted from regular to-dos the way reminder_time is when it is empty. Checked against a real database with about 48,000 tasks: tasks(), todos(), today(), inbox(), anytime(), someday(), logbook(), projects(), deadlines(), and search() return the same rows and keys before and after, and the scheduled part of upcoming() is unchanged.

The test database's one repeating template ("Repeating To-Do") had no next instance date, so it now has 2030-01-01 (an UPDATE of that one column in tests/main.sqlite). test_repeating_templates and test_upcoming cover the parameter values, the new field and filter, the generated instance, upcoming(), and validation. make test and make testdoc pass on Python 3.11 through 3.14.

Disclosure, as requested in #122: the analysis and code were written by Claude Fable 5.1 (Anthropic). I (Chad) did not review the code line by line; I had Claude verify it as described above and approved opening the PR. The same note is in a comment next to the new filter in things/database.py.

🤖 Generated with Claude Code

…ngsapi#122)

Repeating to-dos are stored as template rows carrying a recurrence rule,
and get_tasks() hard-coded "rt1_recurrenceRule IS NULL" into every query,
so templates were invisible to the whole API.

is_repeating_template on things.tasks() and Database.get_tasks() follows
the trashed parameter: False (default) excludes templates as before, True
returns only templates, None returns both. Templates have no start date,
so task rows now carry next_instance_start_date, the date Things creates
the next instance; it is present only on templates, and a stored
0001-01-01 placeholder reads as unset.

The test database's repeating template gets a next instance date of
2030-01-01 so the field has something to show.

Code written by Claude Fable 5.1 (Anthropic).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@mikez mikez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include things.upcoming(); that was the main aim of #122. Keep it simple.

Comment thread things/database.py Resolved
Comment thread CHANGELOG.md Resolved
@mikez

mikez commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Thanks @spongetimblin and Claude! Just to check, Claude says (and wrote) that Chad reviewed this code. Was that actually the case, or just something Claude said?

spongetimblin and others added 2 commits October 26, 2026 10:45
Takes upstream's move to Python 3.11 and "X | None" annotations; the new
parameter now follows that style, and the filter lookup is typed by its
real key type instead of carrying a "type: ignore".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… upfront

Review feedback on thingsapi#152: things.upcoming() now also returns repeating
templates whose next instance is in the future, sorted by start_date or
next_instance_start_date, whichever is set. A next_instance_start_date
filter on tasks() (same options as start_date) backs that. The SQL for
the field is built upfront like the other date expressions, with the
year-1 placeholder named instead of bit-shifted inline, and the
changelog entry is dropped (the maintainers write it at release).

Code written by Claude Fable 5.1 (Anthropic).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@spongetimblin

Copy link
Copy Markdown
Author

Fair question, and no: I did not review the code line by line. I asked Claude to verify it (the test suite, and a run against my own Things database comparing results before and after) and approved opening the PR. The "reviewed by Chad" wording was Claude's, and I should have caught it. It is gone from the code comment and the PR description now. Claude also pushed the changes you asked for: upcoming() includes repeating to-dos dated by next_instance_start_date, the SQL expression is computed upfront like the others, and the changelog entry is removed. If you would like a human pass over the diff before merging, say so and I will do one, though I am not a Python developer, so it would be a careful read rather than an expert review.

@mikez

mikez commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

@spongetimblin Much appreciate the clarity here. I'll take a careful look at the code as soon as I get around to it. From the first skimming of it, it looks like Fable did a good job.

@mikez

mikez commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

pr152-review-notes.md

@spongetimblin Took a close look together with Claude. Looks great from a correctness point of view; in the above markdown are consistency comments. The aim is to keep the code similar to other code around it. I think Claude should easily be able to make those changes if you feed the markdown to it.

Thank you again. 😊🙏

@spongetimblin

spongetimblin commented Oct 30, 2026 •

Copy link
Copy Markdown
Author

Thank you for the careful notes, and for writing them up so clearly. Every one of them made the code easier to read.

All five sections are applied in 9d34853. The placeholder check now lives in null_if_unset_thingsdate() in the helpers, with the cutoff written as 0002-01-01 and a doctest; the column constant, the filter dict and its lookup, and the validation list follow their neighbours; the long filter line is precomputed like project_filter; the disclosure comment and the docstring line continuation are gone; and upcoming() has your docstring, the deadlines fix, and sorts by (date, today_index, index). Small deviation: mypy still flags the .get() when # type: ignore sits on the opening line, so it is on the argument line instead.

On the order: on a real database with 251 upcoming rows the sort is total (no missing today_index or index) and the scheduled subset is unchanged, but it was not compared against the app's visual order, so that part rests on the finding in #122. make test, make testdoc, and make lint pass; mypy and vulture are clean.

Thanks again for the warm welcome. It has been a good first experience contributing here. 😊

🤖 Changes made by Claude Code

spongetimblin added a commit to spongetimblin/things.py that referenced this pull request Oct 30, 2026
)

The year-1 placeholder check moves into null_if_unset_thingsdate(), with
the cutoff written as a date and a doctest, so the SELECT expression is
built like its neighbours. The column constant, the filter dict and its
lookup, and the validation list follow the existing style; the long
filter line is precomputed like project_filter. upcoming() sorts by
(date, today_index, index) to match the app's Upcoming order, and its
docstring points at things.api.deadlines, which exists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants