Skip to content

New support for subtasks#594

Open
powerjungle wants to merge 9 commits into
pimutils:mainfrom
powerjungle:powerjungle/subtasks
Open

New support for subtasks#594
powerjungle wants to merge 9 commits into
pimutils:mainfrom
powerjungle:powerjungle/subtasks

Conversation

@powerjungle
Copy link
Copy Markdown

@powerjungle powerjungle commented Sep 8, 2025

Add support for RELATED-TO property and RELTYPE parameter.

Taken from RFC 5545, section 3.8.4.5. If present in a file, these values are now read and stored in the cache.

Tests are modified to reflect the change.

The SCHEMA_VERSION is incremented to allow the cache to be recreated after the update.

Nothing is required from the user to be done for the changes in this PR to work.

Before:
image

After:
image

Closes: #396

@balejk
Copy link
Copy Markdown
Contributor

balejk commented Oct 1, 2025

If by cache you mean the cache database, it should be enough to just increment the schema version to force a rebuild.

@powerjungle powerjungle force-pushed the powerjungle/subtasks branch from e87552a to b6ee7fa Compare October 1, 2025 14:50
@powerjungle
Copy link
Copy Markdown
Author

powerjungle commented Oct 1, 2025

If by cache you mean the cache database, it should be enough to just increment the schema version to force a rebuild.

Ah thank you, I totally missed it! I rebased my changes to make use of this and to describe it properly in comments and in the documentation. I edited the first comment of the PR to remove the "breaking changes" part since it's not true anymore.

@jlkDE
Copy link
Copy Markdown

jlkDE commented Apr 10, 2026

Hey, I just rebased this to the current main for myself and as far as I can see everything works as intended, thanks for that!
What is needed for this to progress, is there some way to support this?

@WhyNotHugo WhyNotHugo force-pushed the powerjungle/subtasks branch 2 times, most recently from 064a26f to 8b78386 Compare April 13, 2026 23:50
Copy link
Copy Markdown
Member

@WhyNotHugo WhyNotHugo left a comment

Choose a reason for hiding this comment

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

Rebased this applying ruff format on the way.

Looks good, appreciate all the attention to detail.

Mostly minor feedback. There are two things which I think can be improved but can be done separate later (I'd rather keep this PR lean and not block on further changes):

  • Tree-building. This seems rather inefficient right now, but I won't block merging on that; we can iterate later gradually.
  • Style. I think the output would be easier to parse keeping the left [ ] $ID fields all aligned, maybe even something like:
[ ] 2 Some task
[ ] 4 ├─ Some subtask
[ ] 3 └─ Another subtask

Again, this seems like something that's best done separately.

Comment thread tests/test_porcelain.py
Comment on lines +84 to +85
"related_to": "",
"related_to_reltype": "",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should exclude the fields when absent, instead of including empty fields.

(same applies to other instances below)

Comment on lines +26 to +30
If the database is changed in a breaking way, the ``SCHEMA_VERSION`` variable
in the class ``Cache`` has to be incremented to allow the cache to be recreated
after todoman has been updated. An example would be adding new fields or
removing old unnecessary fields and etc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice addition!

Comment thread todoman/cli.py
Comment on lines +243 to 251
# Merges all different property arguments into one dictionary
# `todo_properties` argument, so that it can be directly looped through
# easily for directly setting the `todoman.model.Todo` class attributes
# from within a command function.
#
# The names of the options are the same as the
# `todoman.model.Todo` class attributes.
@functools.wraps(command)
def command_wrap(*a, **kw) -> click.Command:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nitpick: this should be a docstring rather than comment.

Comment thread todoman/cli.py
)

_subtask_option = click.option(
"--subtask-for",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What do you think of calling this --parent?

Code-wise it's a bit more obvious, but I'm not sure how obvious it is for a user interface.

Comment thread todoman/cli.py
changes = True
todo.description = sys.stdin.read()

if subtask_for is not None and not_subtask is False:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If subtask_for and not_subtask, we should return an error (invalid arguments).

Comment thread todoman/formatters.py
# are specified."
# Source:
# https://www.rfc-editor.org/rfc/rfc9253#section-9.1
if todo.related_to != "":
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Check truthiness, so this also matches if it's None.

Suggested change
if todo.related_to != "":
if todo.related_to:

Comment thread todoman/formatters.py
return self._join_tree(tree).strip()

def _tree_reorder_related(
self, tree: dict[str, list], related_todos: dict[str, list]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Formatting:

Suggested change
self, tree: dict[str, list], related_todos: dict[str, list]
self,
tree: dict[str, list],
related_todos: dict[str, list],

Comment thread todoman/formatters.py
Comment on lines +209 to +236
store_path: list = []
for related, related_to in related_todos.items():
# Find the root parent todo of the `related_to` todo.
related_to_path_tracer: str = related_to[0]
related_dict: dict[str, list] = tree
while related_to_path_tracer in related_todos:
related_to_path_tracer = related_todos[related_to_path_tracer][0]
store_path.append(related_to_path_tracer)

# Walk from the parent root todo, down to the
# `related_to` todo itself.
store_path.reverse()
# If `related_to` is a SIBLING, walk one todo backwards towards the
# root parent todo. If there is no path to be walked,
# just ignore it.
with contextlib.suppress(IndexError):
if related_to[1] == "SIBLING":
related_to[0] = store_path.pop()
for inwards in store_path:
try:
related_dict = related_dict[inwards][1]
if related_dict is None:
break
except KeyError:
related_dict = tree
break

self._tree_move_related(related, related_to[0], tree, related_dict)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks quite inefficient, since it walks the entire list multiple times. I think we need a "real" tree when building the whole tree in the first place.

But this is non-blocking at this stage; we can merge it in its current form and iterate on this later.

Comment thread todoman/model.py
Comment on lines +98 to +99
self.related_to = ""
self.related_to_reltype = ""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Default should be None, not empty string. This affects the field's presence in the output icalendar file.

Comment thread todoman/model.py
Comment on lines +684 to +688
got_related_to_reltype = ""
got_related_to = todo.get("related-to", "")
if got_related_to != "":
got_related_to_reltype = got_related_to.params.get("reltype")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As above, default should be None.

@WhyNotHugo WhyNotHugo force-pushed the powerjungle/subtasks branch from 8b78386 to d52e187 Compare April 14, 2026 01:16
Taken from RFC 5545, section `3.8.4.5`. If present in a file,
these values are now read and stored in the cache. Nothing in the UI
has been changed. Tests are modified to reflect the change.

Increment `SCHEMA_VERSION` to allow cache recreation on todoman update.
New names and values added to the JSON output for the `RELATED-TO`
property.
List text output is now with a tree structure:
task 1
    task 2
    task 3
        task 4
task 5

The table list is changed to a dictionary representing a tree.
No changes to the SQL queries are made.

RELTYPEs PARENT, CHILD and SIBLING are supported.
https://www.rfc-editor.org/rfc/rfc5545#section-3.2.15

Includes output space formatting fixes.

Tests adjusted to changes.
Comment for describing the weird code for merging arguments.
Using the newly supported attributes, two new options are added.
`--subtask-for`, making a task be a subtask for another and
`--not-subtask`, making a task no longer be a subtask.
The commands `new` and `edit` are fitted with these new options.
@WhyNotHugo WhyNotHugo force-pushed the powerjungle/subtasks branch from d52e187 to 373cdee Compare April 14, 2026 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show subtasks in a better way

4 participants