Last active
March 5, 2025 03:10
-
-
Save erichschroeter/81252c89af1aa8914d5c3ccccef5324e to your computer and use it in GitHub Desktop.
Obsidian script and usage using Dataview community plugin to list all tasks between a date range. Thanks to https://www.youtube.com/watch?v=8MyHCQsyN1k
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function aggregateTimes(...timeStrings) { | |
const timeUnits = { h: 3600, m: 60, s: 1 }; | |
let totalSeconds = 0; | |
for (const timeString of timeStrings) { | |
if (typeof timeString === 'string' || timeString instanceof String) { | |
const matches = timeString.match(/(\d+)([hms])/g); | |
if (matches) { | |
for (const match of matches) { | |
const [, value, unit] = match.match(/(\d+)([hms])/); | |
totalSeconds += parseInt(value, 10) * timeUnits[unit]; | |
} | |
} | |
} | |
} | |
const hours = Math.floor(totalSeconds / 3600); | |
const minutes = Math.floor((totalSeconds % 3600) / 60); | |
const seconds = totalSeconds % 60; | |
let result = ""; | |
if (hours) result += `${hours}h `; | |
if (minutes) result += `${minutes}m `; | |
if (seconds && !hours) result += `${seconds}s`; | |
return result.trim() || "0s"; | |
} | |
function summary(...args) { | |
// Define your date range (inclusive) | |
var startDateRange = new Date("1900-01-01"); // Adjust the start date | |
var endDateRange = new Date("9999-01-31"); // Adjust the end date | |
if ("startDate" in args[0]) { | |
startDateRange = new Date(args[0]["startDate"]); | |
} | |
if ("endDate" in args[0]) { | |
endDateRange = new Date(args[0]["endDate"]); | |
} | |
// Define tags to filter (set to an empty array to retrieve all tags) | |
const userSpecifiedTags = ["#work"]; // Set tags here (e.g., ["#work", "#hiring-meetings"]), or leave empty for all tags | |
// console.log("Filtering tags " + userSpecifiedTags + " between " + startDateRange.toISOString().split("T")[0] + " to " + endDateRange.toISOString().split("T")[0]); | |
// Collect tasks with valid metadata within the date range | |
const tasks = []; | |
for (const page of dv.pages('"Journals"')) { | |
if (page.file.tasks) { | |
for (const task of page.file.tasks) { | |
// e.g. - [.] watched a youtube tutorial #work [timeTracked:: 7m 36s] | |
const timeTrackedMatch = task.text.match(/\[timeTracked::\s*((\d+)([hms])\s*)*\]/); | |
const tagMatches = task.text.match(/#[\w-]+/gi); // Match multiple hashtags, case-insensitive | |
if (timeTrackedMatch && tagMatches) { | |
try { | |
// Get the note's creation date using `file.ctime` | |
const noteDate = new Date(page.file.ctime); | |
// Check if the note's creation date falls within the specified range | |
if (noteDate >= startDateRange && noteDate <= endDateRange) { | |
// Add a record for each hashtag that matches the filter | |
for (const tag of tagMatches) { | |
if (userSpecifiedTags.length === 0 || userSpecifiedTags.includes(tag)) { | |
var timeTracked = timeTrackedMatch[0]; | |
tasks.push({ tag, timeTracked }); | |
} | |
} | |
} | |
} catch (e) { | |
dv.span(`Error parsing dates for task: ${task.text}\n`); | |
} | |
} | |
} | |
} | |
} | |
// Group by tag and sum durations | |
const grouped = tasks.reduce((acc, task) => { | |
acc[task.tag] = aggregateTimes((acc[task.tag] || 0), task.timeTracked); | |
return acc; | |
}, {}); | |
// Render results | |
if (Object.keys(grouped).length > 0) { | |
dv.table(["Activity", "Total Time"], | |
Object.entries(grouped).map(([tag, totalTime]) => [tag, totalTime])); | |
} else { | |
dv.span(`No valid tasks with durations to display for the date range ${startDateRange.toISOString().split("T")[0]} to ${endDateRange.toISOString().split("T")[0]}.\n`); | |
} | |
} | |
summary(input) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function fromToday(offset) { | |
const date = new Date(); | |
const dateRegex = /(-?\d+)([d])/; | |
if (offset && dateRegex.test(offset)) { | |
const [, value, unit] = offset.match(dateRegex); | |
if (unit === "d") { | |
date.setDate(date.getDate() + parseInt(value, 10)); | |
} | |
} | |
// Convert to CST (Central Standard Time) | |
const options = { timeZone: "America/Chicago", year: "numeric", month: "2-digit", day: "2-digit" }; | |
const cstDate = new Intl.DateTimeFormat("en-CA", options).format(date); | |
return cstDate.replace(/\//g, "-"); | |
} | |
//await dv.view("scripts/aggregateTasks", {"startDate": "2025-03-04", "endDate": "2025-03-05"}); | |
await dv.view("scripts/aggregateTasks", {"startDate": fromToday("-7d"), "endDate": fromToday("1d")}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment