Hey there @boomshakalaka
Since the previous posts on this subject, I made this into a CustomJS user script. I find it more convenient. I call the script from within my daily note template like so:
let today = "{{date:YYYY-MM-DD}}";
let tomorrow = "{{date+1d:YYYY-MM-DD}}";
let showDone = dv.current().showDone;
function checkAndExecuteGantt() {
const { dailyGantt } = customJS || {};
if (dailyGantt && dailyGantt.drawGantt) {
let parsedToday = new Date(today);
if (parsedToday.getDay() != 0) {
dailyGantt.drawGantt(dv, today, tomorrow, moment, showDone);
}
} else {
setTimeout(checkAndExecuteGantt, 500);
}
}
checkAndExecuteGantt();
(This works with DataviewJS, Templater, the CustomJS plugin and a dailyGantt.js file in that plugin’s scripts directory)
Here is what the script looks like:
class dailyGantt{
extractDate(emoji, taskText) {
const start = taskText.indexOf(emoji);
if (start < 0) {
return "";
}
const match = taskText.slice(start + 1).match(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))(\s|$)/);
return match ? match[0] : "";
}
textParser(taskText, noteCreationDate) {
const emojis = ["📅", "⏳", "🛫", "➕", "✅", "⏫", "🔼", "🔽"];
const DueText = this.extractDate("📅", taskText);
const scheduledText = this.extractDate("⏳", taskText);
const startText = this.extractDate("🛫", taskText);
let addText = this.extractDate("➕", taskText);
const doneText = this.extractDate("✅", taskText);
if (addText === "") {
addText = noteCreationDate;
}
let h = taskText.indexOf("⏫");
let m = taskText.indexOf("🔼");
let l = taskText.indexOf("🔽");
let PriorityText="";
if(h>=0){
PriorityText="High";
}
if(m>=0){
PriorityText="Medium";
}
if(l>=0){
PriorityText="Low";
}
const emojisIndex = emojis.map(emoji => taskText.indexOf(emoji)).filter(index => index >= 0);
let words;
if (emojisIndex.length > 0) {
words = taskText.slice(0, Math.min(...emojisIndex)).split(" ");
} else {
words = taskText.split(" ");
}
words = words.filter((word) => (word) !== "#task");
let newWords = words.map(
(word) => word.startsWith("#") ? `{${word.slice(1)}}` : word);
let nameText = newWords.join(" ");
return {
add: addText,
done: doneText,
due: DueText,
name: nameText,
priority: PriorityText,
scheduled: scheduledText,
start: startText
};
}
loopGantt (args){
const { pageArray, showDone, today, tomorrow, moment } = args;
let queryGlobal = "";
let i;
for (i = 0; i < pageArray.length; i+=1) {
let taskQuery = "";
if (!pageArray[i].file.tasks || pageArray[i].file.tasks.length === 0) {
continue;
}
let taskArray = pageArray[i].file.tasks;
let taskObjs = [];
let noteCreationDate = moment(pageArray[i].file.cday).format('YYYY-MM-DD');
let j;
for (j = 0; j < taskArray.length; j+=1){
taskObjs[j] = this.textParser(taskArray[j].text, noteCreationDate);
let theTask = taskObjs[j];
let monthLater = new Date(today);
monthLater.setDate(monthLater.getDate() + 30);
monthLater = monthLater.toISOString().slice(0, 10);
if (theTask.name === "") continue;
if (!showDone && theTask.done) continue;
let taskName = theTask.name
.replace(/:/g, '') // Removes colons
.replace(/\(http[^\)]+\)/g, '') // Removes anything that starts with "(http" and ends with ")"
.replace(/\(file[^\)]+\)/g, '') // Removes anything that starts with "(http" and ends with ")"
.replace(/\(obsidian[^\)]+\)/g, '') // Removes anything that starts with "(http" and ends with ")"
.replace(/\[\[[^\|]+\|/g, '') // Removes wiki link aliases "[[...|"
.replace(/\]\]/g, '') // Removes "]]"
.replace(/\[|\]/g, '') // Removes "[" and "]"
.replace(/\(file[^\)]+\)/g, ''); // Removes anything that starts with "(file" and ends with ")"
let startDate = theTask.start || theTask.scheduled || theTask.add || noteCreationDate || today;
let endDate = theTask.done || theTask.due || theTask.scheduled;
if (!endDate) {
if (startDate >= today) {
let weekLater = new Date(startDate);
weekLater.setDate(weekLater.getDate() + 7);
endDate = weekLater.toISOString().slice(0, 10);
} else {
endDate = tomorrow;
}
}
// Handling tasks with only a due date and no start date
if (!theTask.start && !theTask.scheduled && theTask.due) {
let weekBefore = new Date(theTask.due);
weekBefore.setDate(weekBefore.getDate() - 7);
startDate = weekBefore.toISOString().slice(0, 10);
}
if (endDate == startDate) {
let weekLater = new Date(startDate);
weekLater.setDate(weekLater.getDate() + 7);
endDate = weekLater.toISOString().slice(0, 10);
}
// mise des tâches au format mermaid
if (theTask.due){
if (theTask.due < today){
taskQuery += taskName + ` :crit, ` + startDate + `, ` + endDate + `\n\n`;
} else {
taskQuery += taskName + ` :active, ` + startDate + `, ` + endDate + `\n\n`;
}
} else if (theTask.scheduled){
if (startDate >= today){
taskQuery += taskName + ` :active, ` + startDate + `, ` + endDate + `\n\n`;
} else {
taskQuery += taskName + ` :inactive, ` + startDate + `, ` + endDate + `\n\n`;
}
} else {
taskQuery += taskName + ` :active, ` + startDate + `, ` + endDate + `\n\n`;
}
}
queryGlobal += taskQuery;
}
return queryGlobal;
}
drawGantt(dv, today, tomorrow, moment, showDone) {
const Mermaid = `gantt
dateFormat YYYY-MM-DD
axisFormat %b\n %d
`;
// Get all dashboard pages with status "en cours" (ongoing)
let dashboardPages = dv.pages().where(p => (p.status == "en cours" || p.status == "permanent") && p.type == "dashboard");
// Extract tagNames from these dashboard pages
let tagNames = dashboardPages.map(page => page.tagName);
// Get all pages that contain tasks with the extracted tagNames and #task
let filteredPages = [];
for (let page of dv.pages()) {
let tasks = page.file.tasks.filter(t =>
t.status != "-" &&
t.text.includes("#task") &&
tagNames.some(tag => t.text.includes(tag)) &&
!t.text.includes("#someday") &&
!t.text.includes("#waitingFor") &&
!t.text.includes("Revue hebdomadaire")
);
if (tasks.length > 0) {
filteredPages.push({ ...page, file: { ...page.file, tasks: tasks } });
}
}
let ganttOutput = this.loopGantt({pageArray:filteredPages, showDone, today, tomorrow, moment});
ganttOutput += "🧘🏻♂️ :active, " + today + ", " + today + "\n\n"; // (dummy task to display today's date)
dv.paragraph("```mermaid\n" +
Mermaid +
ganttOutput +
"\n```");
// this prints a meta-bind inline field allowing the user to toggle a boolean in the metadata and thus show/hide completed tasks:
//dv.paragraph(`Montrer les tâches terminées \`INPUT[toggle:showDone]\``);
}
}
edit: a few corrections brought to the script a few months later
Let me know if you could adapt it to your use case.
(edit: I realized that I copy-pasted my code with some lines that are very specific to my use case, for example the nested if statement in the checkAndExecuteGantt function checks that it’s not Sunday before display the Gantt, you probably don’t need this)