obsidian-habit-mood-tracker icon indicating copy to clipboard operation
obsidian-habit-mood-tracker copied to clipboard

Code snippet for automatically handling multiple habits

Open scorchio opened this issue 2 years ago • 0 comments

Hi,

Thanks for this repo! It's a nice example which encouraged me to set up my own implementation.

In my setup, I have my daily notes stored in the "daily" folder in Obsidian, and the habit tracking part looks like this:

### Tracker
- [ ] habit 1
- [ ] habit 2

etc.

One thing that I wanted to modify compared to your example was to automatically pick up any new items added to the tracker without a code change. Here's how I did it:

const calendarDataTemplate = {
	year: moment().year(),
	colors: {
		"x": ["#228B22", "#228B22", "#228B22", "#228B22", "#228B22"],
		"-": ["#FF0000", "#FF0000", "#FF0000", "#FF0000", "#FF0000"],
		">": ["#696969", "#696969", "#696969", "#696969", "#696969"]
	},
	intensityScaleStart: 1,
	intensityScaleEnd: 5,
	showCurrentDayBorder: true
}
// This above is mostly the same as in your example, but the enties part is omitted.
// I've added showCurrentDayBorder, as I think that's quite useful here.
// I'm going to use this calendarDataTemplate as a template below

// I broke things apart a bit, just to make things a bit more readable.
// So I'm loading my daily notes from the "daily" folder...
const pages = dv.pages('"daily"').file
// ...and filter it down for tasks in the tracker section, but not checking 
// whether the tasks are completed - at this stage, I need even the non-complete ones
const trackerTasks = pages.tasks.where(p => String(p.section).includes("Tracker"))

// I'm going to store the calendar entries per habit in this:
const calendarEntriesPerHabit = {}

for (const task of trackerTasks.values) {
	// If the habit in the current task is not yet in the entries, 
	// I'm adding it with an empty array
	if (!(task.text in calendarEntriesPerHabit)) {
		calendarEntriesPerHabit[task.text] = []
	}
	// If the task is not incomplete, I'm adding it to the earlier entries 
	if (task.status !== ' ') {
		calendarEntriesPerHabit[task.text].push({
			date: task.path.split("/").pop().replace(".md", ""),
			color: task.status,
			intensity: 5
		})
	}
}

// At this stage I should have a complete dict with all the occurrences per habit, ...
for (const habit in calendarEntriesPerHabit) {
	// ...so all that's left is to put a header with a habit name...
	dv.el('h3', habit)
	// ...and to render the heatmap for the given habit, constructing
	// the calendar data from the template + the entries
	renderHeatmapCalendar(this.container, {
		...calendarDataTemplate, 
		entries: calendarEntriesPerHabit[habit]
	})
}

This nicely renders the habit heatmaps one by one without having to duplicate code.

scorchio avatar Oct 28 '22 22:10 scorchio