Automatic Gantt Chart from Obsidian Tasks & Dataview

You’re most welcome, and credit should rather go to @LynnXie00 who did all the hard part at the top of this thread :slight_smile:

As for showDone, you guessed it, it’s a property in my note, that is linked to a switch in the note’s body thanks to the Meta-Bind plugin.

Hi, everyone and thank you for your effort. I tried to follow your guidelines, but unfortunately, it doesn’t work for me.

Below performed steps:

  • Installed CustomJS. Dataview and templater already installed
  • Made a Scripts Folder and placed dailyGantt.js inside. Configured CustomJS to look into that folder to find scripts
  • In my Daily note i placed the dataviewjs query to invoke the script

return this:

Is that a file you downloaded, or did you copy/paste the code from this thread into dailyGantt.js?

The error seems to be some kind of syntax error. My first guess would be if you copy/pasted but ended up with smart quotes instead of regular quotes, or something similar.

I think it would be helpful to see the code in that file. The best thing to do would be to copy/paste it here between backticks.

```
Your dailyGantt.js code
```

Hi @rigmarole thank you for your answer. Probably my bad, having copied/pasted the code from this thread directly into the file.

Below the script:

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, '')
                    .replace(/\(http[^\)]+\)/g, '')
                    .replace(/\[\[[^\|]+\|/g, '')
                    .replace(/\]\]/g, '');
                let startDate = theTask.start || theTask.scheduled || theTask.add || noteCreationDate || today;

                if (startDate >= monthLater) continue;
                // ligne suivante cache les tâches qui n'ont aucune date, uncomment -> la date de création de la tâche ou de la note est employée par défaut

                if (!theTask.start && !theTask.scheduled && !theTask.due) continue;

                let endDate = theTask.done || theTask.due || theTask.scheduled;
                if (!endDate) {
                    if (startDate >= today) {  // If start date is in the future
                        let weekLater = new Date(startDate);
                        weekLater.setDate(weekLater.getDate() + 7);
                        endDate = weekLater.toISOString().slice(0, 10);
                    } else {  // If start date is in the past
                        endDate = tomorrow;
                    }
                }

                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("#someday") &&
                !t.text.includes("waitingFor")
            );
            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]\``);
    }

}

As you noticed the error seems to be some kind of syntax error, according to attached preview.

Thanks a lot

So I think (but I’m not familiar with what this code is doing) that you didn’t put it inside code blocks and say what the language is:

```dataviewjs

The code goes here

```

If you copy the triple backticks here, the forum doesn’t show them, and so people copying the code miss that part. I’m showing them by putting them surrounded by 4 backticks. And I’ll show that by using 5 backticks.

````
```dataviewjs

Code goes here

```
````
1 Like

I am close to fix the problem, i pasted the script into Visual studio code saved as .js file and dropped into my vault, now it works.

Regarding the dataview code to trigger Mermaid inside my Daily note:

```dataviewjs
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();
```

I receive this error

Modifying the dates in the beginning of the short script seems to work:

let today = "2024-01-30";
let tomorrow = "2024-01-31";

I am still a newbie and i am trying to deepen the problem, any suggestion appreciated

I have update, according to my needs, the two code blocks as shown below, now it works:

DailyGantt.js

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);

                if (monthLater instanceof Date && !isNaN(monthLater)) {
                    monthLater = monthLater.toISOString().slice(0, 10);
                } else {
                    console.error("Invalid monthLater:", monthLater);
                    continue;
                }

                if (theTask.name === "") continue;
                if (!showDone && theTask.done) continue;

                let taskName = theTask.name
                    .replace(/:/g, '')
                    .replace(/\(http[^\)]+\)/g, '')
                    .replace(/\[\[[^\|]+\|/g, '')
                    .replace(/\]\]/g, '');
                let startDate = theTask.start || theTask.scheduled || theTask.add || noteCreationDate || today;

                if (startDate >= monthLater) continue;

                if (!theTask.start && !theTask.scheduled && !theTask.due) continue;

                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;
                    }
                }

                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("#someday") &&
                !t.text.includes("waitingFor")
            );
            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]\``);
    }

}

Dataview snippet for daily note

```dataviewjs
let today = new Date().toISOString().slice(0, 10); 
let tomorrow = new Date(); 
tomorrow.setDate(tomorrow.getDate() + 1); 
tomorrow = tomorrow.toISOString().slice(0, 10);
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();
```

I hope it can be of help to others too

Need help because your script doesn’t work for the selected dates. For duration it works well. I’m including a screenshot.

axis_format:: %d-%m

```dataviewjs
function textParser(taskText){//input text,return object

let du= taskText.indexOf("⏱️")
let durText = "";
if (du>0){
let i=taskText.slice(du).search(/\d+(d|w|m)/);
	durText=taskText.substr(du+i, 3)

}
 
let miletext = taskText.indexOf("🚩") > -1 ? 1 : 0;

let d = taskText.indexOf("📅");
let DueText="";
if(d>=0){
let i=taskText.slice(d).search(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
	DueText=taskText.substr(d+i,10);
} 







    let sch = taskText.indexOf("⏳");
    let scheduledText="";
    if(sch>0){
        let i=taskText.slice(sch).search(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
        scheduledText=taskText.substr(sch+i,10);
    } 

    let st = taskText.indexOf("🛫");
    let startText="";
    if(st>0){
        let i=taskText.slice(st).search(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
        startText=taskText.substr(st+i,10);
    } 
    
    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= [d,sch,st,h,m,l,du]
    const presentEmojiIndex= emojisIndex.filter(x => x > 0);

    let nameText=taskText.slice(0,Math.min(...presentEmojiIndex)).trim();


    //console.log(taskText,Math.min(...presentEmojiIndex))
	return {    
		name: nameText,
		due: DueText,
        start:startText,
        scheduled:scheduledText,
        priority:PriorityText,
        duration:durText,
	 miletext: miletext
	}
}

function loopGantt (pageArray){
	





	let querySections=``;
	let today = new Date().toISOString().slice(0, 10)
	for (let i = 0; i < pageArray.length; i++) {  
		let taskQuery=``;
		var taskArray = pageArray[i].file.tasks;





//parse name, due, start, completion, scheduled,priority from task text to objects
		var compObjs = pageArray[i].file.tasks.completed
		var completionArray = [];
		for (let s=0; s< compObjs.length;s++){		
			completionArray[s] =compObjs[s]}



		var taskObjs=[];
		for (let j=0; j< taskArray.length;j++){		
			taskObjs[j] = textParser(taskArray[j].text)

		}

 

        //determine the mermaid task parameters
		for (let j=0; j< taskObjs.length;j++){		
			let theTask = taskObjs[j];
  



				// create stats variable 

function getLastLesserIndex(arr, currentIndex) {
  let currentValue = arr[currentIndex];
  for (let i = currentIndex - 1; i >= 0; i--) {
    if (arr[i] < currentValue) {
      return i;
    }
  }
  return -1;
}

let SecNum = "sect" + (i+1)+ "-"


let taskNum= "task" + (j+1) +", "

			let Ind = pageArray[i].file.tasks.position.start.col
			



let IndUp = getLastLesserIndex(Ind,j) 


let aft = ""
if ((taskArray[j].parent != null) && (IndUp > 0)){aft+= "after "+ SecNum+ "task"+(IndUp+1)} else if(taskArray[j].parent != null) {aft+= "after "+ SecNum+ "task"+(j)}  else {aft +="" }



var stats = ""

if (completionArray[j] == true ){
stats += "done, "
} else { stats+= "active, "}
	
// test stuff
var critStat = ""

if (theTask.priority === "High" ) {critStat = "crit"+ ", "} else { critStat = ""}

var start = ""
if (taskArray[j].parent != null) {start += aft +","} else{start+= theTask.start+","}


var end = ""
if(taskArray[j].duration = null) {end += theTask.duration} 
	else {end += theTask.due}

let mile = ""
if (taskObjs[j].miletext>0) {mile+= "milestone, "}


//

					taskQuery+= theTask.name + `    : `  +mile+ critStat+ stats+ SecNum+taskNum+ start+ end+ theTask.duration+`\n\n`;		
					
						
				};
		
		
		
		
		querySections+= `section  `+pageArray[i].file.name+`\n\n`+taskQuery;
		
	};
	return querySections
}


const Mermaid = `gantt

    title Gannt Charts (v0.4)
    
   
    

axisFormat  `+ dv.current().axis_format +


  ` \n `;



// set the path of your project folder below
dv.paragraph('```mermaid\n' + Mermaid + loopGantt(dv.pages('#gannt'))+ '\n```');




// render gannt page with checkboxes

//digonstic rendiering. uncomment to get a render of the merimaid text and otehr diagnostic stuff

//dv.paragraph('```' + Mermaid + loopGantt(dv.pages('#gannt'))+ '\n ```');

So sorry for the delay, I really need to log in more, lol.

Not entierly sure why that is happinging. But it works fine on my current version of the script.

the update take more inputs from feilds on the page with the gannt chart to allow for adjsuting the Axis ticks based on the format descrbed here.

in the example below, “tick amount” holds the number and “tickScale” holds the day/week/month parts of the pattern Described in the link. along the way i must have halso fixed whatever was causing your issue

I hope this helps!


Gannt config
axis_format:: %m-%d-%y
axis tick
tickAmount:: 6
tickScale:: month

Gannt render

```dataviewjs
function textParser(taskText){//input text,return object

let du= taskText.indexOf("⏱️")
let durText = "";
if (du>0){
let i=taskText.slice(du).search(/\d+(d|w|m)/);
	durText=taskText.substr(du+i, 3)

}
 
let miletext = taskText.indexOf("🚩") > -1 ? 1 : 0;
 
 



    let d = taskText.indexOf("📅");
    let DueText="";
    if(d>=0){
        let i=taskText.slice(d).search(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
        DueText=taskText.substr(d+i,10);
    } 







    let sch = taskText.indexOf("⏳");
    let scheduledText="";
    if(sch>0){
        let i=taskText.slice(sch).search(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
        scheduledText=taskText.substr(sch+i,10);
    } 

    let st = taskText.indexOf("🛫");
    let startText="";
    if(st>0){
        let i=taskText.slice(st).search(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/);
        startText=taskText.substr(st+i,10);
    } 
    
    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= [d,sch,st,h,m,l,du]
    const presentEmojiIndex= emojisIndex.filter(x => x > 0);

    let nameText=taskText.slice(0,Math.min(...presentEmojiIndex)).trim();


    //console.log(taskText,Math.min(...presentEmojiIndex))
	return {    
		name: nameText,
		due: DueText,
        start:startText,
        scheduled:scheduledText,
        priority:PriorityText,
        duration:durText,
	 miletext: miletext
	}
}

function loopGantt (pageArray){
	





	let querySections=``;
	let today = new Date().toISOString().slice(0, 10)
	for (let i = 0; i < pageArray.length; i++) {  
		let taskQuery=``;
		var taskArray = pageArray[i].file.tasks;





//parse name, due, start, completion, scheduled,priority from task text to objects
		var compObjs = pageArray[i].file.tasks.completed
		var completionArray = [];
		for (let s=0; s< compObjs.length;s++){		
			completionArray[s] =compObjs[s]}



		var taskObjs=[];
		for (let j=0; j< taskArray.length;j++){		
			taskObjs[j] = textParser(taskArray[j].text)

		}

 

        //determine the mermaid task parameters
		for (let j=0; j< taskObjs.length;j++){		
			let theTask = taskObjs[j];
  



				// create stats variable 

function getLastLesserIndex(arr, currentIndex) {
  let currentValue = arr[currentIndex];
  for (let i = currentIndex - 1; i >= 0; i--) {
    if (arr[i] < currentValue) {
      return i;
    }
  }
  return -1;
}

let SecNum = "sect" + (i+1)+ "-"


let taskNum= "task" + (j+1) +", "

			let Ind = pageArray[i].file.tasks.position.start.col
			



let IndUp = getLastLesserIndex(Ind,j) 


let aft = ""
if ((taskArray[j].parent != null) && (IndUp > 0)){aft+= "after "+ SecNum+ "task"+(IndUp+1)} else if(taskArray[j].parent != null) {aft+= "after "+ SecNum+ "task"+(j)}  else {aft +="" }



var stats = ""

if (completionArray[j] == true ){
stats += "done, "
} else { stats+= "active, "}
	
// test stuff
var critStat = ""

if (theTask.priority === "High" ) {critStat = "crit"+ ", "} else { critStat = ""}

var start = ""
if (taskArray[j].parent != null) {start += aft +","} else{start+= theTask.start+","}


var end = ""
if(taskArray[j].duration = null) {end += theTask.duration} 
	else {end += theTask.due}

let mile = ""
if (taskObjs[j].miletext>0) {mile+= "milestone, "}


//

					taskQuery+= theTask.name + `    : `  +mile+ critStat+ stats+ SecNum+taskNum+ start+ end+ theTask.duration+`\n\n`;		
					
						
				};
		
		
		
		
		querySections+= `section  `+pageArray[i].file.name+`\n\n`+taskQuery;
		
	};
	return querySections
}

let tick1 = dv.current().tickAmount // define first part of tick interval (number) 
let tick2 = dv.current().tickScale // define the scale of axis tick (day, week, month, year) 

const Mermaid = `gantt

    title Gannt Charts (v0.5.5)
    
   
    
 \n ` + `dateFormat YYYY-MM-DD` + ` \n ` +
`axisFormat  `+ dv.current().axis_format + ` \n ` + `tickInterval `  + tick1 + tick2+


  `\n `;



// set the path of your project folder below
dv.paragraph('```mermaid\n' + Mermaid + loopGantt(dv.pages('#gannt and [[]]'))+ '\n```');




// render gannt page with checkboxes

//digonstic rendiering. uncomment to get a render of the merimaid text and otehr diagnostic stuff

//dv.paragraph('```' + Mermaid + loopGantt(dv.pages('#gannt and [[]]'))+ '\n ```');


1 Like

Hello there.
I tried to follow along, since I would also really like to have a GANTT for my tasks. However, I seem to be incapable.
I installed CustomJS and Dataview. But I only get the same red line as @Brains4Us did on Jan 21.
Maybe at some point somebody can provide a short tutorial?
regards

1 Like

I look forward to giving this a shot. Thank you for this. :slight_smile:

2 Likes

Hello,

By my side, i found that this plugin is the best solution to aim to an automatic gantt generator:

Hope in the future it will be released in the plugins store a definitive version.

Regards

hi! i’m novice. How to use this md files? I’ve searched on youtube and didnt found how to use them

how to view on the graph subtasks in one line one after previous if task have not date, but have duration in minutes, hours, days?

what i have did wrong?

I somehow started chasing this rabbithole and after trying a few solutions, combining what i could glean as a logic with chatgpt interactions and help i have got this working here.

Hope this helps

Man thanks for the code snippet.

I made some adjustments and since the tasks plugin is working with subtasks I added this functionality.

// Priority mapping based on emojis
const priorityMap = {
    "⏬": "Lowest",
    "🔽": "Low",
    "⏺️": "Medium",
    "⏫": "High",
    "🔺": "Highest"
};

// Function to determine priority based on emoji in task text
const getPriority = (text) => {
    for (let emoji in priorityMap) {
        if (text.includes(emoji)) {
            return priorityMap[emoji];
        }
    }
    return "Medium"; // Default to Medium if no emoji is found
};

// Function to count completed and total subtasks
const countSubtasks = (subtasks) => {
    const completed = subtasks.filter(st => st.completed).length;
    return { completed, total: subtasks.length };
};

// Filter tasks
const tasks = dv.pages("").file.tasks.where(t => t.start && t.due && !t.completed);

if (tasks.length > 0) {
    let ganttData = `
\`\`\`mermaid
gantt
    title Tasks with Start and Due Dates
    dateFormat YYYY-MM-DD
    axisFormat %d-%b
    `;
    
    // Group tasks by priority
    const priorityGroups = { "Lowest": [], "Low": [], "Medium": [], "High": [], "Highest": [] };
    tasks.forEach(task => {
        const priority = getPriority(task.text);
        
        // Sanitize task text
        let sanitizedTaskText = task.text
            .replace(/#[\w-]+/g, "") // Remove hashtags
            .replace(/(?:\p{Emoji_Presentation}|[\u2600-\u27BF])\s*\d{4}-\d{2}-\d{2}/gu, "") // Remove dates with emojis
            .replace(/[\n\r]+/g, " ") // Remove newlines
            .trim(); // Trim leading/trailing spaces

		for (let emoji in priorityMap) { sanitizedTaskText = sanitizedTaskText.replace(emoji, "").trim(); }

        const startDate = task.start.toString().split("T")[0]; // Extract YYYY-MM-DD
        const dueDate = task.due.toString().split("T")[0]; // Extract YYYY-MM-DD

        // Find subtasks of the current task
        const subtasks = task.subtasks || [];
        const { completed, total } = countSubtasks(subtasks);
        const progressMarker = total > 0 ? ` [${completed}/${total}]` : "";

        // Add main task
        if (sanitizedTaskText) {
            priorityGroups[priority].push(`    ${sanitizedTaskText}${progressMarker} :active, ${startDate}, ${dueDate}`);
        }

        // Add subtasks as dependencies
        subtasks.forEach((subtask) => {
            const sanitizedSubtaskText = subtask.text
                .replace(/#[\w-]+/g, "")
                .replace(/[\n\r]+/g, " ")
                .trim();

            if (sanitizedSubtaskText) {
                const subStartDate = subtask.start ? subtask.start.toString().split("T")[0] : startDate;
                const subDueDate = subtask.due ? subtask.due.toString().split("T")[0] : dueDate;

                priorityGroups[priority].push(`        ${sanitizedSubtaskText} : ${startDate}, ${subDueDate}`);
            }
        });
    });

    // Add tasks to Gantt chart by priority
    for (const priority in priorityGroups) {
        if (priorityGroups[priority].length > 0) {
            ganttData += `\nsection ${priority}\n`;
            ganttData += priorityGroups[priority].join("\n");
        }
    }

    ganttData += `\n\`\`\``;

    // Debug mode: Show raw text
	dv.el("pre", ganttData); 
} else {
    dv.paragraph("No tasks found with Gantt chart marker and with start and due dates. Mark text with #Gantt and ensure both start and due dates are marked.");
}

Maybe I’ll do a pull request but this really helps me have an overview.

Thanks again and all the best