How to use constants (and compare dates) in dataviewjs?

I can’t seem to understand how constants work in dataviewjs. Here are two queries, which yield the exact same results (a single task (without any headings):

dv.taskList(dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due.day == dv.date('2022-12-07').day), false);
const dueTasks = dv.taskList(dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due.day == dv.date('2022-12-07').day), false);
if(dueTasks.length > 0) {
  dv.header(3, "Due today");
  dv.taskList(dueTasks);
}  

I don’t understand why the second query doesn’t produce the heading “Due today”, and - even weirder - how it can produce the task but not the heading. The task is rendered even when I just define the constant:

const dueTasks = dv.taskList(dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due.day == dv.date('2022-12-07').day), false);

How can the above query output anything?

1 Like

I think dv.taskList() is the command associated with the render (the output format), not with the values. In constants you are interested in the values, not in the output command.
So, I guess you need to remove the dv.taskList() in the const.

2 Likes

OMG, brilliant! Thank you so much!

So here is the code that works as expected:

const dueTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due.ts == dv.date('2022-12-07').ts);
if(dueTasks.length > 0) {
  dv.header(3, "Due today");
  dv.taskList(dueTasks, false);
}  

(Edited code to fix it based on discussion below)

1 Like

And would you use this in a template (e.g. Templater template to fill in the date) so when you make a daily note it has “due today” in it?

Yes, exactly.

Here is what it currently looks like in my template:

const today = '<%tp.date.now("YYYY-MM-DD",0,tp.file.title,"YYYY-MM-DD")%>'
const overdueTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due < dv.date(today));
if(overdueTasks.length > 0) {
  dv.header(3, "Overdue");
  dv.taskList(overdueTasks, false);
}  

const dueTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due.ts == dv.date(today).ts);
if(dueTasks.length > 0) {
  dv.header(3, "Due today");
  dv.taskList(dueTasks, false);
}  

const startingTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => !(t.due && t.due.ts == dv.date(today).ts))
  .where(t => t.start && t.start.ts == dv.date(today).ts);
if(startingTasks.length > 0) {
  dv.header(3, "Starting today");
  dv.taskList(startingTasks, false);
}  

 const ongoingTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.start && t.start < dv.date(today));
if(ongoingTasks.length > 0) {
  dv.header(3, "Ongoing tasks");
  dv.taskList(ongoingTasks, false);
} 

(Edited as mentioned below)

1 Like

This is super helpful - can you give an example of the input used ? (eg how you denote tasks)
Thanks !

Just normal tasks, using - [ ]

Hi there, I noted that you do the same call to get all tasks, before filtering them over and over again. This can be improved upon, and I think it also looks a little cleaner, so here is an alternate version of your code, which should do the same (although I’ve not tested this particular version).

const today = '<%tp.date.now("YYYY-MM-DD",0,tp.file.title,"YYYY-MM-DD")%>'
const thisDay = dv.date(today).day

// Get all non-completed task, just one time
const alltasks = dv.pages()
  .file.tasks
  .where(t => !t.completed)

// My wanted tasklists
let overdueTasks = []
let dueTasks = []
let startingTasks = []
let ongoingTasks = []

// Loop through all tasks _once_, and filter them
for (let task of tasks) {

  if (task.due && task.due.day < thisDay) 
    overdueTasks.push(task)

  if (task.due && task.due.day == thisDay)
    dueTasks.push(task)
    if (task.start && task.start.day == thisDay)
       startingTasks.push(task)
 
  if (task.start && task.start.day < thisDay) 
    ongoingTasks.push(task)
}

// Display the various taskslist, _if_ they
// have any tasks at all

if (overdueTasks.length > 0) {
  dv.header(3, "Overdue");
  dv.taskList(overdueTasks, false);
}  

if(dueTasks.length > 0) {
  dv.header(3, "Due today");
  dv.taskList(dueTasks, false);
}  

if (startingTasks.length > 0) {
  dv.header(3, "Starting today");
  dv.taskList(startingTasks, false);
}  

if (ongoingTasks.length > 0) {
  dv.header(3, "Ongoing tasks");
  dv.taskList(ongoingTasks, false);
} 

Hope this doesn’t have too many errors, and that it gives some food for thoughts as to another approach to achieving your goals. I reckon, without proper testing, that this solution should be cheaper and slightly faster since it only gets the tasks once, and only loops over this set of tasks once.

I also like this way of doing it, since I think it’s a little cleaner, and you separate the logic of filtering out the tasks, from the presentation of the tasks. But this is indeed some matter of personal preferences.

2 Likes

I have some generic questions:

  • Why the fixed “today” date? Dataview results are dynamic. If you use these queries in your daily notes, they are valid for that day… In the next day you jump to a new daily note and create the same group of queries? If yes, the queries you leave the the previous daily note are “trash” (because the results aren’t static… you can’t retain the results in that day).
  • Even if you add these queries in each daily note, once the results are only valid for that day, you don’t need to fix a date with the templater, you just need to use dv.date('today').
  • And I don’t understand the reason for the use of the day. day is the number of the day in the month. Isn’t a specific a date, it’s a repeated number in each month. Is this intended? Check all open tasks for the day “7” in any month and year?
  • If the intention is an updated list of the open tasks (overdues, today, ongoing, next week, etc.), you can create the queries based on dv.date('today') in a dedicated note and fix that note in the right or left pane.

I misunderstood the meaning of day. I thought it simplifies the date information down to the granularity of the day (instead of hours, minutes, seconds). The need to do that, in turn, was based on a misinterpretation of an error message I kept getting when I didn’t have the .day. Long story short: you are right, the .day should be removed. In fact, I’ll edit my post to avoid head aches for anyone copying the code.

Yes, they are dynamic and that’s exactly why we need to fix “today” because in any particular daily note, “today” is the date of the note, not the date on which I’m looking at that note.

I believe this will not work for the reason explained above. Or what am I missing?

Thank you for your improved code, I will learn from it. While your code is better for the reasons you mention, I would still claim mine is better from the point of view of simplicity (of the code, not necessarily of the actions performed by the code). Simplicity is valuable when you know very little about dataview/ javascript and don’t want to spend hours testing (actually, already my version took me hours to figure out).

I understand that. But my core point is: in the next day, you insert a new query in the new daily note and complete some tasks. This means that your query in the previous daily note (a day before) is no more a valid query in that fixed date, because the results are no more a “picture” of that day! (because meanwhile you complete some tasks…). Accepting this - the queries are only valid for the current day -, I ask “why not use the today date”.

They are also valid for any future day…

It didn’t occur to me that I might want to keep a record of which tasks were on my list on that day, but now you mention it, I’ll think about that. One way of keeping an kind of archive would be to add a query with tasks completed on that day. But that is not the same as preserving the original todo list.

“valid” in the sense “the real tasks to do today”, because meanwhile that tasks will go away… :slight_smile: … until the moment there will be no tasks listed in that daily note.

Without a way to keep the record of the “true” list, I look to these queries as a remaining “trash” in older daily notes. But that is me. That’s why I suggested a single note dedicated to the tasks in reference to the “today” date (or a kind of “home” note).

My questions were a way to identify if you were aware of that (because many times we see the request of queries to place in daily notes to list the files modified in that date, thinking that the results are a static thing…). :slight_smile:

Watching this thread evolving has been interesting, and educational, and I would like to chime in with my current (work-in-progress) setup for daily tasks and how I tackle some of the valid questions you both have asked in this thread.

First of all, I do use a variant of the code I presented here earlier, but I fully accept and encourage that you keep an ownership and understanding of the code you’re using in your own setup. I wanted to present my take on it, just for a little bit of perspective and nuance.

Secondly, I use the Minimal theme, and heavily rely about the task decorations it provides, and I’ve extended it with some of my own variations suitable for where I am in life just now. Enough with this, let me describe my setup.

I’ve got daily notes where I keep on adding new tasks which spring to mind as something I need to do. In addition I add decorated tasks in my daily notes to keep track of vital happenings on any particular day. However, I do not complete any of my tasks within the daily notes, as I discovered that became a lot of clutter and disturbed the view of that note when looking back on my daily notes.

Instead I’ve got a note named something like “Running tasks” (but in Norwegian), which I’ve pinned to the right-hand pane (a little like @mnvwvnm showed an example of). This note has the following content currently:

  • Currently open/uncompleted tasks belonging to the current period
  • Tasks I’ve completed today (as a booster for myself)
  • Other tasks related to the vault/larger area/projects
  • A sorted list of all the decorated tasks, which helps me keep track of habits and important stuff I like to revisit

Regarding the decorated tasks, I use them for tracking some exercise, phone calls, larger spending of money, some categories related to my personal well being, and so on.

In addition I’ve got a summary note for each period (in my case each month), where I also summarise all completed tasks for that period, the various decorated tasks, a list of all notes (with a summary line from that note), and a final list of tasks which I use for rescheduling into the next month. I’m kind of adapting my own Bullet Journaling here, so I migrate some tasks, postpone some and even delete some tasks.

For me this setup has the following advantages:

  • It keeps my daily notes tidy, and to the point of what I focused on for that particular day
  • The running tasks allows for a given place to look for actions to do, and encouragement to do so
  • Every period it also allows for a cleanup, and refocusing for the next period
  • And finally, the decorated tasks helps me to journal key concepts of each particular day, and later on how the month turned out (which is easily done using dataview query on the decorated tasks)

I just wanted to share this, to give some inside into my thinking related to some of this stuff. Hope this helps somehow, and is not just rambling from a random dude totally missing the point of this thread.

1 Like

Thanks again for sharing your setup. I am just getting started with building something like a setup (with templates and a “way of doing stuff”). Have just been writing notes and linking them over the past year or so and my mind is regularly blown by all the potential in obsidian. Need to constrain myself to not spend the next two months just exploring and configuring obsidian.

So, what I’m learning here is that putting your tasks in your daily notes may not be the best idea. This resonates with one of the items on my to-do list: build a dashboard. For the time being, I am using my daily note as my daily dashboard, but I can see the point of separating the two: the dashboard would show the tasks and the daily note would be for, well, notes. More generally: the dashboard shows stuff (output) and the daily note receives stuff (input).

I am not familiar with this concept. I can guess that it has to do with CSS and how stuff is displayed, but are “task decorations” a specific feature in

  • the minimalist theme?
  • themes in general?
  • obsidian?
  • HTML/CSS?

I’m putting the new tasks in my daily note, but I’m showing/completing tasks from a view in another place. In the dashboard concept, I reckon that would be a different card/section of the dashboard.

But yes, it helps keeping my daily notes focused on what I’m experiencing or doing that day, aka the journal aspect of it. The reason I still keep the task definition there, it that it can provide some context for the task as well. I also discovered that to get the Dataview annotation of completion dates, it requires you to complete the task outside of the task definition.

I found these as part of the Minimal theme, see Checklists - Minimal Documentation, and it is a CSS way of styling tasks with different icons. Firstly I used this as part of my modified Bullet Journaling adaption, as it is possible to mark tasks as incomplete, migrated, scheduled(/postponed) and deleted, but nowadays I also use it to indicate various other actions, like shown in image below:

In this example, only the configure task would show in my task list, and the two others would show up as BuJo points in overviews. This is a contrived example, but it shows the gist of my use.

Turns out that removing .day will reintroduce the problem I was trying to solve with it, which is that

const today = '2022-12-08';
const dueTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due == dv.date(today));

Will produce an empty list, even when the dates match. I suspect that t.due is in a different format or includes time information which is not included or different in today. I am sure there is a very simple solution for this, but I have not been able to figure it out. My less elegant solution is this:

const today = '2022-12-08';
const dueTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due.day == dv.date(today).day && t.due.month == dv.date(today).month && t.due.year == dv.date(today).year);

I post it here, not to suggest that anyone should use it but rather to clarify the kind of operation that needs to be performed on t.due in order to make it work. Anyone who knows js will know a shorthand for this within seconds… I looked at moment.js but found it too complex to figure out quickly, and probably it is not even needed.

I’m not versed in JS (or any code) and I struggle to understand dates in JS. Isn’t a intuitive matter for non-coders. The comparisons in this format between dates as task.due > dv.date('today') or task.due < dv.date('today') works well. But the use of equals fails (I don’t know why, because the attributes on console seem the same). To solve these cases I use the conversion to timestamp, i.e., task.due.ts == dv.date('today').ts

1 Like

Hi,

It seems like this could be possible solution:

const dueTasks = dv.pages().file.tasks
  .where(t => !t.completed)
  .where(t => t.due && t.due.ts == dv.date('today').ts);
  dv.header(3, "Due today");
  dv.taskList(dueTasks, false);

For the record, are you using the community plugin Tasks to set the due dates or is this also a function which comes with obsidian “out-of-the-box”?

update: see that mnvwvnm already answered this

Unfortunately, this doesn’t work. It includes more dates than expected. Not sure about the logic. Where did you find the .date function/method/whatever it is?

This works. Do you have any reference for the timestamp thing (.ts)? It’s not very searchable…

Yes, that’s what I’m using. Not sure whether this is also available in core.