# How Zakirullin Files Tracks Habits: A Year-Long Markdown Matrix Approach

> Discover how Zakirullin Files tracks habits using a year-long markdown matrix. Learn about its emoji system and real-time web interface for efficient habit monitoring.

- Repository: [Artem Zakirullin/files.md](https://github.com/zakirullin/files.md)
- Tags: how-to-guide
- Published: 2026-05-21

---

**Zakirullin Files stores habits as a year-long emoji matrix in markdown files, parsing them into an in-memory map that powers a real-time web interface with GET and POST endpoints.**

Zakirullin Files is a personal knowledge management system that implements habit tracking through a novel markdown-based architecture. Instead of relying on a traditional database, the [`zakirullin/files.md`](https://github.com/zakirullin/files.md/blob/main/zakirullin/files.md) repository stores daily habit completions as emoji strings in yearly markdown files located in the Insights directory. This design enables version control, simple text editing, and fast in-memory querying through a sparse map structure.

## Storage Format and Parsing Logic

The habit tracking system persists data in **`~/Insights/<year> Habits.md`** files (for example, `2024 Habits.md`). Each file contains a markdown matrix where habits are represented as lines of emojis followed by the habit name.

The file format uses month headers and emoji sequences:

```markdown

### January

🟢⚪️🟢🟢  Went to gym
⚪️⚪️⚪️⚪️  Read book

### February

🟢🟢⚪️🟢  Went to gym

```

To parse this format, **[`server/habits/habits.go`](https://github.com/zakirullin/files.md/blob/main/server/habits/habits.go)** implements the `Habits()` function which performs the following steps:

- **Lists existing habit definitions** using `userFS.FilesAndDirs(fs.DirHabits)` to determine valid habit names
- **Reads the yearly insights file** via `userFS.Read(fs.DirInsights, filename)` 
- **Detects month headers** with `strings.HasPrefix(line, "###")` to calculate day offsets correctly
- **Parses mood lines** specially when `strings.Contains(habit, MoodHabit)` to store emoji power levels
- **Parses regular habit lines** mapping each emoji position to `dayOfTheYear+dayOffset` with status values (`0` for skipped, `1` for completed)

## In-Memory Data Structure

Once parsed, the system stores habits in a **sparse map structure** defined as `map[string]Year`, where `Year` is `map[int]int` (day-of-year → status). This means only days with recorded activity occupy memory, making the representation efficient for year-long tracking.

The type definitions function as follows:

- **Key**: Habit name string (e.g., "Went to gym")
- **Value**: Map of integer day-of-year (1-365/366) to integer status
- **Status values**: `0` indicates skipped/incomplete, `1` indicates completed, with special handling for mood emojis

## Rendering the Weekly Habit View

The **`habits.Render()`** function in [`server/habits/habits_render.go`](https://github.com/zakirullin/files.md/blob/main/server/habits/habits_render.go) generates the user interface by calling `LastWeekHabits()` to extract a 7-day slice of the yearly data. It then injects this data into the embedded template located at **[`server/habits/templates/habits.html`](https://github.com/zakirullin/files.md/blob/main/server/habits/templates/habits.html)**.

The template constructs an HTML table where each cell contains data attributes for interactivity:

```html
<td data-habit="{{$habit}}" data-day="{{$day}}" data-status="1"
    class="day {{ if eq $status 1 }}checked{{ end }} {{ if eq $.currentDay $day }}today{{ end }}">
    {{ if $firstLine }}<div class="text">{{$habit}}</div>{{ end }}
</td>

```

These `data-*` attributes enable the JavaScript layer to identify which habit and day to update when users click specific cells.

## API Endpoints for Real-Time Updates

The **[`server/sync/webserver.go`](https://github.com/zakirullin/files.md/blob/main/server/sync/webserver.go)** file exposes two HTTP endpoints that coordinate the read/write cycle between the frontend and the markdown storage.

**GET `/habits_v2/{userID}`** (lines 33-57)
- Constructs a user-specific filesystem abstraction using [`server/fs/fs.go`](https://github.com/zakirullin/files.md/blob/main/server/fs/fs.go)
- Calls `habits.Render()` to generate the HTML table for the current week
- Returns the rendered template with embedded CSS classes `checked` and `today`

**POST `/habits_v2/{userID}/{habitName}/{yearDay}/{status}`** (lines 60-104)
- Parses path parameters: `userID`, `habitName`, `yearDay` (1-365), and `status` (0/1)
- Loads the user's habit map using `habits.Habits()`
- Validates the habit exists: `if _, ok := userHabits[habitName]; !ok { ... }`
- Updates the specific day: `userHabits[habitName][int(yearDay)] = int(status)`
- Persists changes via `habits.Write()` back to the yearly markdown file
- Resolves the appropriate emoji using `habits.Emoji()` and appends to the daily journal via `journal.AddEmoji()` and `journal.AddRecord()`

## Frontend Interaction Flow

The rendered page in [`habits.html`](https://github.com/zakirullin/files.md/blob/main/habits.html) includes client-side JavaScript that captures click events on table cells. When a user toggles a habit completion, the script extracts the `data-habit`, `data-day`, and `data-status` attributes, then issues a POST request:

```javascript
fetch(`https://{{.host}}/habits_v2/{{.userID}}/${habit.habit}/${habit.day}/${habit.status}`, {
    method: "POST"
});

```

The UI updates immediately by toggling the `checked` CSS class, while the server asynchronously writes the change to the markdown file and updates the journal.

## Practical Code Examples

### Reading a User's Full Habit Map

```go
fs, _ := fs.NewUserFS(userID)
habitsMap, err := habits.Habits(fs, 2024) // Returns map[string]habits.Year
if err != nil { 
    log.Fatal(err) 
}
fmt.Println(habitsMap["Went to gym"][150]) // Status for day 150 (May 30)

```

### Updating a Habit via HTTP

```bash
curl -X POST "https://api.example.com/habits_v2/12345/Went%20to%20gym/200/1"

```

Where `200` represents the day-of-year, `1` marks completion, and the server writes the change back to `2024 Habits.md` while logging to the journal.

### Rendering the Weekly View

```html
<div id="habits">
  {{template "habits.html" .}}
</div>

```

The template receives a map structure where each column represents a day of the current week relative to the user's configured timezone.

## Summary

- Zakirullin Files stores habits as emoji matrices in **`~/Insights/<year> Habits.md`** markdown files
- The parser in **[`server/habits/habits.go`](https://github.com/zakirullin/files.md/blob/main/server/habits/habits.go)** converts these files into sparse `map[string]Year` structures
- **[`server/habits/habits_render.go`](https://github.com/zakirullin/files.md/blob/main/server/habits/habits_render.go)** generates interactive HTML tables with data attributes for each cell
- REST endpoints in **[`server/sync/webserver.go`](https://github.com/zakirullin/files.md/blob/main/server/sync/webserver.go)** handle reads (GET) and updates (POST) without requiring a database
- Changes persist back to markdown while simultaneously updating the journal system via **`journal.AddEmoji()`**
- The frontend uses vanilla JavaScript to toggle statuses and synchronize with the server in real-time

## Frequently Asked Questions

### How does Zakirullin Files physically store habit data?

The system writes habits to yearly markdown files (e.g., `2024 Habits.md`) in the Insights directory using emoji characters to represent daily status. Each line contains a string of emojis followed by the habit name, with month headers (`### January`) providing structural boundaries for parsing day calculations.

### What happens when I click a habit cell in the web interface?

The client-side JavaScript in [`templates/habits.html`](https://github.com/zakirullin/files.md/blob/main/templates/habits.html) reads the `data-habit`, `data-day`, and `data-status` attributes from the clicked `<td>` element, toggles the status value, and sends a POST request to `/habits_v2/{userID}/{habitName}/{yearDay}/{status}`. The server then updates the in-memory map and rewrites the entire yearly markdown file via `habits.Write()`.

### Can I edit habit files manually without breaking the parser?

Yes, because [`server/habits/habits.go`](https://github.com/zakirullin/files.md/blob/main/server/habits/habits.go) parses the file line-by-line looking for emoji sequences and month headers. You can manually add emojis or edit existing ones using any text editor; the parser recalculates day offsets based on the `### <Month>` headers and emoji positions, making the system compatible with standard git workflows and manual edits.

### How does the system handle different years and timezones?

The `Habits()` function accepts a year parameter (e.g., `habits.Habits(fs, 2024)`) to load the specific file, while `LastWeekHabits()` uses `cfg.Timezone()` to calculate the correct 7-day window for display. The day-of-year integer (1-366) serves as the universal index across both storage and API layers, ensuring consistent addressing regardless of client timezone.