Querying Tasks from Yesterday's Daily Note in Obsidian

I'm a fairly prolific note-taker and have been, in both my personal and professional lives, for years.

I manage my notes with Obsidian (in fact, even this post was drafted in it) putting its Daily Notes Plugin into use to manage my to-do list. At the start of each day, the rollover daily todos plugin copies any incomplete to-do's over from the previous day's notes.

This combination gives me a fresh notes file each day with any unfinished tasks copied across.

I've been using this system for quite a while, so there are now a lot of daily note files, which does make manually navigating to an older set of notes a little inconvenient.

Recently, I've developed a more regular need to look at the previous day's todo list: I've found that it's sometimes quite helpful to also be able to see what was completed in the previous day (as well as being useful for giving updates, it helps me avoid getting stuck in a rut where it feels like I'm not getting anything done).

Obsidian has template support which is able to insert the current date, but it's quite simple and cannot do date maths to work out what the filename of the previous notes file would be.

In this post, I'm going to write about the approach that I've taken to have Obsidian show me yesterdays work and display a link back to the relevant notes file.


Templater

Originally, I went hunting for something that would allow me to simply generate a link back to the previous note (essentially, I wanting to do something like include {{date - 1d}} in the base template).

Whilst looking for options to that, I stumbled across these Templater examples.

So, my first attempt was to have Templater render the following:

<%tp.date.now("YYYY-MM-DD", -1)%>

It worked, but, didn't fit into my workflow very well for a couple of reasons:

  • Despite being set to watch for new files, Templater wasn't picking up on new daily notes (I think that's because they don't go into the default new note location) and so wasn't replacing the placeholder.
  • Dropping a template into a file requires clicking a different button on the left (though fixing the above would probably address this one too).

Whilst looking online to see whether there was a setting that I might have missed, I stumbled across an example on Reddit which used a different plugin.


dataview

The reddit example looked like it should do what I wanted and more:

  • It calculates the name of the previous file, so can cope with gaps in notes (like weekends and holidays)
  • Rather than just linking to the file, it would show the todo items in-situ.

Having read up on the dataview plugin, I wanted to be able to use it because having it enabled could potentially unlock a ton of other possibilities.

I started with the dataviewjs function from Reddit and adapted it a little so that it would take the date from the notes file being viewed (rather than always using today) and only embed completed todo items:

function getLatestDailyLog() {
    // change: take the date from the file name
    const date = new Date(dv.current().file.name);
    let month = date.getMonth() + 1;
    if (month < 10) {
        month = '0' + month;
    } else {
        month = month.toString();
    }

    const today = `${date.getFullYear()}-${month}-${date.getDate()}`;

    // get latest log that isn't today's date
    // change: path
    let dailyLogs = dv.pages('"0000_Daily_Notes"').file.name.where(p => p !== today);
    const maxLog = dailyLogs[dailyLogs.length - 1];
    // change: path
    return `0000_Daily_Notes/${maxLog}`;
}

// change: pull out the completed tasks
let tasks = dv.page(getLatestDailyLog()).file.tasks
let completed = []
for (var i=0; i < tasks.length; i++){
    if (tasks[i].completed){
        completed.push(tasks[i])
    }
}

dv.taskList(completed)

This appeared to work, however when I tested by dropping it into an older note, I found that there was an issue:

// get latest log that isn't today's date
let dailyLogs = dv.pages('"0000_Daily_Notes"').file.name.where(p => p !== today);g
const maxLog = dailyLogs[dailyLogs.length - 1];

When loaded in a historic note, this section of the code will return today's note rather than the day previous to the historic note (because the logic simply excludes the currently viewed note and then selects the last item in the list - which'll be today).

So, I changed the script to iterate through and stop when it reached the current note. I also added some logic so that it would print something meaningful if it couldn't find a previous note

function getLatestDailyLog() {
    // change: take the date from the file name
    const date = new Date(dv.current().file.name);

    // Adjust month
    let month = date.getMonth() + 1;
    if (month < 10) {
        month = '0' + month;
    } else {
        month = month.toString();
    }

    // Turn into a date string
    const today = `${date.getFullYear()}-${month}-${date.getDate()}`;

    // change: Get latest log that isn't today's date
    let pages = dv.pages('"0000_Daily_Notes"').file.name;
    let last = false;
    for (var i=0; i < pages.length; i++){
        if (pages[i] == today){
            if (!last){
                return false;
            }
            break;
        }
        // Otherwise, update last
        last = pages[i];
    }

    return `0000_Daily_Notes/${last}`;
}

let lastpage = getLatestDailyLog()
if (lastpage){
    let tasks = dv.page(lastpage).file.tasks
    let completed = []
    for (var i=0; i < tasks.length; i++){
        if (tasks[i].completed){
            completed.push(tasks[i])
        }
    }

    dv.taskList(completed)
}else{
    dv.span("None found");
}

Before adding it to the template for my Daily Notes, I first tested by dropping it into the current day's notes. Although the screenshot is a somewhat contrived example, the result looked like this

Screenshot of the result that dataview rendered. It's a list of the todo list items that I completed on the 26th

A key thing that I hadn't realised is that the information is rendered but is not written back into the underlying file.

I can think of uses where that might be problematic, but in this case it's ideal: it means that the duplication of this data has no negative impact on searches etc.

Happy with the change, I added it into my TODO list template.


Extra: Creating a Related Items Section

Part of the reason that I got excited when I stumbled across the dataview plugin is that it enables you to run arbitrary queries against your vault.

One potential use, for example, is to create a "Related Items" section based on the tags applied to a note:

TABLE WITHOUT ID
 file.link as "Topic",
 join(file.etags) as "Tags"
FLATTEN list(filter(file.etags,
  (t) => econtains(this.file.etags, t))) as matchingtags
WHERE 
    length(matchingtags) > 0
    AND file.name != this.file.name
SORT length(matchingtags) desc, file.mtime desc
LIMIT 5 

This injects a table like the following, with entries ordered by the number of matching tags:

Table showing related items. It includes the name of the linked file and its tags

So, now when I create and tag up a new note, it can point me towards others that might be helpful - functionality that's potentially invaluable when troubleshooting.


Conclusion

I really do quite like Obsidian, in fact, it's one of the relatively few bits of non-FOSS software than I actively choose to use (and do not mind paying something for).

But, I've still just barely scratched the surface of what it can do. The dataview plugin is likely to be just the first step down a weird and wonderful path of discovery.