2021-06-27 23:20:37 -05:00
|
|
|
# Tiempo
|
|
|
|
|
2021-06-27 23:22:50 -05:00
|
|
|
A [timetrap](https://github.com/samg/timetrap/) compatible command line time
|
|
|
|
tracking application.
|
2021-06-27 23:20:37 -05:00
|
|
|
|
2021-08-14 18:47:58 -05:00
|
|
|
## Installation
|
|
|
|
|
2022-07-22 06:20:47 -05:00
|
|
|
### For Archlinux (and derivatives) users
|
|
|
|
|
|
|
|
There are a binary and a source package in the AUR:
|
|
|
|
|
|
|
|
* [tiempo-bin](https://aur.archlinux.org/packages/tiempo-bin)
|
|
|
|
* [tiempo-git](https://aur.archlinux.org/packages/tiempo-git)
|
|
|
|
|
|
|
|
### For every other linux users
|
|
|
|
|
|
|
|
Go to [gitlab releases page](https://gitlab.com/categulario/tiempo-rs/-/releases)
|
|
|
|
and grab the latest binary for your linux. There is a `.deb` file for Debian and
|
|
|
|
Ubuntu as well as a binary for any `x86_64` Linux.
|
|
|
|
|
|
|
|
### For Rust developers
|
|
|
|
|
|
|
|
You have `cargo`! you can run:
|
2021-08-14 18:47:58 -05:00
|
|
|
|
|
|
|
cargo install tiempo
|
|
|
|
|
2022-07-22 06:20:47 -05:00
|
|
|
### For everyone else
|
|
|
|
|
|
|
|
You need to compile `tiempo` by yourself. But don't worry! It is not that hard.
|
|
|
|
Just clone [the repository](https://gitlab.com/categulario/tiempo-rs), make sure
|
|
|
|
you have [rust installed](https://rustup.rs) and run:
|
|
|
|
|
|
|
|
cargo build --release
|
|
|
|
|
|
|
|
inside the repository. The binary will be named `t` (or `t.exe` if you use
|
|
|
|
windows) and it is located inside the `target/release` directory that was
|
|
|
|
created during compilation.
|
2021-08-14 18:47:58 -05:00
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
## Tutorial
|
|
|
|
|
2021-07-03 17:05:53 -05:00
|
|
|
First of all, you can abbreviate all commands to their first letter, so `t in`
|
|
|
|
and `t i` are equivalent.
|
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
### Managing entries
|
|
|
|
|
|
|
|
Register the start of an activity in the default timesheet with:
|
|
|
|
|
|
|
|
t in 'Doing some coding'
|
|
|
|
|
|
|
|
which sets the activity's start time to the current time. Later when you're done
|
|
|
|
use
|
|
|
|
|
|
|
|
t out
|
|
|
|
|
2021-07-03 17:05:53 -05:00
|
|
|
to mark it as finished. If you forgot to start the activity before you can do so
|
|
|
|
with:
|
2021-07-03 16:44:31 -05:00
|
|
|
|
|
|
|
t i --at '20 min ago'
|
|
|
|
|
|
|
|
the same applies for `t out`.
|
|
|
|
|
|
|
|
Edit an entry with
|
|
|
|
|
|
|
|
t edit [options]
|
|
|
|
|
|
|
|
where the options are
|
|
|
|
|
|
|
|
-i, --id <id:i> Alter entry with id <id> instead of the running entry
|
|
|
|
-s, --start <time:qs> Change the start time to <time>
|
|
|
|
-e, --end <time:qs> Change the end time to <time>
|
|
|
|
-a, --append Append to the current note instead of replacing it
|
|
|
|
the delimiter between appended notes is
|
|
|
|
configurable (see configure)
|
|
|
|
-m, --move <sheet> Move to another sheet
|
|
|
|
|
2021-07-03 17:02:34 -05:00
|
|
|
You can remove an entry with
|
|
|
|
|
|
|
|
t kill --id 123
|
|
|
|
|
|
|
|
or an entire timesheet with
|
|
|
|
|
|
|
|
t kill somesheet
|
|
|
|
|
|
|
|
check bellow to see how to get the ids.
|
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
### Displaying entries
|
|
|
|
|
2021-07-03 17:02:34 -05:00
|
|
|
At any point in time you can check your time spent in the current or other
|
|
|
|
timesheet with:
|
2021-07-03 16:44:31 -05:00
|
|
|
|
|
|
|
t display [options] [SHEET | all | full]
|
|
|
|
|
|
|
|
the available options are
|
|
|
|
|
|
|
|
-v, --ids Print database ids (for use with edit)
|
|
|
|
-s, --start <date:qs> Include entries that start on this date or later
|
|
|
|
-e, --end <date:qs> Include entries that start on this date or earlier
|
|
|
|
-f, --format <format> The output format. Valid built-in formats are
|
|
|
|
ical, csv, json, ids, factor, and text (default).
|
2021-07-03 17:04:19 -05:00
|
|
|
Check the docs on defining custom formats bellow.
|
2021-07-03 16:44:31 -05:00
|
|
|
-g, --grep <regexp> Include entries where the note matches this regexp
|
|
|
|
|
|
|
|
Some shortcuts available are:
|
|
|
|
|
2021-07-06 22:51:36 -05:00
|
|
|
`today` - Display entries that started today
|
2021-07-03 16:44:31 -05:00
|
|
|
|
|
|
|
t today [--ids] [--format FMT] [SHEET | all]
|
|
|
|
|
2021-07-06 22:51:36 -05:00
|
|
|
`yesterday` - Display entries that started yesterday
|
2021-07-03 16:44:31 -05:00
|
|
|
|
|
|
|
t yesterday [--ids] [--format FMT] [SHEET | all]
|
|
|
|
|
2021-07-03 16:48:51 -05:00
|
|
|
`week` - Entries of this week so far. The default start of the week is Monday
|
2021-07-03 16:44:31 -05:00
|
|
|
(configurable).
|
|
|
|
|
|
|
|
t week [--ids] [--end DATE] [--format FMT] [SHEET | all]
|
|
|
|
|
2021-07-06 22:51:36 -05:00
|
|
|
`month` - Entries of this month or a specified one.
|
2021-07-03 16:44:31 -05:00
|
|
|
|
|
|
|
t month [--ids] [--start MONTH] [--format FMT] [SHEET | all]
|
|
|
|
|
|
|
|
### Using different timesheets
|
|
|
|
|
|
|
|
You can organize your activities in different timesheets by first switching to
|
|
|
|
an existing one, then starting an activity:
|
|
|
|
|
|
|
|
t sheet somename
|
|
|
|
t in 'some activity'
|
|
|
|
|
|
|
|
which will also create the timesheet if it doesn't exist.
|
|
|
|
|
2021-07-03 17:02:34 -05:00
|
|
|
List all existing timesheets using
|
|
|
|
|
|
|
|
t list [all]
|
|
|
|
|
|
|
|
(defaults to not showing archive timesheets with names preceded by an
|
|
|
|
underscore)
|
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
### Advanced management
|
|
|
|
|
2021-07-03 17:02:34 -05:00
|
|
|
You can archive entries from a timesheet using:
|
|
|
|
|
|
|
|
t archive [--start DATE] [--end DATE] [SHEET]
|
|
|
|
|
|
|
|
which defaults to archiving all entries in the current sheet, or you can be more
|
|
|
|
specific using these options:
|
|
|
|
|
2022-08-03 08:54:25 -05:00
|
|
|
-s, --start <date:qs> Include entries that start on this date or later.
|
|
|
|
-e, --end <date:qs> Include entries that start on this date or earlier.
|
2021-07-03 17:02:34 -05:00
|
|
|
-g, --grep <regexp> Include entries where the note matches this regexp.
|
2022-08-03 08:54:25 -05:00
|
|
|
-t, --time <hours> Only archive up to `hours` hours.
|
2021-07-03 17:02:34 -05:00
|
|
|
|
|
|
|
This subcommand will move the selected entries to a hidden timesheet named
|
|
|
|
`_[SHEET]` (the name of the timesheet preceded by an underscore).
|
2021-07-03 16:44:31 -05:00
|
|
|
|
2021-07-03 17:02:34 -05:00
|
|
|
It is possible to access directly the sqlite database using
|
2021-07-03 16:44:31 -05:00
|
|
|
|
2021-07-03 17:02:34 -05:00
|
|
|
t backend
|
2021-07-03 16:44:31 -05:00
|
|
|
|
2021-08-14 11:30:20 -05:00
|
|
|
### Configuration
|
|
|
|
|
|
|
|
`tiempo` keeps a config file, whose location you can learn usign `t configure`.
|
|
|
|
It is also possible to edit the config file in-place passing arguments to
|
|
|
|
`t configure` like this:
|
|
|
|
|
|
|
|
t c --append-notes-delimiter ';'
|
|
|
|
|
|
|
|
it will print the resulting config file. Beware that it wont keep comments added
|
|
|
|
to the file.
|
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
## Specifying times
|
|
|
|
|
|
|
|
Some arguments accept a time as value, like `t in`'s `--at` or `t d --start`.
|
2021-07-13 17:53:30 -05:00
|
|
|
These are the accepted formats:
|
2021-07-03 16:44:31 -05:00
|
|
|
|
|
|
|
**Something similar to ISO format** will be parsed as a time in the computer's
|
|
|
|
timezone.
|
|
|
|
|
|
|
|
* `2021-01-13` a date
|
|
|
|
* `2019-05-03 11:13` a date with portions of a time
|
|
|
|
|
|
|
|
**ISO format with offset or UTC** will be parsed as a time in the specified
|
|
|
|
timezone. Use `Z` for `UTC` and an offset for everything else
|
|
|
|
|
|
|
|
* `2021-01-13Z`
|
|
|
|
* `2005-10-14 19:20:35+05:00`
|
|
|
|
|
|
|
|
**something that looks like an hour** will be parsed as a time in the current
|
|
|
|
day in the computer's timezone. Add `Z` or an offset to specify the timezone.
|
|
|
|
|
|
|
|
* `11:30`
|
|
|
|
* `23:50:45` (with seconds)
|
|
|
|
|
|
|
|
**some human times**, for now restricted to time ago:
|
|
|
|
|
|
|
|
* `an hour ago`
|
|
|
|
* `a minute ago`
|
|
|
|
* `50 min ago`
|
|
|
|
* `1h30m ago`
|
|
|
|
* `two hours thirty minutes ago`
|
|
|
|
|
2021-09-07 22:48:35 -05:00
|
|
|
## Custom formatters
|
|
|
|
|
|
|
|
You can implement your own formatters for all subcommands that display entries
|
|
|
|
(like `t display`, `t week` etc.). It is as easy as creating an executable file
|
|
|
|
written in any programming language (interpreted or compiled) and placing it in
|
|
|
|
a path listed in the config value for `formatter_search_paths`.
|
|
|
|
|
|
|
|
This executable will be given as standard input a csv stream with each row
|
|
|
|
representing a time entry with the same structure as the `csv` formatter output.
|
2021-09-07 22:57:21 -05:00
|
|
|
It will also be given a command line argument representing user settings for
|
|
|
|
this formatter stored in the config file and formatted as JSON.
|
2021-09-07 22:48:35 -05:00
|
|
|
|
|
|
|
### Example
|
|
|
|
|
|
|
|
Suppose we have this config file:
|
|
|
|
|
|
|
|
```toml
|
2021-09-07 22:57:21 -05:00
|
|
|
database_file = "/home/user/.config/tiempo/database.sqlite3"
|
2021-09-07 22:48:35 -05:00
|
|
|
round_in_seconds = 900
|
|
|
|
append_notes_delimiter = " "
|
|
|
|
formatter_search_paths = ["/home/user/.config/tiempo/formatters"]
|
|
|
|
default_formatter = "text"
|
|
|
|
auto_sheet = "dotfiles"
|
|
|
|
auto_sheet_search_paths = ["/home/user/.config/tiempo/auto_sheets"]
|
|
|
|
auto_checkout = false
|
|
|
|
require_note = true
|
|
|
|
week_start = "Monday"
|
|
|
|
|
|
|
|
[formatters.earnings]
|
|
|
|
hourly_rate = 300
|
|
|
|
currency = "USD"
|
|
|
|
```
|
|
|
|
|
|
|
|
then we can create the `earnings` formatter by placing the following file in
|
|
|
|
`/home/user/.config/tiempo/formatters/earnings`:
|
|
|
|
|
|
|
|
```python
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
|
|
import json
|
|
|
|
import csv
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from datetime import timedelta
|
|
|
|
from math import ceil
|
|
|
|
|
|
|
|
config = json.loads(sys.argv[1])
|
|
|
|
|
|
|
|
reader = csv.DictReader(
|
|
|
|
sys.stdin,
|
|
|
|
fieldnames=['id', 'start', 'end', 'note', 'sheet'],
|
|
|
|
)
|
|
|
|
|
|
|
|
total = timedelta(seconds=0)
|
|
|
|
|
|
|
|
for line in reader:
|
|
|
|
start = datetime.strptime(line['start'], '%Y-%m-%dT%H:%M:%S.%fZ')
|
2021-09-08 10:06:36 -05:00
|
|
|
|
|
|
|
if not line['end']:
|
|
|
|
end = datetime.utcnow()
|
|
|
|
else:
|
|
|
|
end = datetime.strptime(line['end'], '%Y-%m-%dT%H:%M:%S.%fZ')
|
2021-09-07 22:48:35 -05:00
|
|
|
|
|
|
|
total += end - start
|
|
|
|
|
2021-09-08 10:06:36 -05:00
|
|
|
hours = total.total_seconds() / 3600
|
2021-09-07 22:48:35 -05:00
|
|
|
earnings = hours * config['hourly_rate']
|
|
|
|
currency = config['currency']
|
|
|
|
|
2021-09-08 10:06:36 -05:00
|
|
|
print(f'You have earned: ${earnings:.2f} {currency}')
|
2021-09-07 22:48:35 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
Now if you run `t display -f earnings` you will get something like:
|
|
|
|
|
|
|
|
```
|
|
|
|
You have earned: 2400 USD
|
|
|
|
```
|
|
|
|
|
2021-07-16 13:13:18 -05:00
|
|
|
## Why did you write this instead of improving timetrap?
|
|
|
|
|
2021-08-14 11:30:45 -05:00
|
|
|
* timetrap is [hard to install](https://github.com/samg/timetrap/issues/176),
|
|
|
|
hard to keep [updated](https://github.com/samg/timetrap/issues/174) (because
|
2022-08-03 08:53:52 -05:00
|
|
|
of ruby). With tiempo you can get (or build) a binary, put it somewhere in
|
|
|
|
your `PATH`, and it will just work forever in that machine. I'm bundling
|
|
|
|
sqlite.
|
2021-07-16 13:13:18 -05:00
|
|
|
* timetrap is slow (no way around it, because of ruby), some commands take up to
|
|
|
|
a second. Tiempo always feels snappy.
|
|
|
|
* needed major refactor to fix the timezone problem (in a language I'm not
|
|
|
|
proficient with). I was aware of this problem and designed tiempo to store
|
|
|
|
timestamps in UTC while at the same time being able to work with a database
|
2021-08-14 11:30:45 -05:00
|
|
|
made by timetrap without messing up. And there are a lot of tests backing this
|
|
|
|
assertions.
|
2021-07-16 13:13:18 -05:00
|
|
|
|
|
|
|
### Other advantages
|
|
|
|
|
2021-08-14 11:30:45 -05:00
|
|
|
* Columns in the output are always aligned.
|
|
|
|
* Fixed some input inconsistencies.
|
2022-08-03 08:53:52 -05:00
|
|
|
* CLI interface is easier to discover (ask -h for any sub-command)
|
|
|
|
* End times are printed with +1d to indicate that the activity ended the next
|
2021-08-14 11:30:45 -05:00
|
|
|
day in the 'text' formatter.
|
2022-08-03 08:53:52 -05:00
|
|
|
* Solved some old issues in timetrap.
|
|
|
|
* Added new features!
|
2021-07-16 13:13:18 -05:00
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
## How to build
|
2021-06-27 23:20:37 -05:00
|
|
|
|
|
|
|
You need [rust](https://rustup.rs), then clone the repo and simply run
|
|
|
|
|
|
|
|
cargo test
|
|
|
|
|
|
|
|
to check that everything is working, and then
|
|
|
|
|
|
|
|
cargo build --release
|
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
to have a binary at `target/release/t` that you can then move to a
|
|
|
|
directory in your `PATH` or use it by its absoulte or relative paths.
|
2021-06-27 23:20:37 -05:00
|
|
|
|
|
|
|
Run
|
|
|
|
|
2021-07-03 16:44:31 -05:00
|
|
|
t --help
|
2021-06-27 23:20:37 -05:00
|
|
|
|
|
|
|
to see the options.
|
2021-07-21 19:07:05 -05:00
|
|
|
|
2021-08-11 20:25:21 -05:00
|
|
|
### Development database
|
|
|
|
|
|
|
|
When developing I prefer not to mess with my own database, so I use this `.env`
|
|
|
|
file:
|
|
|
|
|
|
|
|
export TIMETRAP_CONFIG_FILE=/absolute/path/to/repo/dev_config.toml
|
|
|
|
PS1="$ "
|
|
|
|
|
|
|
|
and when I want to test some commands against such config file I just source it:
|
|
|
|
|
|
|
|
source .env
|
|
|
|
cargo run -- in 'hola'
|
|
|
|
|
2021-10-26 20:29:18 -05:00
|
|
|
### Documentation
|
|
|
|
|
|
|
|
The docs are written using [sphinx](https://www.sphinx-doc.org/en/master/), so
|
|
|
|
first you need to install it somehow. Two options I can offer are:
|
|
|
|
|
|
|
|
* using your computer's package manager. Install a package with a name similar
|
|
|
|
to `python-sphinx`.
|
|
|
|
* using [pipenv](https://duckduckgo.com/?t=ffab&q=pipenv&ia=web). Just ensure
|
|
|
|
you have python 3.9 on your computer, enter the `docs` directory and do
|
|
|
|
`pipenv install`.
|
|
|
|
|
|
|
|
To build the docs just enter the `docs` directory and some of the language
|
|
|
|
directories (currently `es` or `en`) and run:
|
|
|
|
|
|
|
|
make html
|
|
|
|
|
|
|
|
for the html version (output located at `docs/<lang>/build/html`), or
|
|
|
|
|
|
|
|
make man
|
|
|
|
|
|
|
|
for the man page (output located at `docs/<lang>/build/man/tiempo.1`). If you
|
|
|
|
are using pipenv just prefix the commands with `pipenv run` or run `pipenv
|
|
|
|
shell` before running any command.
|
|
|
|
|
2021-10-26 20:32:59 -05:00
|
|
|
The contents of the docs are located in `docs/<lang>/source/index.rst`,
|
|
|
|
formatted as
|
|
|
|
[reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html).
|
|
|
|
|
2021-07-21 19:07:05 -05:00
|
|
|
## Special Thanks
|
|
|
|
|
2021-10-26 20:29:18 -05:00
|
|
|
To [timetrap](https://github.com/samg/timetrap) for existing, to
|
|
|
|
[samg](https://github.com/samg) for creating it. It is the tool I was looking
|
|
|
|
for and whose design I took as reference, keeping compatibility when possible.
|
2022-08-03 08:53:52 -05:00
|
|
|
|
|
|
|
## Release checklist
|
|
|
|
|
|
|
|
(mostly to remind myself)
|
|
|
|
|
|
|
|
* [ ] Ensure tests pass and that clippy doesn't complain
|
2022-08-04 06:00:40 -05:00
|
|
|
* [ ] Create an entry in `CHANGELOG.md` with the target version, stage it
|
2022-08-03 08:53:52 -05:00
|
|
|
* [ ] run `vbump`
|
|
|
|
* [ ] push commits and tags
|
|
|
|
* [ ] wait for release
|