Dataview query on all tasks with modified status. Possible or not?

What I’m trying to do

In my daily note I have a basic dataview query that shows a list of all tasks that I completed on that day. However … during the day I also change the status of tasks to (e.g.) ‘In progress’ or ‘Canceled’. I would also like to also see those changes in my daily ‘Modified tasks’ overview.

Things I have tried

Since no date is recorded for changes other than setting the status to ‘Completed’, I did not find a way to get the list that I want via a dataview-query. So I am kind of stuck (or I am missing something realy obvious)

Obsidian only tracks changes on the file level, so you don’t know what has changed within a file unless you somehow specify it like adding a date of when you started/canceled the task.

I haven’t always done this, I understand, but for query questions: it’s a good idea to show a sample of your source data and the query you are using :face_with_monocle:.

1 Like

Great thought, holroy, care to elaborate a little on that? :smiley:

Sure, you could for example create two Templater templates which changes the current item (preferably pre-selected) into either in-progress or canceled. The template should change the task status, and in addition add a marker at the end with the date of the change.

So given a line like:

- [ ] My to be canceled task

It could change that into:

- [-] My to be canceled task %% [canceled:: 2023-03-10] %%

I can’t write out the templates just now, but using tp.file.selection and a little regex replace it shouldn’t be too hard to do, and after assigning it to a hotkey, you could have the following workflow (on desktop):

  • Triple-click the task to select all of it
  • Hit the hotkey
  • Enjoy the properly marked up task in a query at your daily note
1 Like

Thank you @ariehen. I should indeed have done that. Ommited it since the query is definetely very basic. Added it below:

TASK WHERE completion = this.file.day

This works because completion is recorded as a date and can be compared to this.file.day.

I would idealy like to do something like (pseudo-code):

TASK WHERE *StatusChangeDate*= this.file.day

However, as @holroy already mentioned, something like StatusChangeDate does not exist
and status-changes themselves are not recorded with a date. So I think no easy solution exists and I would need to reside to adding a ‘last modified’ date manualy every time and querying on that (which I know will be too cumbersome).

(Edit) I noticed the more extensive response from @holroy directly after I posted my own reply. Big thank you. I will try this and see how this would work.

Thank you for the help so far.

Since there appears to be no easy solution for this, and because I think others could also benefit from having this functionality, I have made a Feature request for the Tasks plug-in.

https://github.com/obsidian-tasks-group/obsidian-tasks/issues/1746

I kept on thinking about this, and decided to expand the template a little, and give it some more options, and I ended up with this template:

<%*
const consoleDebug = true
let sel = tp.file.selection()

newStatusDict = [
  ["Cancel task",       "-", "canceled", true ],
  ["Task in progress",  "/", "started", true ],
  ["Complete task",     "x", "completion", false ],
  ["Complete task ✅",  "x", "✅", false ],
  ["Clear the task",    " ", "removeComments", false ],
]

const newStatus = await tp.system.suggester( t => t[0], 
                                  newStatusDict, true,  "Choose new status")

// Some debugging and extraction of values
if ( consoleDebug ) console.log("newStatus\n", newStatus)
const [ name, status, fieldName, useComment ] = newStatus

// Set status to new value
sel = sel.replace(/\[.\]/, `[${ status }]`)

// Define the field
if ( fieldName != "removeComments" ) {
  const optComment = useComment ? ' %%' : ''
  const field = fieldName.length == 1  ?
      ` ${fieldName} ${ tp.date.now() }` :
      ` [${fieldName}:: ${ tp.date.now() }]`
      
  sel += optComment + field + optComment
} else {
  // Remove any comments, if that's the wanted action
  sel = sel.replace(/%%.*%%/, "")
}

// Replace the selected part with our modified version
tR += sel
%>
Some explanation of the template
  • It’s prerequisite of the template that the entire line with the task is selected
  • The newStatusDict holds the various actions available, where each action consists of four elements:
    • The name of the action used in the suggester
    • The new status character used within the [ ]
    • The field name used to mark the new task status, either a field name or an icon
    • true or false depending on whether the field should be enclosed in comments or not. If enclosed it’ll not show up in reading view
  • So present the actions to the user for selection, and do some variable manipulation to ease the rest of the template
  • Always set the status to the new status character
  • I’m “misusing” the field name to denote whether to remove the comments, but first lets handle all the other field names
    • Build an optional comment string to include before and after
    • Depending on the length of the field name, either output just the icon and the date, or a proper inline field definition with the date including the [ ]
  • Back to removing the comments, then remove whatever comments are present. Note that this expression is greedy, so if you type something like %% a comment%% some non comment %% another comment $$ it’ll happily gobble up all of that text. So keep comments at the end of the task!
  • End the template by replacing the selected text with our modified alternative

How to use this template

Following the previous described workflow:

  • Triple-click one tasks to select of that task
  • Trigger the template, through a hotkey or other means, and select one of these options in the suggester:
    • Cancel task
    • Task in progress
    • Complete task
    • Complete task :white_check_mark:
    • Clear the task (and remove any comments)

I can do all of those actions, one after the other, and change the following list of tasks:

- [ ] Just testing
- [ ] To be canceled  
- [ ] To be marked as in progress 
- [ ] To be completed 
- [ ] Complete with icon

Into the following modified list:

- [ ] Just testing
- [-] To be canceled %% [canceled:: 2023-03-10] %%
- [/] To be marked as in progress %% [started:: 2023-03-10] %%
- [x] To be completed [completion:: 2023-03-10]
- [x] Complete with icon ✅ 2023-03-10

And if I apply the last choice of the menu on all of these (one after another):

- [ ] Just testing
- [ ] To be canceled   
- [ ] To be marked as in progress  
- [ ] To be completed [completion:: 2023-03-10]
- [ ] Complete with icon ✅ 2023-03-10

Notice since the last two don’t use comments, their field marking is not removed. This will also preserve any other tagging of the tasks like due or start dates, and so on.

How to query these fields

The middle variant displays for me as follows:
image

And if I use the following query:

```dataview
TASK
WHERE file.name = this.file.name
  AND (canceled OR started)
```

I get this output:
image

Extending the actions

If you feel like it, you could keep on adding or modifying the lines in the newStatusDict, so that this template could be your go-to way of changing your tasks.

Just add, change or modify the lines according to the templates given, and it should pick up on the changes the next time you trigger the template. In fact, I’m seriously considering changing to using this template myself (with some more action related to adding custom statuses for tasks which I use a lot in my personal vault)

Some final remarks and comments
  • In the queries I’ve used WHERE file.name = this.file.nam to limit the test set. Modify/change/remove this according to your needs
  • I’m using Minimal theme, and some custom formatting of fields, so your output might be a little different
  • I’m also thinking about incorporating this into a multi using QuickAdd, where I’d also could add new custom tasks, but that’s a little beside this request
  • I would love for this script not needing to pre-select the entire task, but I’m not sure how to reliable find the cursor position, and select the entire task programatically. If you know that, please let me know
1 Like

Wow … That is quite some work you did there @holroy! Thank you for that. I will dive in to what you created later. I allready did a quick test and using a shortcut-key would definetely work in my workflow. I do need to think about your choice of clearing the task while preserving the ‘Completed’ icon. Since this icon implies that the task is in fact completed, you could maybe check if that icon exists, and then mark the task as ‘[x]’.

But again: Thank you for this!

(At the same time I am discussing my feature-request in the Obsidian-Tasks issue-forum on GitHub. Would be great if we can bring these two discussions together)

I’m not all in agreeance with my self on that either. :slight_smile: It could be argued for possibly not changing the status at all, and just remove the comments. This would require just moving the setting of the status inside the field setting section (and a clearer text to indicate what the action does).

One could also check if the icon exists, but then again would you then want to have other actions to remove that or other actions? One possible solution in a future version could possibly be to toggle various fields, but in this request I just wanted to present something showing the possibilities (and some of the caveats) using a template to modify the task.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.