Add options to "guess" unknown tags

This will be the default option so that the script "just
works" without configuration. Also, the config setting
"skip_unknown_tags" has been changed to "unknown_tags" to
allow choosing among multiple behaviours.
This commit is contained in:
Badri Sunderarajan 2024-08-09 14:58:25 +05:30
parent 031c654e09
commit bde5c22636
Signed by: badrihippo
GPG key ID: 9F594532AD60DE03
2 changed files with 108 additions and 43 deletions

View file

@ -12,13 +12,7 @@ tools like [ledger](https://ledger-cli.org) and
extensions directory (usually something like
`~/.timewarrior/extensions` and make sure that it's
executable.
2. Update your configuration:
```sh
timew config ext.timewclock.accounts 'work = Expenses:Time:Work; play = Expenses:Time:Play; test = Expenses:Other'
```
Add your own Timewarrior tags instead of `work`,
`play`, etc.
3. Run `timew timeclock` to generate your report! You
2. Run `timew timeclock` to generate your report! You
can add any filters you want, like `timew timeclock
today`
@ -31,8 +25,8 @@ added to `timewarrior.cfg` or using the command
`timewarrior config SETTING_NAME VALUE`
The configuration variables for this script are
described below. You only need to set the first one
to get started, the others are optional.
described below.
### `ext.timeclock.accounts`
@ -56,40 +50,72 @@ will be stripped automatically before parsing. Note
that this means tags with spaces at the end like
` play` will not be supported.
**Default:** empty (needs to be set before use)
If a timewarrior entry has multiple tags, this srcript
will attempt to match against each one. If none of the
tags match, the behaviour specified by
`ext.timeclock.unknown_tags` will be used.
**Default:** empty
### `ext.timeclock.unknown_tags`
Define how to handle unknown tags.
This setting defines how to handle tags that are not
mapped to accounts in the `ext.timeclock.accounts`
setting defined above. The default behaviour is to
"guess" the unknown tag, i.e. use the tag name as
the account name. This way you can just use whatever
account names you want when tracking time with
Timewarrior, and it'll automatically be passed on to
your timeclock file.
There are four possible settings in total:
- **`guess`** - "guess" the account name based on the tag
name (in other words, take the first tag and use that
as the account name). If the record is untagged, the
default tag (from `ext.timeclock.default_tag`,
described below) is used instead. If no default tag
is set, an error is thrown.
- **`default`** - use a "default" tag from the
`ext.timeclock.default_account` setting when a record
with no known tags is found. This default tag is also
used for untagged records. Note that "default" doesn't
mean this is the default setting; it just describes
what account name is to be used!
- **`fail`** - fail immediately whenever an unknown
tag or a record with no tags is encountered. This
is the strictest behaviour and forces you to either
define the tags or tell Timewarrior to filter them
out.
- **`ignore`** - silently ignore records where the tags
are blank or the account names can't be matched. This
is the most permissive, but you should be careful
while using this to make sure you don't accidentally
miss out on some records! When possible, try to use
some other setting and choose which records you want
using timewarrior filters instead.
**Default:** guess
### `ext.timeclock.default_account`
A default account to set records to if a tag mapping
is not specified.
A default account to set records to if a record is
untagged or if a tag mapping is not specified.
If no tag mapping is found for a specific record, this
default account will be used instead. Note that if a
record has multiple tags, the script will check them
one by one until it finds a matching tag, and only
give up if all the unknown tags fail to match.
This setting is used in one of the following situations:
**Default:** empty
- an untagged record comes up, and
`ext.timeclock.unknown_tags` is set to `guess` or
`default`
- no matching tag is found, and
`ext.timeclock.unknown_tags` is set to `default`
### `ext.timeclock.skip_unknown_tags`
**Default:** empty (needs to be set)
Silently skip records with unknown tags.
The default behaviour is to abort with an error when
an unknown tag (i.e. a tag not mapped in the
`ext.timeclock.accounts setting` above) is encountered.
With this setting, records for the unknown tags will
be skipped silently. Note that this means not all
Timewarrior records will be exported; a better way to
do this would be to set appropriate filters in the
Timewarrior command itself.
This command overrides `ext.timeclock.default_account`
**Default:** false
## Contributing

View file

@ -24,12 +24,14 @@
# General Public License along with this program.
# If not, see <https://www.gnu.org/licenses/>.
use v5.14;
use experimental qw( switch );
use JSON;
# Process the headers
my %tag_for_account;
my $default_account;
my $skip_unknown_tags;
my $unknown_tags;
while(<>) {
last if /^\n/;
@ -54,19 +56,35 @@ while(<>) {
$default_account = $1;
}
if (/^ext\.timeclock\.skip_unknown_tags: (.*)$/) {
$skip_unknown_tags = $1;
if (/^ext\.timeclock\.unknown_tags: (.*)$/) {
$unknown_tags = do {
given($1) {
"default" when /d(efault)?$/i;
"fail" when /f(alse)?$/i;
"fail" when /f(ail)?$/i;
"fail" when /no?$/i;
"ignore" when /t(rue)?$/i;
"ignore" when /y(es)?$/i;
"guess" when /g(uess(Instead)?)?$/i;
default {
die "Error: invalid option '$_' for setting ext.timeclock.unknown_tags. Should be one of 'fail', 'guess', 'default', or 'ignore'.";
}
}
};
}
}
# If tags are not set, exit with an error message
if (!keys %tag_for_account) {
# If guessing tag names in disabled but tags are not
# set, exit with an error message
if (!keys %tag_for_account && $unknown_tags ne "guess") {
print STDERR <<HELPTEXT;
Error: guessing is disabled but no tag-to-account mappings were found! To use this script, please specify some tags in your timewarrior config file, under the setting `ext.timeclock.accounts`. The format for this setting is:
TAG1=ACCOUNT1;TAG1=ACCOUNT2;...
...where tags are the timewarrior tags and accounts are the accounts to be used for the timeclock output. You can put spaces around the semicolor (;) and equals (=) signs; those will be ignored before processing.
Alternatively, you can set 'ext.timeclock.unknown_tags` to `guess`.
HELPTEXT
exit 1;
}
@ -124,10 +142,31 @@ sub decide_account {
}
}
# Set a default account if needed
if (!$account && !$skip_unknown_tags) {
$account = $default_account
or die "Error: No matching account found for '@tags' and no default tag was set. Try setting ext.timeclock.default_tag or ext.timeclock.skip_unknown_tags to get around this error.\n";
# Handle unknown tags
if (!$account) {
$account = do {
given ($unknown_tags) {
when("default") {
$default_account
or die "Error: No matching account found for '@tags' and no default tag was set. Try setting ext.timeclock.default_tag or ext.timeclock.unknown_tags to get around this error.\n";
}
when ("guess") {
# Return the first tag we find
shift @tags;
}
when ("ignore") {
return;
}
default {
die "Error: No matching account found for '@tags'. Try adding it to ext.timeclock.accounts or set ext.timeclock.unknown_tags to change handling behaviour.\n"
}
}
};
}
return $account;