My first week with Obsidian, and I built a Habit Tracker
Tags: Habits, Obsidian
I’ve been a long-time subscriber of Evernote, 15+ years, with hundreds of notebooks, thousands of notes. When they went off the deep end and increased their subscription pricing by 217%, I ended my long history with them.
I moved all of my Evernote notes and notebooks into Joplin, and used that for the last couple of years as a generalized, hierarchical capture system. Many of my friends in the productivity space recommended I consider Obsidian, but until very recently it wasn’t free, and carried a $50/year subscription fee (yes, for everyone who used it for more than just personal use). It was, and continues to be, commercial software.
I had no problems with Joplin, but it was ‘plain’, and wasn’t very extensible in the rich ways I needed to link my notes together, much like I use Xmind for mind mapping.
So I jumped over to Obsidian, and imported all of my Joplin notes. Well, “imported” is the wrong word here, since it was just a tree of Markdown files, I exported from Joplin and moved that tree into the Obsidian data directory.
The next challenge was finding the right plugins to extend the capability of Obsidian to help me with my day-to-day workflow. I found a dozen useful plugins that I’m now using, including the Kikijiki Habit Tracker plugin I’m using for the habit tracker I built tonight. There are plugins for JIRA, Github, Amazon Kindle book highlights, Calendar, Kanban, Mermaid Charts, Tasks and many more.
I started using Dataview and Templater to build a custom Daily Journal page, with some dynamic elements, and soon realized the power of Dataview, so had a play with its visualizations, and built this little Habit Progress tracker around Kikijiki’s plugin:

The resulting rendering of this progress tracker looks like this:

The code is quite simple, just add this to your page of choice:
``` dataviewjs
const folder = "Daily Journal";
const today = new Date();
const daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate();
const notes = [...dv.pages(`"${folder}"`)]
.filter(p => p.file.tags && p.file.tags.some(t => t.startsWith("#habit/")));
const habits = {};
notes.forEach(p => {
p.file.tags
.filter(t => t.startsWith("#habit/"))
.forEach(tag => {
habits[tag] = (habits[tag] || 0) + 1;
});
});
const habitKeys = Object.keys(habits);
const totalPossible = daysInMonth * habitKeys.length;
const totalCompleted = Object.values(habits).reduce((sum, count) => sum + count, 0);
const overallPercent = totalPossible > 0 ?
((totalCompleted / totalPossible) * 100).toFixed(1) : "0.0";
dv.paragraph(`**? Overall Completion:**
${totalCompleted}/${totalPossible} (${overallPercent}%)`);
const table = habitKeys.map(tag => {
const count = habits[tag];
const percent = ((count / daysInMonth) * 100).toFixed(1);
const barCount = Math.round(percent / 10);
const bar = "?".repeat(barCount) + "?".repeat(10 - barCount);
return [tag.replace("#habit/", ""),
`${count}/${daysInMonth}`, `${percent}%`, bar];
});
dv.table(["Habit", "Days Completed", "Completion %", "Progress"], table);
```
That’s it!
It will build a dynamic inventory of your defined habits, using the ‘#habit/Thing’ structure provided by Kikijiki as tags on each day’s Daily Journal page. My pages are titled with the format ‘YYYY-MM-DD’, and it parses those inside the ‘Daily Journal’ folder. You may need to alter the code if your pages are in a different place or have a different name.
Suggestions, comments, fixes or anything else you think would improve this are welcome!