Extracting uncompleted task from structured task list with dataview

Hi everyone,
I am new to Obsidian and would like to implement it into my workflow as a scientist.
Here I focus on my task management. For this I would like to use the dataview plugin and very simple queries to pull out daily tasks.
I could not find an answer to my question so far, even though it is probably easy to solve.

I structured my work in projects that aim to answer a scientific question. To answer the latter I usually run several experiments at same time to gather experimental data. Those experiments usually take several days/weeks to complete. Because they don’t differ much in their steps I use tasklists templates in which I only change the date of each step. (My date preset is DD.MM) So the following structure emerges:

(file name: Current Projects)

Project 1

  • [ ] EXP 1 - Title
    • [ ] Step 1
      • [x] 27.11 - Substep 1
      • [ ] 28.11 - Substep 2
      • [ ] …
  • [ ] EXP 2 - Title
    • [ ] Step 1
      • [x] 27.11 - Substep 1
      • [ ] 28.11 - Substep 1
      • [ ] …

Bc it is tedious copying each task out of this structure every day I would like to automate it so that I can have a daily list of tasks at hand the moment I enter the lab.

I wish to end up with a daily task list for the 28.11 that looks like the following:

Project 1

  • [ ] EXP 1 - Title
    • [ ] Step 1
      • [ ] 28.11 - Substep 1
  • [ ] EXP 2 - Title
    • [ ] Step 1
      • [ ] 28.11 - Substep 1

MY PROBLEM:
When I use dataview to collect all uncompleted tasks:

TASK
FROM "Current Projects" 
WHERE !completed 
WHERE date("2022-11-28")

… I get an output that contains all completed subtasks as well as subtasks without a date.

  • How can I solve this?
  • How can I also show the specific project name ahead of the tasklist?

Thank you all in advance for your help! :slight_smile:

1 Like

First thing to clarify: why you think dataview can read 28.11 as a date in the middle of the task text? Neither it follows the minimum syntax for a valid date, neither it’s a specific field. 28.11 it’s just a substring in string 28.11 - Substep 1.

https://blacksmithgu.github.io/obsidian-dataview/annotation/types-of-metadata/

I thought dataview can read the date “28.11” because I specified the date format to this syntax in the dataview settings. So I concluded it can search for text in a certain format and knows it is a date. But I guess this setting only defines the output format when using date().
So one always has to write dates like “2022-11-28”?

Format in settings is related with the render, i.e., the output of the date format.
To a valid date you need to write something like “2022-11-28”. But that isn’t enough. It needs to be a value per se (considering the fields as the pair key: value, dates need to be a value, alone, not with something else). If 2022-11-28 - substep, this isn’t a date, is a string (in specific case is the text - file.tasks.text - of the task).

I think you need to explore something like this:

## EXP 1 - Title
- [ ] Step 1
    - [x] (dt:: 2022-11-27) - Substep 1
    - [ ] (dt:: 2022-11-28) - Substep 2
    - [ ] …
## EXP 2 - Title
- [ ] Step 1
    - [x] Substep 1 (dt:: 2022-11-27)
    - [ ] Substep 1 (dt:: 2022-11-28)
    - [ ] …

And use a variant of this query (in tasks we have metadata related with the header/section where the task is placed)

TASK
FROM "Current Projects" 
WHERE !completed 
WHERE dt = date("2022-11-28")
GROUP BY section
1 Like

Thank you this really helped!
We are close to the goal :slight_smile:

My current input (as you suggested):

EXP 1 - Title

  • [ ] Step 1
    • [x] (dt:: 2022-11-27) - Substep 1
    • [ ] (dt:: 2022-11-28) - Substep 2

to which I applied this query:
(including your mentioned “meta(section).subpath” from another forum)

TASK 
FROM "Current Projects" 
WHERE !completed 
WHERE dt = date("2022-11-28")
GROUP BY meta(section).subpath

and got this:

EXP 1 - Title

  • [ ] (dt:: 2022-11-28) - Substep 2

Final question: How can I show "Step1 above “Substep 2”?

(I realised that I can achieve my desired date output by activating the reading mode, genius ^^)

In regular DQL you can’t achieve that, i.e., if you point for a subtask you can’t see the parent. It works in reverse logic: if you point a task which is parent, then you’ll see all the children. But if the filtered task is a children we don’t see the parent.

In dataviewjs (dvjs) maybe it’s possible, I don’t know.

Your original input data is well designed. It does not need to be modified if you use the DVJS10.

Topic

Summary
  • How to get the specific texts which contain a formatted string like "dd.MM - " from the file.lists for open tasks with the desired structure?

Test

Summary
  • dataview: v0.5.46

Input

Summary

dictionary files:

  • Location: “100_Project/01_dataviewjs/01_by_example/Q25_ddMMyyyy/Q25_test_data”

folder: 01_Note_Structures

Summary_01
  • filename : dic_19730101
---
Date: 1973-01-01
---
#Project/P01

## The Note Structures

### main tasks : CASE S1
- 28.11 - main task GH
  - [ ] task g
  - [ ] task h
      - [ ] task h1
      - [ ] task h2

### main tasks : CASE S2
- [ ] 28.11 - main task IJ
  - [ ] task i
  - [ ] task j
      - [ ] task j1
      - [ ] task j2

### some tasks : CASE S3
- some task MN
  - [ ] task m
  - [ ] task n
      - [ ] task n1
      - [ ] 28.11 - task n2 (==This task has no children.==)
      - [ ] 29.11 - task n3
      - [ ] 30.11 - task n4

### some tasks : CASE S4
- [ ] some task RS
  - [ ] task r
  - [ ] task s
      - [ ] task s1
      - [ ] 28.11 - task s2 (==This task has no children.==)
      - [ ] 29.11 - task s3
      - [ ] 30.11 - task s4

### other tasks: CASE S5
- other task UV
  - [ ] task u
  - [x] task v : Completed and not fullyCompleted
      - [ ] task v1
      - [ ] task v2

### other tasks: CASE S6
- [ ] other task XY
  - [ ] task x
  - [x] task y : Completed and not fullyCompleted
      - [ ] task y1
      - [ ] task y2



folder: 02_data_from_Mork

Summary_02
  • filename : dic_19730201_EXP1
---
Date: 1973-02-01
---
#Project/P02

## The data from Mork
-   [ ] **EXP 1 - Title**
    -   [ ] Step 1
        -   [x] 27.11 - Substep 1
        -   [ ] 28.11 - Substep 2
        -   [ ] 29.11 - Substep 3
        -   [ ] 30.11 - Substep 4
        -   [ ] 01.12 - Substep 5
        -   [ ] 02.12 - Substep 6
        -   [ ] 03.12 - Substep 7



  • filename : dic_19730202_EXP2
---
Date: 1973-02-02
---
#Project/P02

## The data from Mork
-   [ ] **EXP 2 - Title**
    -   [ ] Step 1
        -   [x] 27.11 - Substep 1
        -   [ ] 28.11 - Substep 2
        -   [ ] 29.11 - Substep 3
        -   [ ] 30.11 - Substep 4
        -   [ ] 01.12 - Substep 5
        -   [ ] 02.12 - Substep 6
        -   [ ] 03.12 - Substep 7



DVJS10_use_fLists_hide_fullyCompleted_subTasks_and_filter_by_string_and_display_uncompleted_tasks: For CASE S4 or S6 ONLY

Summary

Main DVJS

Code Name Data type Group By Purposes Remark
DVJS10
_use_fLists
_hide
_fullyCompleted_subTasks
_and
_filter_by_string
_and
_display_uncompleted_tasks
file.lists:
a list item or
a task item
with a formatted string like "dd.MM - "
yes 1.To hide fullyCompleted sub-tasks
2.To show uncompleted sub-tasks under list items and completed (sub)-tasks
3.To show items that contain a formatted string
4.To display the result by using dv.taskList [with the original structure]
1.The DVJS10 is based on the DVJS10 in the following topic.
- Solutions: by Justdoitcc

2.Recursive Functions:
2.1 taskAny
2.2 hide_fullyCompleted_subTasks
2.3 designed by Developers:
2.3.1 [email protected]
2.3.2 [email protected]

Notes

Summary

Q1: How to modify the following code like MAIN_TITLE? (M01)

Summary_Q1
Original Example: Q1 (To be modified)
```dataviewjs
// M01. define MAIN_TITLE: 
// #####################################################################
const MAIN_TITLE = "Project 1: Current Projects";
```

A1:

Another Example: A1_11
```dataviewjs
// M01. define MAIN_TITLE: 
// #####################################################################
const MAIN_TITLE = "Project 1";
```

Q2: How to modify the following code like STRING_TO_SEARCH_FOR? (M03)

Summary_Q2
Original Example: Q2 (To be modified)
```dataviewjs
// M03. define STRING_TO_SEARCH_FOR: 
// #####################################################################
// const STRING_TO_SEARCH_FOR = "29.11 -"; // "dd.MM -"
const STRING_TO_SEARCH_FOR = "28.11 -"; // "dd.MM -"
```

A2:

Another Example: A2_21
```dataviewjs
// M03. define STRING_TO_SEARCH_FOR: 
// #####################################################################
const STRING_TO_SEARCH_FOR = "29.11 -"; // "dd.MM -"
// const STRING_TO_SEARCH_FOR = "28.11 -"; // "dd.MM -"
```

Q3: How to modify the following code like pages? (M11)

Summary_Q3
Original Example: Q3 (To be modified)
```dataviewjs
// M11. define pages: gather all relevant pages
// #####################################################################
// let pages = dv.current();
let pages = dv
    .pages('"100_Project/01_dataviewjs/01_by_example/Q25_ddMMyyyy/Q25_test_data/02_data_from_Mork" and #Project')
    .where((page) => dv.func.contains(page.file.name, "dic_")); 
```

A3:

Another Example: A3_31
```dataviewjs
// M11. define pages: gather all relevant pages
// #####################################################################
// let pages = dv.current();
let pages = dv.pages('"Current Projects"');
```
Another Example: A3_32
  • dv.current(): the page the script is currently executing on
```dataviewjs
// M11. define pages: gather all relevant pages
// #####################################################################
let pages = dv.current();
```

Q4: How to modify the following code like MAIN_TITLE to hide it in the result? (M82)

Summary_Q4
Original Example: Q4 (To be modified)
```dataviewjs
// M82. output MAIN_TITLE:
// #####################################################################
dv.header(MAIN_TITLE_HEADER_LEVEL, MAIN_TITLE);
```

A4:

Another Example: A4_41
```dataviewjs
// M82. output MAIN_TITLE:
// #####################################################################
// dv.header(MAIN_TITLE_HEADER_LEVEL, MAIN_TITLE);
```

Code DVJS10_use_fLists_hide_fullyCompleted_subTasks_and_filter_by_string_and_display_uncompleted_tasks: For CASE S4 or S6 ONLY

Summary_code
title: DVJS10_use_fLists_hide_fullyCompleted_subTasks_and_filter_by_string_and_display_uncompleted_tasks => 1.To hide fullyCompleted sub-tasks 2.To show uncompleted sub-tasks under list items and completed (sub)-tasks 3.To show items that contain a formatted string 4.To display the result by using `dv.taskList` [with the original structure]
collapse: close
icon: 
color: 
```dataviewjs
// M01. define MAIN_TITLE: 
// #####################################################################
const MAIN_TITLE = "Project 1: Current Projects";


// M03. define STRING_TO_SEARCH_FOR: 
// #####################################################################
// const STRING_TO_SEARCH_FOR = "29.11 -"; // "dd.MM -"
const STRING_TO_SEARCH_FOR = "28.11 -"; // "dd.MM -"


// M05. define GROUP_HEADER_LEVEL: 
// #####################################################################
let GROUP_HEADER_LEVEL = 3 ;


// M07. define MAIN_TITLE_HEADER_LEVEL: 
// #####################################################################
const MAIN_TITLE_HEADER_LEVEL = 2 ;


// M10. define regPattern: according to STRING_TO_SEARCH_FOR
// #####################################################################
const regPattern = /^\d{2}\.\d{2}\s-/;


// M11. define pages: gather all relevant pages
// #####################################################################
// let pages = dv.current();
let pages = dv
    .pages('"100_Project/01_dataviewjs/01_by_example/Q25_ddMMyyyy/Q25_test_data/02_data_from_Mork" and #Project')
    .where((page) => dv.func.contains(page.file.name, "dic_")); 


// M21. define Recursive Function:To expand search to children
// #####################################################################
const taskAny = function taskAny(t, f) {
    if (f(t)) {
        return true;
    }
    for (let sub of t.children) {
        if (taskAny(sub, f)) {
            return true;
        }
    }
    return false;
};


// M25. define Recursive Function:To gather un-fullyCompleted children
// (1.To hide a fullyCompleted subtask item 2.Not to hide a root item)
// (comments)FLATTEN_CASE_10A:To gather an un-fullyCompleted task item that 
//                            doesn't contain a specific string "28.11 - " in the form "dd.MM - "
// 
// FLATTEN_CASE_10B:To gather an un-fullyCompleted task item that
//                  contains a specific string "28.11 - " in the form "dd.MM - "
//
//                  (CASE S3 or S4: For example
//                    1.To gather the last un-fullyCompleted subtask  
//                    item which contains a specific string "28.11 - " in the form "dd.MM - "
//                    in the [[dic_19730412]] or [[dic_19730417]]
// 
//                    2.To exclude the last un-fullyCompleted subtask
//                    item which contains a specific string "27.11 - " in the form "dd.MM - "
//                    in the [[dic_19730812]] and [[dic_19730817]]
//                   )
// #####################################################################
// dv.span(hide_fullyCompleted_subTasks(root_tasks_or_lists.text));
const hide_fullyCompleted_subTasks = (t) => ({
    ...t,
    children: t.children
        .filter((st) =>
            taskAny(
                st,
                (st) =>
                    // Remarked by Justdoitcc 2022-12-02
                    // FLATTEN_CASE_10A
                    // (st.task &&
                    //     !st.fullyCompleted &&
                    //     // st.outlinks.length === 0
                    //     !dv.func.contains(st.text, STRING_TO_SEARCH_FOR) &&
                    //     !regPattern.test(L.text)
                    // ) ||

                    // Edited by Justdoitcc 2022-12-02
                    // FLATTEN_CASE_10B
                    st.task &&
                    !st.fullyCompleted &&
                    // dv.func.contains(st.outlinks, "[[Note P]]")
                    dv.func.contains(st.text, STRING_TO_SEARCH_FOR) // &&
                    // st.children.length === 0
            )
        )
        .map(hide_fullyCompleted_subTasks),
});






// M31. define root_tasks_or_lists : gather a root item
// WHERE_CASE_11 :To gather a root list item or a root task item
// (comments)WHERE_CASE_12A:To gather a root task item which is not fullyCompleted
// (comments)WHERE_CASE_12B:To gather a root list item which contains a subtask
// (comments)               item that is not fullyCompleted
// (comments)               (CASE S1 or S3 or S5: To gather
// (comments)                [[dic_19730402]] or [[dic_19730412]] or [[dic_19730522]])
// WHERE_CASE_13A:To gather a root task item which is not fullyCompleted and
//                doesn't contain a specific string in the form "dd.MM - "
//                (CASE S4 or S6:To gather [[dic_19730417]] or [[dic_19730527]])
// #####################################################################
let root_tasks_or_lists = pages.file.lists
    .where((L) => !L.parent)
    // .where(
    //     (L) =>
    //         (L.task && !L.fullyCompleted) ||
    //         (!L.task &&
    //             L.children.length > 0 &&
    //             L.children.filter((c) => c.task && !c.fullyCompleted))
    // );
    .where(
        (L) =>
            // Edited by Justdoitcc 2022-12-02
            // WHERE_CASE_13A
            // To exclude the root task item which starts with "27.11 -".
            // [[dic_19730807]]:
            // - [ ] 27.11 - also remember to do this task for it
            (L.task &&
                !L.fullyCompleted &&
                // L.outlinks.length === 0
                !dv.func.contains(L.text, STRING_TO_SEARCH_FOR) &&
                !regPattern.test(L.text)
            ) 
    );

    

// M33. Hide completed Subtasks:
// #####################################################################
root_tasks_or_lists.values = root_tasks_or_lists.values.map(
    hide_fullyCompleted_subTasks
);


// M81. deal with root_tasks_or_lists: 
// To groupBy t.path
// To sort in ascending order
// #####################################################################
// t.path   : "100_Project/02_dataview/Q12_Tasks/Q12_test_data02/08/dic_20110801.md"
// group.key: "100_Project/02_dataview/Q12_Tasks/Q12_test_data02/08/dic_20110801.md"
root_tasks_or_lists = root_tasks_or_lists
    .groupBy((t) => t.path)
    .sort((group) => group.key, "asc");


// M82. output MAIN_TITLE:
// #####################################################################
dv.header(MAIN_TITLE_HEADER_LEVEL, MAIN_TITLE);


// M83. output root_tasks_or_lists:
// #####################################################################
for (let group of root_tasks_or_lists) {

    
    // M83.FR13 define s_display_name:
    // #####################################################################     
    let s_file_name = dv.page(group.key).file.name;
    let s_display_name = s_file_name + ": ";
    
    
    // M83.FR15 define s_link:  \[[alphabet#a|pqr]\]  //=>pqr
    // ##################################################################### 
    // let s_link =  "[[" + group.key + "#" + Heading + "|" + s_display_name + "]]";//=>pqr
    let s_link =  "[[" + group.key + "|" + s_display_name + "]]";
    
    
    // M83.FR21 output s_link: \[[08/dic_20110801.md|dic_20110301: 7 uncompleted]\]
    // ##################################################################### 
    dv.header(GROUP_HEADER_LEVEL, s_link);
    
    
    // M83.FR31 output group.rows:
    // ##################################################################### 
    dv.taskList(group.rows, false);
}


```

Screenshots(DVJS10)

const STRING_TO_SEARCH_FOR = “28.11 -”;