go-timetracker | Part I - Why and What
Tags: programming, go, sideprojects
Published: 2020-04-19 18:18:03

---
title: "go-timetracker | Part I - Why and What"
published: true
description: A simple time tracker implemented in go
tags: [programming, golang, sideprojects]
---

Lately, I've been enjoying programming in `go`. I don't know what it is, but I feel _good_ writing go code. Maybe it's because I have a background in dynamically typed languages (such as `ruby` and `javascript`) and so returning now to a statically typed and compiled language just seems like its a new, shiny thing and I like shiny new things. And what better way to learn a new programming language than to actually build something with it?

This post will be part of a series of posts, where I will describe my process of building a command-line application in go. In this first post, I'll go over what the project is, why I'm building it, and how I intend to build it!

## Why

I've always heard that tracking your time is a good thing. It allows you to review _where_ you're spending your time and it gives you a better view on whether you're using your time effectively or not. It can also tell you how much time you spend working and procrastinating. There are tons of tools that can do this for you, but I wanted something that could be used via simple commands from my terminal, and which doesn't get in the way of doing what I set out to do (i.e, I don't want to spent too much time learning a productivity tool, because that would be... unproductive).

## What

The tool I will be writing (`go-tt`) will allow me to say something like _I'm now working on unit tests_ and, a few minutes later, I can say _I'm now writing documentation for feature X_. If I want to consciously take a break, like reading an article, I can quickly say _I'm now reading an article_. I should then be able to see at a glance the tasks I've been working on in the last day, week, month, year or in any other random period in time.

## Requirements

* It should be accessible via the **command line** and be easily **installable via popular package management systems** (like `homebrew`, `apt`, etc). However, installing it from source should be easy too.
* It should work on all major operating systems.
* It should be **_fast_**, with a **few set of commands**, which must be easy to use (which implies a low number of arguments for each command) and **good documentation**.
* It should be free for modification and distribution, i.e, **open-sourced**.
* The code should be **_extensible_**:
  * Even though it will be primarily a CLI application, adding a GUI in the future should not be much of a trouble.
* It should report the data in various formats: `CSV`, `JSON`, etc (more on this later).
* It should **contain as few dependencies as possible**.
* It should be well covered with **tests**.

## Specification

Let's get into the nitty-gritty parts. In this section I will outline the major commands in the program, what they should do and how they should work. Note that things can (and most probably will) change during the development of the project.

### Modules

* **UI**: This module takes care of everything about displaying and receiving data from/to the user. Here we can have one submodule per UI layer: `CLI`, `GUI`, etc.
* **Persistence**: This module takes care of writing/reading hard data. I will talk a bit more about the data we're going to need later.
* **Core**: This module will contain most of the business logic. It will combine all the other modules to achieve the goals of the program.
* **Reporter**: This module will handle the logic of taking the data and presenting it to the user (for instance, in CSV files). More on this later.

### Operations

Do not confuse this with command-line commands, even though the syntax I will use may suggest that. This is an _high_ level overview of the operations the program should accept.

* `add new activity`

This operation creates a new activity in the system. For instance, one could create an activity named `writing tests` or `attending meeting`. An activity is identified by a unique identifier and can additionally have a description and a list of aliases. If the activity already exists, a helpful error message should be returned to the user.

* `start activity X | alias`

Once this operation is executed, the program starts tracking the time that the user is taking to do the activity _X_. If activity _X_ is not yet created, then it should automatically create it and start tracking it. _Tracking_  here simply means recording a timestamp with the current system time. **Only _one_ activity can be tracked at the same time**, i.e, the user cannot start two activities one after the other, unless the previous one is stopped (this is an explicit decision on my part: I don't want to encourage multi-tasking). An alias can be used instead to reference the activity.

* `stop activity X | alias`

Once this operation is executed, the program records the current system time and calculates the total time spent on this activity by using the start time recorded early. If the activity _X_ does not exist, or if it does exist but its not the current one being tracked, or if none activity at all is being tracked, an error message should be returned. An alias can be used instead to reference the activity.

* `delete activity X | alias`

This operation deletes the activity `X` from the system. If the activity does not exist, an error is returned. This action is not reversible: deleting an activity will also delete all data associated with it. An alias can be used instead to reference the activity.

* `get report`

This operation returns the tracked data of all activities or for a given specified list of activities in a given period. The user can decide which format he wishes the report to be in. More on this later.

* `list activities`

This operation lists all activities currently registered in the system.

* `backup`

This operation creates a compressed file (`.zip` or `.tar`, for instance) with all the data recorded for your activities since the beginning of time.

* `purge`

This operation wipes out all the data in the system.

## Data

As I've mentioned, in the first version, we're going to rely on the filesystem to persist data. The data we'll need is:

* A list of tracked activities. (_Activity metadata_ files)
* A daily log where each activity start and end time is recorded (_Activity Log_ files)

I can easily implement this using the file system with JSON files. (the choice here is arbitrary, we could easily just use plain `.txt` files, but I find working with JSON pleasant).

### Activity Metadata

Each activity will have its own meta data file. The name of the file will be the identifier of the activity. For instance, for the activity _activity-1_:

```json
{
  "identifier": "activity-1",
  "description": "Some description...",
  "aliases": ["alias1", "alias2"]
}
```

### Activity Log

Each individual activity log has the following structure:

```json
{
  "activity-1": [
    {
      "start": 1587231890,
      "end": 1587231901,
      "duration": "..."
    },
    {
      "start": 1587231920,
      "end": 1587231931,
      "duration": "..."
    }
  ]
}
```

I have yet to decide the data format for the `duration` key. The name of the activity log file will have the structure `YYYY/MM/DD.json`, for instance: `2020/10/01.json`.

Each entry in the array for `activity-1` corresponds to a cycle of starting and stopping the tracking.

For instance:

```bash
> go-tt start a1 # t0
# ...
> go-tt stop a1 # t1
> go-tt start a2 # t2
# ...
> go-tt stop a2 # t3
> go-tt start a1 # t4
# ...
> go-tt stop a1 # t5
> go-tt start a2 # t6
# ...
> go-tt stop a2 # t7
```

This would produce the following log file:

```json
{
  "a1": [
    {
      "start": "t0",
      "end": "t1",
      "duration": "..."
    },
    {
      "start": "t4",
      "end": "t5",
      "duration": "..."
    },
  ],
  "a2": [
    {
      "start": "t2",
      "end": "t3",
      "duration": "..."
    },
    {
      "start": "t6",
      "end": "t7",
      "duration": "..."
    }
  ]
}
```

Combining every thing together, here's a possible folder hierarchy for the data describe above:

```txt
meta/
  activity-1.json
  activity-2.json
  ...
log/
  2020/
    01/
      1.json
      2.json
    02/
      10.json
  2021/
    03/
      1.json
  ... etc
```

**This is madness!**

Yes, I know! This solution has SO many disadvantages, including:

* What if the files get corrupted?
* What if the user manually changes the files?
* What if the files disappear?
* I have to load _metadata_ and log files in memory to read/update activities and to create the reports! This is going to cause memory problems soon enough!
* What about automatic/regular backups?
* You're trying to be smarter than the guys that spent many years writing and optimizing databases for this kind of thing! Who do you think you are?

_I know_.

Since I'm new to the language, I think that learning how to do I\O is important, and this initial version will be the perfect playground to do it. Plus, if I write the code in a modular fashion, I can then plugin a DBMS like Postgres, SQLite or even Mongo easily. That will certainly come later on.

This is the kind of solution I would be _ashamed_ to show publicly, and only work on it privately. But I want to share the process with you. I am aware this is not a good solution, but I must remind myself: this tool is built to be **used by me** first and foremost, and to learn `go`. On future iterations, I will certainly add a more robust layer of data persistence.

## Reports

In this initial version, the reporting system is straightforward. It should accept three arguments: the format of the report (in this first version, I will write three report systems: JSON, CSV and directly in the stdout), the initial date and end dates (like: `2020-01-02` to `2020-01-04`). The report (independently of the format) should present:

1. A list of activities tracked in that period.
2. For each activity, the total time spent on it.

For fun, we could show other aggregations like:

* The average time spent in each activity per day/week/month.
* The time of day where each activity is most performed.
* The median of each activity.
* The days of the week where each activity is most performed.
* A graph (only possible in UI solutions) showing the evolution of the time spent in each activity in the period.
* ... any other ideas? Let me know!

Some of these can certainly come later on.

## Notes

* Aren't you worried that people might steal your idea? Absolutely not! Please, feel free to go ahead and implement it on your own as well! It would be fun to compare implementations later on!

* Are you going to host the project on Github? [Yes.](https://github.com/luispcosta/go-tt)

* What will be the schedule of the posts? I will not set an explicit hard schedule to write posts for this series. After I have finished a major milestone in the project, I will then write a post about it.

---

Thanks for reading! If you find any clear and alarming problem in this first naive implementation, or if you have any other ideas you'd like to see, **please let me know!**