eztoggl
Table of contents
About
eztoggl is a command-line tool that aims to simplify your monthly Toggl workflow. It's basically a refactor of toggl2gdrive with a couple of extra features.
With the press of a button, eztoggl can:
- generate both summary and detailed Toggl Track PDF reports for last month
- generate a PDF invoice using Fakturoid
- save generated files to local storage
- upload generated files to Google Drive
NOTE: Google Drive integration only works with accounts in the *@applifting.cz
domain because the Google app whose credentials are specified in credentials.json
is limited to internal use. If you'd like to use it for other accounts, you'll have to create your own Google Cloud Platform project, then create OAuth client ID credentials here, download them and replace the existing credentials.json
. When prompted for Application type, select Web application and add http://localhost:3000
to the list of Authorized redirect URIs. I might go through the app verification process with Google in the future to make Google Drive integration available to everyone out-of-the-box.
Installation
There are 3 main options to install eztoggl.
Using go install
go install gitlab.com/jiri.hajek/eztoggl/cmd/eztoggl@latest
Clone and build
Clone the repository and build the project yourself. The executable will be saved to a directory called dist
.
git clone https://gitlab.com/jiri.hajek/eztoggl.git
cd eztoggl
make
Binary release
If you don't want to build the app yourself, you can grab the latest binary from releases.
Usage
Usage: eztoggl [-hiIrRsuv] [-o DIRECTORY] [parameters ...]
-h, --help show help
-i, --invoice generate a Fakturoid invoice
-I, --upload-invoice
upload invoice to Google Drive
-o DIRECTORY local output directory for generated files [.]
-r, --reports generate Toggl reports
-R, --upload-reports
upload Toggl reports to Google Drive
-s, --save save generated files
-u, --upload upload all generated files to Google Drive
-v, --version print version
For even more convenience, you can set up eztoggl to automatically run each month with something like cron or anacron.
# crontab
# Upload Toggl reports and Fakturoid invoice to Google Drive on the 1st day of each month at 6 AM
0 6 1 * * /path/to/eztoggl -iru
If you decide to upload your files to Google Drive, you will be prompted for authorization the first time run eztoggl with the -u
/ --upload
flag. The app will automatically open a browser tab with the Google authorization flow for you. Once you give eztoggl the necessary permissions, you will be redirect to a local web server running in the background, which will automatically collect the authorization code for you. The generated OAuth2 tokens will be saved to token.json
inside your config directory.
As long as token.json
exists, eztoggl will not prompt you for authorization and will instead refresh the existing token as needed. Keep in mind that the app only receives your refresh token on the first authorization. Therefore, if you lose your token.json
, you'll have to:
- remove the app's access from your Google account's security settings
- go through the Google authorization flow again
If you skip step 1, your tokens will only lasts for 1 hour and eztoggl won't be able to refresh them.
Configuration
Create a config.json
file based on the configuration example in config.example.json
.
config.json
is separated into 3 main sections:
fakturoid
gdrive
toggl
You can remove the sections you don't intend to use, e.g. if you don't want to upload files to Google Drive, you can remove the gdrive
section.
Config directory
eztoggl looks for config.json
in the following directories, in this specific order:
- current directory
(config directory)/eztoggl
as per os.UserConfigDir()
- Linux:
$XDG_CONFIG_HOME/eztoggl
if $XDG_CONFIG_HOME
is defined, else $HOME/.config/eztoggl
- macOS:
$HOME/Library/Application Support/eztoggl
- Windows:
%AppData%\eztoggl
(home directory)/.eztoggl
as per os.UserHomeDir()
- Linux:
$HOME/.eztoggl
- macOS:
$HOME/.eztoggl
- Windows:
%USERPROFILE%\.eztoggl
The first encountered directory that contains config.json
will also be used for credentials.json
and token.json
.
Fakturoid slug
To find out what your Fakturoid slug is, go to https://app.fakturoid.cz and log in. You will then be redirected to the Fakturoid dashboard and your slug will be in the URL - https://app.fakturoid.cz/yourslugishere/dashboard
.
For example, in my case the URL is https://app.fakturoid.cz/jirihajek1/dashboard
so my slug is jirihajek1
.
Structure of config.json
Key |
Type |
Description |
fakturoid |
object |
Fakturoid configuration, can be omitted if you don't intend to generate an invoice |
fakturoid.api_token |
string |
Fakturoid API token |
fakturoid.email |
string |
Fakturoid email |
fakturoid.slug |
string |
Fakturoid slug, see Fakturoid slug for more information |
fakturoid.invoice |
object |
Fakturoid invoice configuration |
fakturoid.invoice.prefix_year_and_month |
bool |
Prefix invoice with year and month (e.g. file.pdf becomes YY_MM_file.pdf ) |
fakturoid.invoice.filename |
string |
Invoice filename (including the .pdf extension) |
fakturoid.invoice.bank_account_name |
string |
Name of the Fakturoid bank account to use |
fakturoid.invoice.client_name |
string |
Name of the Fakturoid client to invoice |
fakturoid.invoice.currency |
string |
Currency to be used in the invoice (3letter code) |
fakturoid.invoice.days_until_due |
int |
Amount of days until the due date |
fakturoid.invoice.default_hourly_rate |
int |
Default hourly rate |
fakturoid.invoice.default_project_name |
string |
Default project name used in the invoice |
fakturoid.invoice.unit_name |
string |
Unit name to be used for invoice items, e.g. h , hours or an empty string |
fakturoid.invoice.vat_rate |
int |
VAT rate, usually either 0 or 21 |
fakturoid.invoice.projects |
object |
Object that maps Toggl project names to names and hourly rates that should be used in the invoice |
fakturoid.invoice.projects[KEY].name |
string |
Name that should be used in the invoice for the given project |
fakturoid.invoice.projects[KEY].hourly_rate |
int |
Hourly rate that should be used in the invoice for the given project |
gdrive |
object |
Google Drive configuration, can be omitted if you don't intend to upload files to Google Drive |
gdrive.reports_folder_name |
string |
Name of the Google Drive folder where your Toggl reports should be uploaded |
gdrive.invoice_folder_name |
string |
Name of the Google Drive folder where your invoice should be uploaded |
toggl |
object |
Toggl configuration, cannot be omitted because the Toggl API will always be used |
toggl.api_token |
string |
Toggl API token |
toggl.workspace_name |
string |
Name of the Toggl workspace to generate reports for |
toggl.client_name |
string |
Name of the Toggl client to generate reports for |
toggl.reports |
object |
Toggl reports configuration |
toggl.reports.prefix_year_and_month |
bool |
Prefix reports with year and month (e.g. file.pdf becomes YY_MM_file.pdf ) |
toggl.reports.detailed_report_filename |
string |
Detailed report filename (including the .pdf extension) |
toggl.reports.detailed_report_filename |
string |
Summary report filename (including the .pdf extension) |
Invoice item names and hourly rates
If there is a project in your Toggl summary that is not present in config.json
, eztoggl will fall back to invoice item name from:
fakturoid.invoice.default_project_name
if configured
- the project's name from Toggl if the default is not configured
If you don't set a project's hourly rate, eztoggl will fall back to fakturoid.invoice.default_hourly_rate
.
Public packages
I tried to separate the main logic into packages so that others could reuse it in their projects. You might find these useful if you're implementing something similar.
Google Drive permissions
If you decide to use the built-in Google Drive integration, eztoggl will ask you for 2 scopes of the Google Drive API v3, specifically drive.metadata.readonly
and drive.file
. All scopes along with a short description can be found here.
drive.metadata.readonly
grants the app read-only access to metadata of files in your Google Drive. It is needed to find the folders where your files should be uploaded.
drive.file
allows the app to view and manage Google Drive files and folders that have been opened or created by the app. It is needed to upload your files.