I’m using timetagger to track my working times and I recently wanted to check if I’m over or under my contracted time. Usually I would write a small application in bash or java or nodejs or python. But because I’m using nushell more in the last months I wanted to try if I can achieve the same with my new favorite shell.
The script should do the following:
- query a configurable date range
- display working times grouped by date
- display the overall sum of working times
Simple enough. The timetagger API supports querying a time range with two UNIX timestamps as
parameters. In nushell you can convert a date into a UNIX timestamp with the format
command:
date now | format date "%s"
# 1706081101
nushell also supports operations on dates with durations in a simple notation:
(date now) - 4wk
# Wed, 27 Dec 2023 08:27:12 +0100 (4 weeks ago)
(date now) - 4wk | format date '%s'
# 1703662037
Another function I use pretty often is downloading files or data via nushell:
http get https://www.kopis.de/bookmarks/bookmarks/759
╭───────────┬────────────────────────────────────────────────╮
│ ID │ 759 │
│ CreatedAt │ 2024-01-18T08:47:01.045657Z │
│ UpdatedAt │ 2024-01-18T08:47:01.045657Z │
│ DeletedAt │ │
│ URL │ https://some/host/on/the/internet │
│ Title │ Super interesting title goes here ... │
│ Content │ <div><div> │
│ │ │
...
│ │ </div></div> │
│ Public │ true │
╰───────────┴────────────────────────────────────────────────╯
That should be enough to write a simple script to query the timetagger API:
let base_url = 'https://hostname/path/to/timetagger'
let token = '1234567890'
# returns a datetime at the start of a given date
def start_of_day [date] {
return ($date | format date '%Y-%m-%dT00:00:00Z' | into datetime)
}
# returns a datetime at the end of a given date
def end_of_day [date] {
return ($date | format date '%Y-%m-%dT23:59:59Z' | into datetime)
}
# converts a UNIX timestamp into a datetime
def convert_ts [ts] {
return ($ts * 1_000_000_000 | into datetime)
}
# sends an authenticated request to the timetagger API and returns the records
def request [token t1 t2] {
let url = [$base_url '/api/v2/records?timerange=' ($t1 | format date "%s") '-' ($t2 | format date "%s")] | str join
let data = http get --headers [authtoken $token] $url | get records
return $data
}
# returns records grouped by date and the difference to a default 8 hours working day added as a column for each day
def get_records [token duration] {
let t1 = start_of_day ((date now) - ($duration | into duration))
let t2 = end_of_day (date now)
let data = request $token $t1 $t2 | update t1 {|row| convert_ts $row.t1} | update t2 {|row| convert_ts $row.t2}
let result = $data | insert duration {|row| ($row.t2 - $row.t1)} | select key t1 t2 duration
let grouped = $result | group-by --to-table {$in.t1 | format date '%Y-%m-%d'} | insert sum {|row| $row.items.duration | math sum} | insert diff {|row| $row.sum - ('8hr' | into duration)}| select group sum diff
return $grouped
}
Now I can use this script to display all working times for the last 4 weeks:
source myscript.nu
get_records $token 4wk
#╭────┬────────────┬─────────────────┬──────────────────╮
#│ # │ group │ sum │ diff │
#├────┼────────────┼─────────────────┼──────────────────┤
#│ 0 │ 2024-12-27 │ 8hr │ 0sec │
#│ 1 │ 2023-12-28 │ 8hr │ 0sec │
#│ 2 │ 2023-12-29 │ 8hr │ 0sec │
#│ 3 │ 2024-01-01 │ 8hr │ 0sec │
#...
Calculating the sum of all working times is also straight forward with nushell:
> get_records $token 4wk | get diff | math sum
# 1hr 2min 3sec
The command queries the last 4 weeks from the timetagger API, gets the diff
column and calculates the sum of
all rows. The more I work with nushell tables the more I like it.