From d0d0974ec2f8980697793bb5ab42699c313f9efc Mon Sep 17 00:00:00 2001 From: callumalpass Date: Mon, 1 Dec 2025 21:22:00 +1100 Subject: [PATCH] docs: add formula expressions to Base templates documentation Include the actual Bases formula expressions in the formula reference tables so users can see and copy the exact syntax used. --- docs/views/default-base-templates.md | 106 +++++++++++++-------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/docs/views/default-base-templates.md b/docs/views/default-base-templates.md index ed311321..2be3d48e 100644 --- a/docs/views/default-base-templates.md +++ b/docs/views/default-base-templates.md @@ -26,81 +26,81 @@ All templates include the following calculated formula properties that you can u ### Date calculations -| Formula | Description | -|---------|-------------| -| `daysUntilDue` | Days until due date (negative = overdue, positive = days remaining, null if no due date) | -| `daysUntilScheduled` | Days until scheduled date (negative = past, positive = days remaining, null if no scheduled date) | -| `daysSinceCreated` | Number of days since the task file was created | -| `daysSinceModified` | Number of days since the task file was last modified | +| Formula | Description | Expression | +|---------|-------------|------------| +| `daysUntilDue` | Days until due date (negative = overdue, positive = days remaining, null if no due date) | `if(due, ((number(date(due)) - number(today())) / 86400000).floor(), null)` | +| `daysUntilScheduled` | Days until scheduled date (negative = past, positive = days remaining, null if no scheduled date) | `if(scheduled, ((number(date(scheduled)) - number(today())) / 86400000).floor(), null)` | +| `daysSinceCreated` | Number of days since the task file was created | `((number(now()) - number(file.ctime)) / 86400000).floor()` | +| `daysSinceModified` | Number of days since the task file was last modified | `((number(now()) - number(file.mtime)) / 86400000).floor()` | ### Boolean formulas -| Formula | Description | -|---------|-------------| -| `isOverdue` | True if task has a past due date and is not completed | -| `isDueToday` | True if task is due today | -| `isDueThisWeek` | True if task is due within the next 7 days | -| `isScheduledToday` | True if task is scheduled for today | -| `isRecurring` | True if task has a recurrence rule | -| `hasTimeEstimate` | True if task has a time estimate > 0 | +| Formula | Description | Expression | +|---------|-------------|------------| +| `isOverdue` | True if task has a past due date and is not completed | `due && date(due) < today() && status != "done"` | +| `isDueToday` | True if task is due today | `due && date(due).date() == today()` | +| `isDueThisWeek` | True if task is due within the next 7 days | `due && date(due) >= today() && date(due) <= today() + "7d"` | +| `isScheduledToday` | True if task is scheduled for today | `scheduled && date(scheduled).date() == today()` | +| `isRecurring` | True if task has a recurrence rule | `recurrence && !recurrence.isEmpty()` | +| `hasTimeEstimate` | True if task has a time estimate > 0 | `timeEstimate && timeEstimate > 0` | ### Time tracking -| Formula | Description | -|---------|-------------| -| `timeRemaining` | Time estimate minus time tracked (in minutes), null if no estimate | -| `efficiencyRatio` | Percentage of estimated time used (>100% = took longer, <100% = faster, null if no estimate) | -| `timeTrackedThisWeek` | Total minutes tracked in the last 7 days | -| `timeTrackedToday` | Total minutes tracked today | +| Formula | Description | Expression | +|---------|-------------|------------| +| `timeRemaining` | Time estimate minus time tracked (in minutes), null if no estimate | `if(timeEstimate && timeEstimate > 0, timeEstimate - if(timeEntries, list(timeEntries).filter(value.endTime).map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0), 0), null)` | +| `efficiencyRatio` | Percentage of estimated time used (>100% = took longer, <100% = faster, null if no estimate) | `if(timeEstimate && timeEstimate > 0 && timeEntries, (list(timeEntries).filter(value.endTime).map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0) / timeEstimate * 100).round(), null)` | +| `timeTrackedThisWeek` | Total minutes tracked in the last 7 days | `if(timeEntries, list(timeEntries).filter(value.endTime && date(value.startTime) >= today() - "7d").map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0).round(), 0)` | +| `timeTrackedToday` | Total minutes tracked today | `if(timeEntries, list(timeEntries).filter(value.endTime && date(value.startTime).date() == today()).map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0).round(), 0)` | ### Grouping formulas These formulas return string values useful for grouping tasks in views: -| Formula | Description | Example values | -|---------|-------------|----------------| -| `dueMonth` | Due date as year-month | "2025-01", "No due date" | -| `dueWeek` | Due date as year-week | "2025-W01", "No due date" | -| `scheduledMonth` | Scheduled date as year-month | "2025-01", "Not scheduled" | -| `scheduledWeek` | Scheduled date as year-week | "2025-W01", "Not scheduled" | -| `dueDateCategory` | Human-readable due date bucket | "Overdue", "Today", "Tomorrow", "This week", "Later", "No due date" | -| `timeEstimateCategory` | Task size by time estimate | "No estimate", "Quick (<30m)", "Medium (30m-2h)", "Long (>2h)" | -| `ageCategory` | Task age bucket | "Today", "This week", "This month", "Older" | -| `createdMonth` | Creation date as year-month | "2025-01" | -| `modifiedMonth` | Last modified date as year-month | "2025-01" | -| `priorityCategory` | Priority as readable label | "High", "Normal", "Low", "No priority" | -| `projectCount` | Number of assigned projects | "No projects", "Single project", "Multiple projects" | -| `contextCount` | Number of assigned contexts | "No contexts", "Single context", "Multiple contexts" | -| `trackingStatus` | Time tracking vs estimate | "No estimate", "Not started", "Under estimate", "Over estimate" | +| Formula | Description | Example values | Expression | +|---------|-------------|----------------|------------| +| `dueMonth` | Due date as year-month | "2025-01", "No due date" | `if(due, date(due).format("YYYY-MM"), "No due date")` | +| `dueWeek` | Due date as year-week | "2025-W01", "No due date" | `if(due, date(due).format("YYYY-[W]WW"), "No due date")` | +| `scheduledMonth` | Scheduled date as year-month | "2025-01", "Not scheduled" | `if(scheduled, date(scheduled).format("YYYY-MM"), "Not scheduled")` | +| `scheduledWeek` | Scheduled date as year-week | "2025-W01", "Not scheduled" | `if(scheduled, date(scheduled).format("YYYY-[W]WW"), "Not scheduled")` | +| `dueDateCategory` | Human-readable due date bucket | "Overdue", "Today", "Tomorrow", "This week", "Later", "No due date" | `if(!due, "No due date", if(date(due) < today(), "Overdue", if(date(due).date() == today(), "Today", if(date(due).date() == today() + "1d", "Tomorrow", if(date(due) <= today() + "7d", "This week", "Later")))))` | +| `timeEstimateCategory` | Task size by time estimate | "No estimate", "Quick (<30m)", "Medium (30m-2h)", "Long (>2h)" | `if(!timeEstimate \|\| timeEstimate == 0, "No estimate", if(timeEstimate < 30, "Quick (<30m)", if(timeEstimate <= 120, "Medium (30m-2h)", "Long (>2h)")))` | +| `ageCategory` | Task age bucket | "Today", "This week", "This month", "Older" | `if(((number(now()) - number(file.ctime)) / 86400000) < 1, "Today", if(((number(now()) - number(file.ctime)) / 86400000) < 7, "This week", if(((number(now()) - number(file.ctime)) / 86400000) < 30, "This month", "Older")))` | +| `createdMonth` | Creation date as year-month | "2025-01" | `file.ctime.format("YYYY-MM")` | +| `modifiedMonth` | Last modified date as year-month | "2025-01" | `file.mtime.format("YYYY-MM")` | +| `priorityCategory` | Priority as readable label | "High", "Normal", "Low", "No priority" | `if(priority=="high","High",if(priority=="normal","Normal",if(priority=="low","Low","No priority")))` | +| `projectCount` | Number of assigned projects | "No projects", "Single project", "Multiple projects" | `if(!projects \|\| list(projects).length == 0, "No projects", if(list(projects).length == 1, "Single project", "Multiple projects"))` | +| `contextCount` | Number of assigned contexts | "No contexts", "Single context", "Multiple contexts" | `if(!contexts \|\| list(contexts).length == 0, "No contexts", if(list(contexts).length == 1, "Single context", "Multiple contexts"))` | +| `trackingStatus` | Time tracking vs estimate | "No estimate", "Not started", "Under estimate", "Over estimate" | `if(!timeEstimate \|\| timeEstimate == 0, "No estimate", if(!timeEntries \|\| list(timeEntries).length == 0, "Not started", if(formula.efficiencyRatio < 100, "Under estimate", "Over estimate")))` | ### Combined due/scheduled formulas These formulas work with either due date or scheduled date, useful for finding the "next action date": -| Formula | Description | Example values | -|---------|-------------|----------------| -| `nextDate` | The earlier of due or scheduled date | Date value or null | -| `daysUntilNext` | Days until next date (due or scheduled, whichever is sooner) | -2, 0, 5, null | -| `hasDate` | True if task has either a due or scheduled date | true, false | -| `isToday` | True if due OR scheduled today | true, false | -| `isThisWeek` | True if due OR scheduled within 7 days | true, false | -| `nextDateCategory` | Human-readable bucket for next date | "Overdue/Past", "Today", "Tomorrow", "This week", "Later", "No date" | -| `nextDateMonth` | Next date as year-month | "2025-01", "No date" | -| `nextDateWeek` | Next date as year-week | "2025-W01", "No date" | +| Formula | Description | Example values | Expression | +|---------|-------------|----------------|------------| +| `nextDate` | The earlier of due or scheduled date | Date value or null | `if(due && scheduled, if(date(due) < date(scheduled), due, scheduled), if(due, due, scheduled))` | +| `daysUntilNext` | Days until next date (due or scheduled, whichever is sooner) | -2, 0, 5, null | `if(due && scheduled, min(formula.daysUntilDue, formula.daysUntilScheduled), if(due, formula.daysUntilDue, formula.daysUntilScheduled))` | +| `hasDate` | True if task has either a due or scheduled date | true, false | `due \|\| scheduled` | +| `isToday` | True if due OR scheduled today | true, false | `(due && date(due).date() == today()) \|\| (scheduled && date(scheduled).date() == today())` | +| `isThisWeek` | True if due OR scheduled within 7 days | true, false | `(due && date(due) >= today() && date(due) <= today() + "7d") \|\| (scheduled && date(scheduled) >= today() && date(scheduled) <= today() + "7d")` | +| `nextDateCategory` | Human-readable bucket for next date | "Overdue/Past", "Today", "Tomorrow", "This week", "Later", "No date" | `if(!due && !scheduled, "No date", if((due && date(due) < today()) \|\| (scheduled && date(scheduled) < today()), "Overdue/Past", if((due && date(due).date() == today()) \|\| (scheduled && date(scheduled).date() == today()), "Today", if((due && date(due).date() == today() + "1d") \|\| (scheduled && date(scheduled).date() == today() + "1d"), "Tomorrow", if((due && date(due) <= today() + "7d") \|\| (scheduled && date(scheduled) <= today() + "7d"), "This week", "Later")))))` | +| `nextDateMonth` | Next date as year-month | "2025-01", "No date" | `if(due && scheduled, if(date(due) < date(scheduled), date(due).format("YYYY-MM"), date(scheduled).format("YYYY-MM")), if(due, date(due).format("YYYY-MM"), if(scheduled, date(scheduled).format("YYYY-MM"), "No date")))` | +| `nextDateWeek` | Next date as year-week | "2025-W01", "No date" | `if(due && scheduled, if(date(due) < date(scheduled), date(due).format("YYYY-[W]WW"), date(scheduled).format("YYYY-[W]WW")), if(due, date(due).format("YYYY-[W]WW"), if(scheduled, date(scheduled).format("YYYY-[W]WW"), "No date")))` | ### Sorting -| Formula | Description | -|---------|-------------| -| `priorityWeight` | Numeric weight for priority sorting (lower = higher priority) | -| `urgencyScore` | Combines priority and next date proximity (due or scheduled, higher = more urgent) | +| Formula | Description | Expression | +|---------|-------------|------------| +| `priorityWeight` | Numeric weight for priority sorting (lower = higher priority) | `if(priority=="none",0,if(priority=="low",1,if(priority=="normal",2,if(priority=="high",3,999))))` | +| `urgencyScore` | Combines priority and next date proximity (due or scheduled, higher = more urgent) | `if(!due && !scheduled, formula.priorityWeight, formula.priorityWeight + max(0, 10 - formula.daysUntilNext))` | ### Display formulas -| Formula | Description | Example values | -|---------|-------------|----------------| -| `timeTrackedFormatted` | Total time tracked as readable text | "2h 30m", "45m", "0m" | -| `dueDateDisplay` | Due date as relative text | "Today", "Tomorrow", "Yesterday", "3d ago", "Mon", "Dec 15" | +| Formula | Description | Example values | Expression | +|---------|-------------|----------------|------------| +| `timeTrackedFormatted` | Total time tracked as readable text | "2h 30m", "45m", "0m" | `if(timeEntries, if(list(timeEntries).filter(value.endTime).map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0) >= 60, (list(timeEntries).filter(value.endTime).map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0) / 60).floor() + "h " + (list(timeEntries).filter(value.endTime).map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0) % 60).round() + "m", list(timeEntries).filter(value.endTime).map((number(date(value.endTime)) - number(date(value.startTime))) / 60000).reduce(acc + value, 0).round() + "m"), "0m")` | +| `dueDateDisplay` | Due date as relative text | "Today", "Tomorrow", "Yesterday", "3d ago", "Mon", "Dec 15" | `if(!due, "", if(date(due).date() == today(), "Today", if(date(due).date() == today() + "1d", "Tomorrow", if(date(due).date() == today() - "1d", "Yesterday", if(date(due) < today(), formula.daysUntilDue * -1 + "d ago", if(date(due) <= today() + "7d", date(due).format("ddd"), date(due).format("MMM D")))))))` | ## Mini Calendar