GTD for Daily notes

I have adapted this GTD Obsidian tool by @AlanG for my Daily tasks. It shows four sections: tasks Started Today, Completed today, Pending as of Today, and Due today. They work in connection with the date in the Daily note. I use the format YYYYMMDD, so you will have to perform some minor changes if you work with the date-dashed version.

The core of the script is almost identical to the original GTD. I use the same core functions and variables. I also added some comments to make it self-explanatory.

tasksTodayDaily.js

/*
 * ---------------------
 * CONFIGURATION OPTIONS
 * ---------------------
 *
 * Add the folders, tags, and headings which should be excluded.
 * Tasks found on pages with these tags or paths will not appear in the Tasks page.
 * Headings will to exclude tasks that fall under a heading with this name.
 */

const globalExclude = {
  folders: [
    'Extras'
  ],
  tags: [
    '#exclude-master-tasklist',
    // '#completed'
  ],
  headings: [
    '🌱 Daily Habits'
  ]
}
/*
 * When displaying tasks from a project, tasks that fall under headings with these
 * names won't have these headings displayed when showing the project info
 */
const hideSubsectionName = [
  'Todo',
  'Tasks'
]

/*
 * ----------------------------
 * END OF CONFIGURATION OPTIONS
 * ----------------------------
 */

// Set up the variables to store the tasks and projects
const tasks = []
const noNextAction = []
const excludeItems = ['', ...globalExclude.tags]
globalExclude.folders.forEach(folder => excludeItems.push(`"${folder}"`))
const globalExcludeString = excludeItems.join(' AND -')
// console.log("globalExcludeString", globalExcludeString)


// Define groups for Daily tasklist page
/*
    For the daily view we have so far these groups:

    * Tasks started today
    * Tasks completed today
    * Pending tasks carrying over from previous days
    * Tasks due today, which already exists but doesn't properly work

*/
const Groups = {
  Started: 0,
  Completed: 1,
  Pending: 2,
  Due: 3,
  Normal: 4
}

const self = dv.current();
const path = require('path');

/**
  isStartedToday()
  Shows only tasks that were started on the day of the Daily note

    CRITERIA
    --------
 * Task text must not be empty
 * Task must NOT be completed today
 * Task header path corresponds to today's daily note and is not completed
 * Task with plus-sign emoji and done emoji must not show as Started but Completed
 * Completed tasks have four ways of marking: 
    * Done emoji with date with dashes
    * Done emoji with date with no dashes
    * The `completion `property` and a date with dashes
    * The completion circle filled with a check mark
    
  @param   {task} t - An Obsidian task
  @returns {boolean} true if matching all conditions, false otherwise
*/
function isStartedToday(t) {
  let taskTextNotEmpty = t.text.length > 0;
  // let notCompletedTodayDaily = self.file.path == t.header.path && !t.completed
  let taskInDailyNotCompleted = self.file.path == t.header.path && !t.completed

  let DateWithDashes = moment(self.file.name).format('YYYY-MM-DD')
  let DateNoDashes = moment(self.file.name).format('YYYYMMDD')
  // let containsDateNoDashes = t.text.match(self.file.name) 
  //                            && typeof(self.file.name) == 'number'
  let matchPlusEmojiDateDashes = t.text.match('➕' + DateWithDashes) //  started today
  let containsDateWithDashes = t.text.match(DateWithDashes)
  let containsDateCompletionEmojiDashes = t.text.match('✅ ' + DateWithDashes) // completed
  let containsDateCompletionEmojiNoDashes = t.text.match('✅ ' + DateNoDashes)   // completed
  let containsDateCompletionColonColon = t.text.match('completion:: ' + DateWithDashes) // completed
  return (
        //  containsDateNoDashes 
        // || containsDateWithDashes 
        matchPlusEmojiDateDashes
        && !containsDateCompletionEmojiDashes
        && !containsDateCompletionEmojiNoDashes
        && !containsDateCompletionColonColon
        && taskInDailyNotCompleted
        && taskTextNotEmpty
  )
}

/**
  isCompletedToday()
  Shows only tasks that were completed on the day of the Daily note

    CRITERIA
    --------
 * Task text must not be empty
 * Task must be completed 
 * Task header path corresponds to today's daily note
 * Completed tasks have four ways of marking: 
    * Done emoji with date with dashes: '✅ ' + DateWithDashes
    * Done emoji with date with no dashes: '✅ ' + DateNoDashes
    * `completion` property and date with dashes: 'completion:: ' + DateWithDashes
    * The completion circle filled with a check mark: t.completed
	
  @param   {task} t - An Obsidian task
  @returns {boolean} true if matching all conditions, false otherwise
*/
function isCompletedToday(t) {
  let taskTextNotEmpty = t.text.length > 0;
  // capture tasks that are completed today
  let completedTodayDaily = self.file.path == t.header.path && t.completed
  // date in two formats
  let DateWithDashes = moment(self.file.name).format('YYYY-MM-DD')
  let DateNoDashes = moment(self.file.name).format('YYYYMMDD')
  // capture tasks which text contains the current date, which could be start or completion
  let containsDateNoDashes = t.text.match(self.file.name)
  let containsDateWithDashes = t.text.match(DateWithDashes)
  // boolean statements
  let containsDateCompletionEmojiDashes = t.text.match('✅ ' + DateWithDashes)
  let containsDateCompletionEmojiNoDashes = t.text.match('✅ ' + DateNoDashes)
  let containsDateCompletionColonColon = t.text.match('completion:: ' + DateWithDashes)

  return (
          containsDateCompletionEmojiDashes
       || containsDateCompletionColonColon
       || containsDateCompletionEmojiNoDashes
       || completedTodayDaily
       && taskTextNotEmpty
  )
}

/**
 isOlderThanThisDailyNote()
 Pending tasks. 
 Shows only older tasks that were not yet completed at the date of the Daily note

   CRITERIA
   --------
 * Task text must not be empty
 * Task must NOT be completed 
 * Task header path corresponds to today's daily note
 * Task must be older than current daily note
 * Task contains a `due` date property
	
  @param   {task} t - An Obsidian task
  @returns {boolean} true if matching all conditions, false otherwise
*/
function isOlderThanThisDailyNote(t) {
  let basename = path.parse(t.header.path).name
  let DateWithDashes = moment(self.file.name).format('YYYY-MM-DD')
  let mDailyDate = moment(self.file.name, 'YYYYMMDD', true); // Note
  let mAnyTaskDate = moment(basename, 'YYYYMMDD', true);  // task anywehere else
  //  <  0 does not include pending task if they were created today
  //  <= 0 includes pending task if they were created today
  let isOlderNote = mAnyTaskDate.diff(mDailyDate) < 0 // task date - note date
  let containsDateDueColonColon = t.text.match('due:: ' + DateWithDashes)
  return (
            isOlderNote
         && !t.completed
         && !containsDateDueColonColon
  )
}

/**
 tasksDueToday()
 Shows only tasks that are marked due today in the Daily note

   CRITERIA
   --------
 * Task text must not be empty
 * Task must NOT be completed 
 * Task header path corresponds to today's daily note
 * Task must be older than current daily note
	
  @param   {task} t - An Obsidian task
  @returns {boolean} true if matching all conditions, false otherwise
*/
function tasksDueToday(t) {
  let taskTextNotEmpty = t.text.length > 0;
  let containsDateDueColonColon = false;
  let includesDateDueColonColon = false
  let DateWithDashes = moment(self.file.name).format('YYYY-MM-DD')
  // includesDateDueColonColon    = t.text.includes(DateWithDashes)

  if (t.due) {
    // console.log(DateWithDashes)
    containsDateDueColonColon = t.text.match('due:: ' + DateWithDashes)
    // console.log(containsDateDueColonColon)
  }

  return (
          taskTextNotEmpty
       && t.due
       && !t.completed
      //&& includesDateDueColonColon
       && containsDateDueColonColon
  )
}


/**
 * Take a task and the page it's from, and return a formatted element for the tasks array
 * @param {*} task 
 * @param {*} page 
 * @returns {object}
 */
function generateTaskElement(task, page) {
  // console.log(task)
  let group = Groups.Normal
  // if (task.tags.includes('#someday')) {
  if (isStartedToday(task)) {
    group = Groups.Started
  } else if (isCompletedToday(task)) {
    group = Groups.Completed
  } else if (isOlderThanThisDailyNote(task)) {
    group = Groups.Pending
  } else if (tasksDueToday(task)) {
    group = Groups.Due
  }
  return {
    task: task,
    date: (page.created ? page.created.ts || moment(page.created).valueOf() : null) || page.ctime.ts,
    group: group
  }
}


/*
 * Process tasks
 */
dv.pages("-#exclude" + globalExcludeString)
  .where(p => p.file.tasks.length && !p['kanban-plugin'] && !p['exclude_master_tasklist']).file
  .forEach(page => {
    page.tasks
      .where(t =>
        t.text && // where the task has text (is not blank)
        // !t.completed && // and not completed  // 1063
        // t.completed &&  // 1004
        !t.tags.includes('#exclude') && // and not excluded
        (!t.header.subpath || !t.header.subpath.includes('exclude')) &&
        !globalExclude.headings.includes(t.header.subpath) // and the heading is not excluded
      )
      .forEach(task => tasks.push(generateTaskElement(task, page)))
  })

// Sort tasks into groups, then ascending by created time
tasks.sort((a, b) => a.group - b.group || a.date - b.date)
// console.log(tasks)

/**
 * Output a formatted task list
 * @param {number|null} group - Filter for tasks in a particular group, or null for all tasks
 * @param {string|null} header - The text header for the task list
 */
function taskList(group, header) {
  const list = isNaN(group) ? tasks : tasks.filter(x => x.group === group)
  if (list.length) {
    if (header) dv.header(2, header)
    // dv.taskList(list.map(x => x.task), false)
    dv.taskList(list.map(x => x.task), true)
    return list.length
  } else return 0
}

let todayWithDashes = "*" + moment(self.file.name).format('YYYY-MM-DD') + "*"

// Output the task list
let nStarted   = taskList(Groups.Started, 'Started today: ' + todayWithDashes)
let nCompleted = taskList(Groups.Completed, 'Completed today: ' + todayWithDashes)
let nPending   = taskList(Groups.Pending, 'Still Pending Tasks Today: ' + todayWithDashes)
let nDue       = taskList(Groups.Due, 'Due Today: ' + todayWithDashes)

if (nStarted == 0 && nCompleted == 0 && nPending == 0 && nDue == 0) {
  dv.header(2, "No tasks to report") 
}

Put this script under your Scripts folder, but anywhere else should be okay. Call it from your Daily template with:

```dataviewjs
dv.view("tasksTodayDaily")
```

The only problem I have found so far is that line 66 const path = require('path'); does not work in the iPad. I guess not all the JavaScripts libraries are loaded in Obsidian for iOS.

The task menu called with Alt+T also works with this new script as well as the projects and their dates (due, completion, etc.)

Enjoy!

PS. This is a screenshot of a mockup Daily notes displaying all sections.

Suggestions and critique welcome.

This seems great!

I known nothing about js. I followed the instructions, and put a note has tasks as following:


and I got this:

Is this correct?

I expect the tasks in test note should be shown up in note 2024-03-17 instead of No tasks to report.

Maybe there are something I missed?

Try with the date without dashes.