Build a `[ ] task` summary considering `folder` and `#tags`

Hello everyone.
I am new at Obsidian, but I must admit I am impressed.
And I am amazed about #dataview.

But I need your help.

What I’m trying to do

I want to replicate this following table result.
It has:

  • Group by folder
  • Group by file
  • Group by tag
  • The not completed tasks
  • The status of the tasks
    • If I can interact with those tasks, would be better. But I understand is not possible

Something like:

Folder File Tag Task
Folder A File 1 #todo [ ] Task a1
#todo/test [ ] Task a2
File 2 #todo [/] Task b1
#work [ ] Task b2
Folder B File 3 #personal [ ] Task c1
[ ] Task c2

Things I have tried

I had red previous posts like:

But still, I am not able to complete my proposed table.

I would appreciate any type of help.
Thanks!

Hmm… That’s a tall order, at least in a table format. How do you feel about the following query:

```dataview
TASK 
GROUP BY file.path + " " + tags
```

With exception on the visual format, and possibly the handling of tags, it’ll deliver on most of your items.

If ignoring being able to interact with the tasks, and not removing duplicate folder or name information, maybe something like this beast would suffice for your needs?

```dataview
TABLE WITHOUT ID
  min(rows.file.folder) as Folder,
  min(rows.file.link) as File, 
  sum(rows.item.tags) as Tags, 
  map(rows.item, (r) => "[" + r.status + "] " + r.text) as Tasks
FLATTEN file.tasks as item
GROUP BY file.folder + file.name + item.tags
```

Thank you @holroy .

A small tweak, including something I got from Query Types - Dataview :

```dataview
TASK 
WHERE !completed
GROUP BY file. Path + " (" + file. Link + ") " + tags
```

But I get the list of tags that are in the text description of the task.
I was thinking it was possible to get the tag that was just before the task.

Something like:
Image this is the content of a file Notes/File1.md:

#personal
- [ ] Task 1
- [ ] Tas 2

So, the dataview pulls something like:
The text between () is a link to the file.

Notes/File1.md (File1.md) (2)
# personal
  [ ] Task 1
  [ ] Task 2

Is the previous possible?
Thanks!

This is great! Thank you!

Another tweak:

```dataview
TABLE WITHOUT ID
  min(rows.file.folder) as Folder,
  min(rows.file.link) as File, 
  sum(rows.item.tags) as Tags, 
  map(rows.item, (r) => "[" + r.status + "] " + r.text) as Tasks
FLATTEN file.tasks as item
GROUP BY file.folder
```

But in this particular statement, I am not able to filter only the not-completed tasks.
I had tried all the following WHERE clauses:

WHERE rows.item.status != "completed"
WHERE !completed
WHERE item. Task and contains(list(" ", "/", "-", "x"), item.status)
WHERE row.item.status != "x"
FLATTEN length(filter(sum(rows.file.tasks.status), (t) => t = " " or t = "/")) as Incomplete
WHERE rows.file.task.status != "x"
FLATTEN lenght(filter(file.tasks.status != "x")) as item

Any ideas how to filter only the incomplete tasks?
Thanks!

Tags connected to the page, not the tasks

The #personal tag is here related to the page, and are as such not connected to either of the tasks. And there is no easy way to connect it these tasks beside it being attached to all tasks on that note.

So you could move it to the tasks, and thusly connect it. It’s all so doable, but it’ll complicate other parts of your query, to change “#personal” to something like “## personal” (aka a header) and then do some trickery to connect the tasks to that section.

Filter out certain statuses

This line lists the various items with all variants of statuses. One could apply some logic to it, like the below:

map(  filter(rows.item, (fr) => fr.status != "x"),   (r) => "[" + ... )

I wouldn’t do that though, since if this filter out all the items, you don’t really want that row to show up in the first place. So a better option is to do the filter after the GROUP BY clause, and add another WHERE clause.

Then we end up with something like this:

```dataview
TABLE WITHOUT ID
  min(rows.file.folder) as Folder,
  min(rows.file.link) as File, 
  sum(rows.item.tags) as Tags, 
  map(items, (r) => "[" + r.status + "] " + r.text) as Tasks
FLATTEN file.tasks as item
GROUP BY file.folder + file.name + item.tags
FLATTEN array( filter( rows.item, (r) => r.status != "x" ) ) as items
WHERE items
```

There are three changes made in this variant of the query:

  • FLATTEN array(filter( rows.item, (r) => r.status != "x" ) ) as items, which is the crucial part, where we filter out only the tasks we want to list. Here you can exchange the r.status != "x" with almost any of the WHERE clauses you tried. Just make sure it returns a positive value if you want to keep that task.

    Then the array( filter ( ... ) ) as items will make sure to return this as a new list of the wanted tasks

  • That is then used for displaying purposes in the map( items, ... ) row above the first flatten (which used to be map( rows.itme, ... )

  • And it’s used in the last line WHERE items, where it verifies that we actually have some tasks we want to show for this combination of folder, file and tag.

Thank you @holroy.
Wow, thank you!

I won’t lie to you. I do understand your solution, but there are some things I won’t be able to replicate easily in another use case.

Anyhow, I thank you again for your reply. And for writing this great tutorial on how to use #dataview .

It took a little bit of fiddling to get it to be this variant, and I’ve written quite a few queries lately, so don’t be dismissed by not being able to write such a query in one go.

The trick to building queries like this is to write multiple queries, where you start out listing all of the information you might want in the end result, and then write intermediate queries where you narrow down your data set, and start grouping/filtering/flattening according to your needs.

And of course, if you’re stuck at some point ask for guidance with good descriptions of your data set and the current query and what you ideally would like out of it. In due time, you’ll get to write the queries you want more and more on your own.

1 Like

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