eudaimonia

Motivation

It seems that perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

— Antoine de Saint-Exupery

Eudaimonio is my Personal Knowledge Management System (PKMS). Similar to Obsidian or Zettrl, it visualizes notes and their links to other notes as a graph. The reason why I want to invest time into this project, is because I hope that it will help me in all areas of my life, from cooking, by storing all of my recipies, to learning new skills, by writting, summerizing and reviewing, to use it as a journal, so I don’t forget where I started from, to sharing those ideas with others.

While implementing, I tried to keep it as simple as possible, following the the quote of Antoine de Saint-Exupery. For this reason, I want to take this opportunity and learn how to write simple and maintanable code.

Concept

Behind the scene of eudaimonia is a collection of HTML files, which can viewed on a web browser on any device. This collection is visualized as a graph, with each node being a HTML file and their links to each other as an edge.

Using HTML files has a few advantages:

  1. Sharing notes via a webbrowser is easy.

  2. The contents of eudaimonia can be viewed on any device with a web browser.

  3. It is independant on the source format (Markdown, AsciiDoc, etc.) as they can be compiled into HTML by various tools.

Graph Visualization

One part of eudaimonia is the visualization of notes as a graph.

A force directed algorithm is used to draw the graph. It simulates a physical system, by imagining the edges as springs pulling and pushing the connected vertices to an optimal distance. At the same time all vertices are repelling each other, as if they have the same electromagnetic charge. This leads to a visually pleasing embedding, with few edge crossings and evenly spaced vertices.

The design for nodes and edges are kept as simple as possible. Nodes are represented by a rectangle containing text and edges are straght lines connecting the rectangles.

Drawing Edges

The overall goal for the graph view is to reduce cluttering to a minimum. For edges this means that they need to be as short as possible. The algorithm used to connect two rectangles with the shortest line is described here.

private drawEdge(vertexA: DrawingVertex, vertexB: DrawingVertex): void {
    try {
        const dividendX =
            vertexA.left * vertexB.left - vertexA.right * vertexB.right;
        const divisorX =
            vertexA.left + vertexB.left - (vertexA.right + vertexB.right);
        const x = dividendX / divisorX;

        const dividendY =
            vertexA.top * vertexB.top - vertexA.bottom * vertexB.bottom;
        const divisorY =
            vertexA.top + vertexB.top - (vertexA.bottom + vertexB.bottom);
        const y = dividendY / divisorY;

        const optimalPoint = new Point(x, y);
        const line = this.getDrawingEdge(vertexA, vertexB);
        const anchorA = optimalPoint.copy();
        const anchorB = optimalPoint.copy();

        if (vertexA.right < vertexB.left) {
            anchorA.x = vertexA.right;
            anchorB.x = vertexB.left;
        } else if (vertexA.left > vertexB.right) {
            anchorA.x = vertexA.left;
            anchorB.x = vertexB.right;
        }

        if (vertexA.top > vertexB.bottom) {
            anchorA.y = vertexA.top;
            anchorB.y = vertexB.bottom;
        } else if (vertexA.bottom < vertexB.top) {
            anchorA.y = vertexA.bottom;
            anchorB.y = vertexB.top;
        }

        line.pointA = anchorA;
        line.pointB = anchorB;
    } catch {
        throw new Error(
            `Error drawing edge from ${vertexA.toString()} to ${vertexB.toString()}:`
        );
    }
}

systemd

  1. Copy the eudaimond binary to /usr/local/bin

  2. Create a service file under /etc/systemd/system/eudaimond.service

  3. Enable the service with systemctl

Build the binary with GOOS=linux GOARCH=arm64 go build -o build/eudaimond
eudaimond.service
[Unit]
Description=eudaimond
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/eudaimond
WorkingDirectory=/home/pi/.eudaimond
Restart=always
User=pi

[Install]
WantedBy=multi-user.target

Backend

Reporting Errors

type ProblemDetail struct {
	Type     string `json:"type"`     // Primary identifier for the problem type. Should be an absolute and resolvable URI, that points to a human readable documentation for the problem type
	Status   int    `json:"status"`   // The same status code used in the HTTP response.
	Title    string `json:"title"`    // Short, human-readable summary of the problem type
	Detail   string `json:"detail"`   // Human-readable explanation about how to correct the problem.
	Instance string `json:"instance"` // Unique identifier of the problem occurance for the server
}

Finances

func ProcessCsv()