Tracking your time spent in the terminal

Published: 2022-01-23 | Last Updated: 2022-01-25 | ~19 Minute Read

Table of Contents

Motivation

At the end of 2021 I noticed many people posting some information from their social media accounts, like Spotify and others, about the details of their usage on those applications. I don’t use some of these services because I prefer open source alternatives, however the idea to look back and see your behavior on a given platform/application over time is really interesting to me.

Most of the applications I use don’t have that type of feature and I don’t mind, but for some others I’d like to know some stats of my usage to drive decisions about habits or general behavior tweaks. Tracking yourself can prove to be useful to better yourself or simply to know more about how you operate in general.

An example of the power of tracking yourself is Stephen Wolfram’s personal analytics which showcase many different data points from his life which he then analyzes. Like in the previous example this practice can allow us to use data in a way that could potentially bring meta-information to light that would otherwise be very difficult to obtain.

Inspired by this, I thought it’d be nice to track my activities on the terminal over time. I decided to create a tool for this since I was not able to find anything that did this from the terminal in a way that worked for me. A complex system like Wolfram Alpha is definitely not the goal, but I’ll try to make a tool that I can easily use and that provides enough insight for me to consider useful.

Requirements

I expect this tool to fulfill the following list of requirements:

I think the first two are pretty self explanatory but I’d like to elaborate a bit on the third requirement. I’ve seen other tools for this task, mainly TimeWarrior, Pomogoro and Timetrace, however they seem to focus on to-do or freelancing style functionality. I have a much simpler workflow in mind for my activity tracking so I will focus on that.

I would mainly want to track things I do via the terminal, i.e. vim, ssh. These two tools are the main focus for me right now since I use the Vim editor to write blog posts, like this one, and software development.

I expect to be able to see how many hours I spend working on programming tasks, regardless of what they’re related to, during the year as well as writing/researching blog posts. Similarly I want to track how long I spend on SSH sessions since I’m starting a new project that will require a bit of time over SSH.

Past experience

I have tried several mobile time tracking applications in the past where you can to track pretty much anything and see the data nicely organized. One I used for a while and really liked was ATimeTracker, and another one that I used briefly, and look forward to using again once this feature is implemented, is Track-and-graph.

These apps are great for general habit tracking, however for tracking activities when I’m on my desktop what happens is I often forget to start/stop the timers and data becomes inconsistent.

There are plenty of time trackers for the desktop, however I feel like they add overhead to my workflow and slow me down with the additional logging in, clicking around and such.

Expected workflow

I would like my workflow to be simple, there should not be any overhead to whatever I’m doing. I would like to avoid things like maintenance and preparation/setup to begin tracking a given task.

One thing I have seen from other tools is that there is a lot of setup and configuration prior to being able to track your activities. I understand that this is necessary in some, probably most, situations but my use case is a bit different.

I would like to simply begin a coding session for example and have the tracker stop automatically when I close the application, that way I avoid forgetting to stop it after a long session of programming. The same applies for SSH, I expect to connect via SSH and work on things and when I close the session the tracker will stop tracking that activity.

After tracking my usage over time for a given task I want the ability to check statistics of my activity when I choose to.

Plan

So far I have the following:

Solution

What I have come up with so far is a shell function that uses the time utility to track the process. This approach has the benefit of being able to stop tracking when the process exits, which is handy.

This is the simple .bashrc function I have so far:

# Takes two parameters, 1.command to track 2.tag associated with the task
function track {

    # Pass the first cli argument to the time program
    # Ouput the time results to the time-tracking.txt file
    { time $1 ; } 2>> ~/time-tracking.txt

    # Append the date to the time output
    date >> ~/time-tracking.txt

    # Append the last cli argument as the task name
    echo "task: ${@: -1}" >> ~/time-tracking.txt

}

The user can pass two parameters to the function:

  1. Command to execute/track
  2. Task category aka tag

I plan to group things into categories such as programming or blog-post so that I can easily view the statistics for these groups. This can be easily customized on the fly by using specific tags as the last argument.

As an example one could further specify tag names based on their own naming conventions or more established ones. The BEM methodology comes to mind as one that could be adapted. As a practical example of this blog-post-tracker, programming-golangapp would be tags that provide further details.

Here is a sample execution:

bash-5.1$ track vim blog-post

This needs absolutely no setup, I simply run the command as I usually would with the added flags and the tracking is started, and once I exit the vim program tracking ends.

This is the resulting entry in the time-tracking.txt file:

bash-5.1$ cat time-tracking.txt 

real    6m2.391s
user    0m0.035s
sys     0m0.003s
Sat Jan 22 22:08:15 CST 2022
task: blog-post

I would like to keep only the real time from the time command output only so I created a second function to remove the additional entries that looks like the following:

# Clean up the user and sys entries from the time command
function cleanup() {
    sed -i "/^user/d" ~/time-tracking.txt
    sed -i "/^sys/d" ~/time-tracking.txt
}

I then call the cleanup function from within the track function and the time-tracking.txt file output now looks like so:

bash-5.1$ cat time-tracking.txt 

real    6m2.391s
Sat Jan 22 22:08:15 CST 2022
task: blog-post

This is the updated track function:

# Takes two parameters, 1.command to track 2.tag associated with the task
function track {

    # Clean up the user and sys entries from the time command
    function cleanup() {
        sed -i "/^user/d" ~/time-tracking.txt
        sed -i "/^sys/d" ~/time-tracking.txt
    }

    # Pass the first cli argument to the time program
    # Ouput the time results to the time-tracking.txt file
    { time $1 ; } 2>> ~/time-tracking.txt

    # Append the date to the time output
    date >> ~/time-tracking.txt

    # Append the last cli argument as the task name
    echo "task: ${@: -1}" >> ~/time-tracking.txt

    # Run the cleanup function
    cleanup
}

So with that and after calling it a few times for different tasks, we have a file with entries like the following:

bash-5.1$ cat time-tracking.txt 

real    6m2.391s
Sat Jan 22 22:08:15 CST 2022
task: blog-post

real    0m10.709s
Sat Jan 22 22:13:24 CST 2022
task: blog-post

real    1m24.624s
Sat Jan 22 22:18:08 CST 2022
task: programming

Additionally if we wanted to track something like firefox with additional flags instead of vim which I use as a simple one word command we would track it like so:

bash-5.1$ track firefox https://nixing.mx browsing

This yields the expected output in the time-tracking.txt file:

real    0m0.023s
Sat Jan 22 23:53:49 CST 2022
task: browsing

The last space separated parameter will be taken as the tag to be added to the time-tracking.txt file, again this makes it simple in terms of maintenance and setup overhead.

Statistics

Now that we have a way to track our activities on the terminal we can parse the file to gather some data. I have not had much time to work on this yet but I created a, partially working, new function stats where we can parse the file however we want.

The stats function looks like the following:

# Takes one parameter, the tag the user wants to get stats for
function stats {

    # Process the time-tracking.txt file, remove the date line and add the minutes for the specified task
    cat time-tracking.txt | grep -v Sat | sed -n "/^task: $1/{x;p;d;}; x" | grep real | sed "s/real//g" | \
        awk  -F '[ms]' '{print $1 " minutes "  $2 " seconds"}' | \
        awk '{sum+=$3 ; print $0} END{print "sum:", sum, "secs ", " avg:", sum / NR, "secs"}'
}

When we call this function for the blog-post tag we get the following:

bash-5.1$ stats blog-post
        6 minutes 2.391 seconds
        0 minutes 10.709 seconds
sum: 13.1 secs   avg: 6.55 secs

For a different tag:

bash-5.1$ stats programming
        1 minutes 24.624 seconds
sum: 24.624 secs   avg: 24.624 secs

Finally if we run the stats function without any parameters we get the time sum in minutes of all tasks regardless of tag:

bash-5.1$ stats
        6 minutes 2.391 seconds
        0 minutes 10.709 seconds
        1 minutes 24.624 seconds
sum: 37.724 secs   avg: 12.5747 secs

Next steps

I still need to work on the stats parsing and perhaps on adding more details to the track entries. I will continue to update here as I make progress but I think most of the core functionality I expected was achieved by these simple functions.

I would like to add statistics for month and day of the week next in order to see which tasks I worked on the most during a given period etc.

Update Jan 24th

I have worked a bit more on this and fixed a few bugs and added another function. I noticed that my previous version of the stats function was not counting the minutes in the result, only the seconds.

I also made some changes to the track function to better format the data that’s written to time-tracking.txt, below are the updates for these two functions:

Small updates were done to the track function:

# Takes two parameters, 1.command to track 2.tag associated with the task
function track {

    # Clean up the user and sys entries from the time command
    function format() {

        # Delete the user and sys lines
        sed -i "/^user/d ; /^sys/d" ~/time-tracking.txt

        # Prepend date: to the date line
        sed -i "s/^\([A-Z][a-z][a-z]\)/date: \1/g" ~/time-tracking.txt

        # Prepend time: to the time line
        sed -i 's/^real[[:blank:]]/time: /g' time-tracking.txt
    }

    # Pass the first cli argument to the time program
    # Ouput the time results to the time-tracking.txt file
    { time $1 ; } 2>> ~/time-tracking.txt

    # Append the date to the time output
    date >> ~/time-tracking.txt

    # Append the last cli argument as the task name
    echo "task: ${@: -1}" >> ~/time-tracking.txt

    # Run the cleanup function
    format
}

The contents of the time-tracking.txt file after the updates:

bash-5.1$ head time-tracking.txt 

time: 6m2.391s
date: Sat Jan 22 22:08:15 CST 2022
task: blog-post

time: 0m10.709s
date: Sat Jan 22 22:13:24 CST 2022
task: blog-post

This is the updated stats function:

# Takes one parameter, the tag the user wants to get stats for
function stats {

    # Process the time-tracking.txt file
    # Remove the date line
    grep -v "^date:" ~/time-tracking.txt | \

        # Remove the task line and print line above it
        # This will get the time for the specified tag
        sed -n "/^task: $1/{x;p;d;}; x" | \

        # Remove the time: prepended text
        sed -n "s/^time: //p" | \

        # Print the time entries for minutes and seconds
        awk  -F '[ms]' '{print $1 " minutes "  $2 " seconds"}' | \

        # Add the minutes/seconds columns for the specified task 
        awk '{sum1 += $3 ; print $0 } 
            {sum2 += $1 }
            END {print "sum:", sum2, "mins", sum1, "secs ", " avg:", sum2 / NR, "mins", sum1 / NR, "secs"}'
}

When running the stats function now:

bash-5.1$ stats blog-post
6 minutes 2.391 seconds
0 minutes 10.709 seconds
sum: 6 mins 13.1 secs   avg: 3 mins 6.55 secs

I have also added another function graph. This function graphs the number of tracked activities per week day. The idea behind this is to see which days of the week I’m most active.

I shamelessly grabbed the code shared on this stack overflow question as a baseline and tweaked it a bit.

This what the new graph function looks like:

# Graphs the tracked activities per day of the week
function graph {

    # Generates a tmp file for the graph function to use
    function graphdata {

        # Create an array with weekdays
        WEEKDAYS=(Mon Tue Wed Thu Fri Sat Sun)

        # Loop over the weekdays
        for DAY in "${WEEKDAYS[@]}"; do 

            # Grep each weekday from the list
            grep $DAY time-tracking.txt | \

            # Count the results and write to graphtmpfile.txt
            wc -l >> ~/graphtmpfile.txt

        done
    }

    # Call the graphdata function
    graphdata 

    # Set variables for bold text messages
    NORMAL=$(tput sgr0)
    BOLD=$(tput bold)

    # The data in ~/graphtmpfile.txt should be one value per line.

    # Get highest value from the file
    MAXVALUE=$( sort -nr ~/graphtmpfile.txt | head -n1 )

    # Get single digit numbers from ~/graphtmpfile.txt
    SINGLEDIGIT=$(sed -n "/^[0-9]$/p" ~/graphtmpfile.txt)

    # Get double digit numbers from ~/graphtmpfile.txt
    DOUBLEDIGIT=$(sed -n "/^[0-9][0-9]$/p" ~/graphtmpfile.txt)

    # Get triple digit numbers from ~/graphtmpfile.txt
    TRIPLEDIGIT=$(sed -n "/^[0-9][0-9][0-9].*/p" ~/graphtmpfile.txt)

    # Check if the value is over 100
    if [ $MAXVALUE -ge 100 ]
    then  

        # Set the highest value for chart height
        # divide by 10 to make each line represent 10
        CHARTHEIGHT=$(( $MAXVALUE / 10 ))
    else

        # Set the highest value for chart height
        # leave value as is
        CHARTHEIGHT=$MAXVALUE
    fi

    # Line parsing variable
    CHARTLINE=$CHARTHEIGHT

    # Empty line for readability
    echo

    # Loop through all the lines in the ~/graphtmpfile.txt file
    while [ $CHARTLINE -gt 0 ]; do

        # Subtract one from the CHARTLINE variable
        (( CHARTLINE-- ))

        # Subtract REDUCTION from the VALUE variable
        REDUCTION=$(( $MAXVALUE*$CHARTLINE/$CHARTHEIGHT ))

        # Add line height to the graph
        echo | awk -v num=$CHARTLINE 'BEGIN{ line=num+1} { printf "%-4s", line}'

        # Loop through all lines of the file
        while read VALUE; do

            # Set the VALUE variable value
            VALUE=$(( $VALUE-$REDUCTION ))

            # Determine VALUE is less or equal to zero 
            if [ $VALUE -le 0 ]; then

                # Print spaces
                 echo -n "    "

            # If value is greater than zero
            else

                # Print graph characters
                 echo -n "||| "
            fi
        done < ~/graphtmpfile.txt
        echo
    done

    # Print the days of the week under results
    echo "    Mon Tue Wed Thu Fri Sat Sun"

    # Print a warning message when there is a big disparity
    # in the number of tasks between days
    if [ $MAXVALUE -ge 100 ] && [ $SINGLEDIGIT -ge 1 ] 
    then  
        echo
        echo "${BOLD}Note:${NORMAL} Source data contains mixed triple and single digit data "
        echo "      this graph is most likely inaccurate"
        echo
    fi

    # Delete the ~/graphtmpfile.txt file
    rm ~/graphtmpfile.txt
}

When running the graph function this is the output:

bash-5.1$ graph

13                          ||| 
12                          ||| 
11                          ||| 
10                      ||| ||| 
9                       ||| ||| 
8                       ||| ||| 
7                       ||| ||| 
6   |||                 ||| ||| 
5   |||                 ||| ||| 
4   |||                 ||| ||| 
3   |||                 ||| ||| 
2   |||     |||     ||| ||| ||| 
1   ||| ||| ||| ||| ||| ||| ||| 
    Mon Tue Wed Thu Fri Sat Sun

Each set of ||| characters represent one tracked activity for the day. From the graph above, on Tuesday for example there was only one activity tracked, while on Sunday there were 13.

When the numbers grow into triple digits the graph function will divide them by 10 so that each bar represents 10 tracked items.

I plan to expand the graph function to be able to show months and maybe weeks so you can easily glance at when you were most active during the year.

These few functions have become large enough to warrant their own script which I will be working on next, so keep an eye out for that in my git repositories if you’re interested in tracking yourself from the terminal.

Conclusion

It’s a good thing that I had this idea near the beginning of the year so I can start tracking early on. Hopefully I’ll be able to showcase how long I have spent on activities like programming and blog writing by the end of the year.

Once there is more functionality implemented I will most likely make it a standalone script and publish it on github like I do with Bourne to Blog.

Have a comment on one of my posts? Start a discussion in my public inbox by sending an email to ~grokkingnix/blog@lists.sr.ht [mailing list etiquette] [mailing list archive]


Posts from blogs I follow:

Introducing a Falkon extension RSS Finder

This weekend I decided to semi automate the process of searching for RSS feeds on websites while using Falkon web brosers. Many websites provide RSS feeds but do not provide any visible link or icon to access them (eg. many Wordpress based sites) and I ha…

via My land January 23, 2022
Help Chile write free software values, privacy, and digital sovereignty into their constitution

For those out of the loop, a group which included myself up until recently,1 Chile is in the midst of a revolution. They’re fighting against the increased cost of living, privatization of essential services, and worsening inequality — problems facing everyon…

via Drew DeVault's blog January 19, 2022
A warning to business owners and managers, you are a big part of the problem!

In my last couple of articles, mainly So-called modern web developers are the culprits and Is the madness ever going to end? I have written about some of the major problems with so-called modern web development and I have addressed the issues to the devel…

via unixsheikh.com January 13, 2022

Generated by openring