mirror of
https://github.com/GothenburgBitFactory/timewarrior.git
synced 2025-06-26 10:54:28 +02:00
325 lines
14 KiB
Text
325 lines
14 KiB
Text
Timewarrior
|
||
===========
|
||
|
||
Tracking time is complex, and requires more than simply a set of start/stop
|
||
timestamps. There are daily schedules, breaks, meetings, holidays, and
|
||
vacations to consider.
|
||
|
||
For a tool to be useful, it must support all the above, and make the task of
|
||
tracking time simple.
|
||
|
||
|
||
Goals
|
||
-----
|
||
|
||
Provide a personal tool to easily track time spent and generate status reports
|
||
in multiple formats and styles. The tool will "do the right thing" as much as
|
||
possible. Tool will be Open Source.
|
||
|
||
|
||
Non-Goals
|
||
---------
|
||
- No cloud support.
|
||
- No multi-user support.
|
||
- No sync support.
|
||
- No precision higher than a minute.
|
||
|
||
|
||
Integration
|
||
-----------
|
||
- As a sister-product to Taskwarrior and Tasksh, integration should be as
|
||
simple as installing a hook script.
|
||
- Software may invoke timew for tracking, but timew will not invoke any other
|
||
software.
|
||
- An export and import feature, using JSON, will allow further integration.
|
||
|
||
|
||
Definitions
|
||
-----------
|
||
- A timeline is a continuum that is the one-dimensional space that timestamps
|
||
and intervals map onto.
|
||
- A timmestamp is a point on a timeline.
|
||
- An interval is a span of time on a timeline, consisting of two timestamps.
|
||
- A tag is an arbitrary string.
|
||
- An interval may have any number of tags.
|
||
- An timestamp may have any number of tags.
|
||
- An interval may be an inclusion or an exclusion. A weekend is an exclusion,
|
||
time spent working is an inclusion.
|
||
- A workweek is defined by time that is not spent working, it is a set of
|
||
excluded intervals. Those intervals are tagged to be easily identifiable.
|
||
- Lunch could be an inclusion, but tagged as non-work. It could also be an
|
||
exclusion.
|
||
- An interval set is a set of tags that, when queried, results in the desired
|
||
subset of all intervals. For example, 'home' and 'painting' are tags that
|
||
represent all the tracked time with those tags. The interval set may be the
|
||
empty set.
|
||
- The time resolution is 1 minute.
|
||
|
||
|
||
Rule System Use Cases
|
||
---------------------
|
||
|
||
A rule system should be able to evaluate conditions and perform actions, which
|
||
include continuing to process rules, or stopping. The conditions will need to
|
||
access configuration, exclusions, and tracking data.
|
||
|
||
- A constraint on total tracking time, "do not spend more than 4 hours on x".
|
||
- A constraint that only allows certain tags to be used together, to prevent
|
||
client 1 from being billed for project 2.
|
||
- A constraint that only allows multiples of 15-minute intervals.
|
||
|
||
Ref: http://martinfowler.com/bliki/RulesEngine.html
|
||
|
||
Example rules (assuming Python-like syntax):
|
||
|
||
define rule workweek:
|
||
holidays eng-USA
|
||
workweek mon,tue,wed,thu,fri
|
||
workday start 510
|
||
workday end 1050
|
||
workday tue end 900
|
||
|
||
define rule my_rule:
|
||
total_hours = sum_week +tag1
|
||
if total_hours > 12:
|
||
fail "The 'tag1' tag cannot exceed 12 hours/wk"
|
||
|
||
(needs work)
|
||
|
||
|
||
CLI Syntax
|
||
----------
|
||
The CLI shall have a well-defined and unambiguous grammar.
|
||
|
||
As timew will use tags, by default we assume the same convention as Taskwarrior,
|
||
namely:
|
||
|
||
+tag Implies the presence of a tag
|
||
-tag Implies the absence of a tag
|
||
|
||
Timestamp values should support a wide range of unambiguous formats, including:
|
||
|
||
today
|
||
9am
|
||
today 9am
|
||
0900
|
||
9:00
|
||
2015-12-02T09:00
|
||
2nd 4pm
|
||
|
||
Interval values should support a wide range of unambiguous formats, including:
|
||
|
||
<timestamp> - <timestamp>
|
||
<timestamp> to <timestamp>
|
||
from <timestamp> to <timestamp>
|
||
until <timestamp>
|
||
from <timestamp>
|
||
<timestamp> for <duration>
|
||
|
||
|
||
Macros
|
||
------
|
||
A macro is a keyword that can take arguments, and expands to a set of one or
|
||
more other timew commands, with some dynamic aspect. For example, the macro
|
||
named 'staff meeting' could be an interval that is on Wednesdays at 10:00, and
|
||
has a set of associated tags. This would then create an appropriately tagged
|
||
interval.
|
||
|
||
|
||
General Use Cases
|
||
-----------------
|
||
Suppose 'today' is 2015-11-24, a Tuesday at noon. This date is chosen because
|
||
it is mid-week, a holiday is coming up, and workday exceptions are typical.
|
||
Here is the timeline, with the successive exclusions added, as a work week is
|
||
defined:
|
||
|
||
2015
|
||
November December
|
||
W48 W49
|
||
23rd 24th 25th 26th 27th 28th 29th 30th 1st
|
||
Monday Tuesday Wednesday Thursday Friday Saturday Sunday Monday Tuesday
|
||
: : : : : : : : : :
|
||
:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:
|
||
0 6 12 18 0 6 12 18 0 6 12 18 0 6 12 18 0 6 12 18 0 6 12 18 0 6 12 18 0 6 12 18 0 6 12 18 0
|
||
|
|
||
| ------------ $ timew holidays eng-USA
|
||
| ------------------------ $ timew define workweek mon-fri
|
||
---- ---- | ---- ---- ---- ---- ---- ---- ---- $ timew define workday start 8:30am
|
||
--- | --- --- --- --- --- --- --- --- $ timew define workday end 1730
|
||
| ---- $ timew define workday tue end 3pm
|
||
|
|
||
+++++ ++++ +++++ +++++ +++++ +++++ Resulting work week
|
||
|
|
||
now
|
||
|
||
On that same timeline, here are some example tracked intervals:
|
||
|
||
|
|
||
+++++ tag1 tag2 $ timew track +tag1 +tag2 yesterday
|
||
++++ tag1 tag2 $ timew track +tag1 +tag2 today
|
||
+ tag1 tag2 $ timew start +tag1 +tag2
|
||
+++ tag1 tag2 $ timew start +tag1 +tag2 backfill
|
||
+ tag3 $ timew track +tag3 yesterday 2pm - 4pm
|
||
+ tag4 $ timew track -tag3 +tag4 yesterday 2pm - 4pm
|
||
|
|
||
|
|
||
now
|
||
|
||
Here is the example data storage for the above workweek:
|
||
|
||
define rule workweek:
|
||
holidays eng-USA
|
||
workweek mon,tue,wed,thu,fri
|
||
workday start 510
|
||
workday end 1050
|
||
workday tue end 900
|
||
|
||
Time tracking is based on the inclusion intervals derived from the above rule.
|
||
For week 48 those would be:
|
||
|
||
2015-11-23 510-1050
|
||
2015-11-24 510-900
|
||
2015-11-25 510-1050
|
||
2015-11-27 510-1050
|
||
|
||
Given these intervals, tracking becomes a set of basic modifications:
|
||
|
||
The date 'yesterday' refers to the whole day, and overrides any other
|
||
intervals:
|
||
|
||
$ timew track +tag1 +tag2 yesterday
|
||
|
||
--> 'yesterday' resolves to 2015-11-23
|
||
--> delete all intervals for 2015-11-23
|
||
--> obtain inclusions for 2015-11-23: 510-1050
|
||
--> store: 2015-11-23 510-1050 tag1 tag2
|
||
|
||
The date 'today' refers to the whole day, and overrides any other intervals:
|
||
|
||
$ timew track +tag1 +tag2 today
|
||
|
||
--> 'today' resolves to 2015-11-24
|
||
--> delete all intervals for 2015-11-24
|
||
--> obtain inclusions for 2015-11-24: 510-900
|
||
--> store: 2015-11-24 510-900 tag1 tag2
|
||
|
||
The 'start' command uses 'now' (12:00pm) in this alternate example:
|
||
|
||
$ timew start +tag1 +tag2
|
||
|
||
--> 'now' resolves to 2015-11-24T12:00
|
||
--> obtain inclusions for 2015-11-24: 510-900
|
||
--> store: 2015-11-24 720- tag1 tag2
|
||
Note the open-ended interval
|
||
|
||
The 'start' recognizes 'backfill' to indicate that earlier, untracked
|
||
inclusions should be tracked:
|
||
|
||
$ timew start +tag1 +tag2 backfill
|
||
|
||
--> 'now' resolves to 2015-11-24T12:00
|
||
--> obtain inclusions for 2015-11-24: 510-900
|
||
--> store: 2015-11-24 510- tag1 tag2
|
||
Note the open-ended, back-dated interval
|
||
|
||
Inserting tracked time into an existing set of intervals:
|
||
|
||
$ timew track +tag3 yesterday 2pm - 4pm
|
||
|
||
--> 'yesterday' resolves to 2015-11-23
|
||
--> '2pm' - '4pm' resolves to 840-960
|
||
--> obtain inclusions for 2015-11-23: 510-1050
|
||
--> store: 2015-11-23 840-960 tag3
|
||
|
||
Change tagging for an interval:
|
||
|
||
$ timew track -tag3 +tag4 yesterday 2pm - 4pm
|
||
|
||
TBD
|
||
|
||
|
||
Not shown:
|
||
- $ timew holidays work 2015-11-26 # override the holiday
|
||
|
||
|
||
Data Format
|
||
-----------
|
||
- The storage format should be all text files.
|
||
- An example storage format for a work week:
|
||
-
|
||
- An example storage format as a starting point, in compact form. First line is
|
||
a full day, no break from 8am - 5pm. Second line is the same with an untracked
|
||
lunch break:
|
||
2015-12-01 480-1020 tag1 tag2
|
||
2015-12-02 480-720 tag1 tag2, 780-1020 tag1 tag2
|
||
- Same in JSON:
|
||
[
|
||
{"day":"2015-12-01","intervals":[{"start":480,"end":1020,"tags":["tag1","tag2"]}]}
|
||
{"day":"2015-12-02","intervals":[{"start":480,"end":720,"tags":["tag1","tag2"]},{"start":780,"end":1020,"tags":["tag1","tag2"]]}
|
||
]
|
||
- If the user runs "timew track tag1 tag2 yesterday", then the "yesterday" is
|
||
resolved against the set of exclusions to determine its boundaries, and record
|
||
as a set of matching intervals. A later change to the work start time will
|
||
not be reflected in already tracked time.
|
||
- Tags may need to be quoted, to allow spaces.
|
||
|
||
|
||
Reporting
|
||
---------
|
||
- Reports should have multiple output formats: TTY, JSON, HTML.
|
||
-
|
||
|
||
|
||
Unsolved Issues
|
||
---------------
|
||
- Is a tag a single word, or a KV pair? Why? If tags are quoted strings, then
|
||
there will be no issues with folks wanting to use actual project names.
|
||
- Multiple timelines?
|
||
- Terminology for backwards and forwards tracking (backfill for the past, and
|
||
just assume everythign else is for the future)?
|
||
- Given this:
|
||
$ timew track yesterday 9am - 5pm tag1
|
||
What does this do:
|
||
$ timew track yesterday 10am - 11am tag2
|
||
Does it yield:
|
||
a) yesterday 540-1020 tag, 600-660 tag2 # overlap
|
||
b) yesterday 540-600 tag1, 600-660 tag2, 660-1020 tag1 # no overlap
|
||
c) yesterday 540-600 tag1, 600-660 tag1 tag2, 660-1020 tag1 # explicit overlap
|
||
Stating 'track yesterday 10am - 11am tag2' sounds imperative, thus overriding
|
||
any existing tags in that interval. Perhaps a 'merge' keyword could be added
|
||
to combined the results (track yesterday 10am - 11am tag2 merge)?
|
||
|
||
|
||
Raw Notes (Slack)
|
||
-----------------
|
||
|
||
F: start (frontfill) and backfill (end) could be considered orthogonal to each other. They just operate into different directions. but I think they behave the same, more or less.
|
||
|
||
F: Which brings me to the topic of overlapping intervals. While most of things would not be multitasking/overlapping. I could think of a few things that could be reported overlapping.
|
||
P: Agreed, I can multitask and be pursuing two results at the same time.
|
||
|
||
P: I can do all these [manipulations] with my finger. But not at the CLI. That means lists, and ID numbers, I think. Any thoughts?
|
||
F: List… Hm. Don’t we already have one implicitly with the timeline and timestamps?
|
||
P: Yes, but you’d need to view it, in order to identify which block should be manipulated.
|
||
F: Can’t that list not be a list of key/value pairs? The keys are the minutes of the day, the values the different tags (another list of key/values).
|
||
F: Then you just traverse the minutes in either direction.
|
||
F: If you want to extend an event just look at the minutes after it and change the stored tags...
|
||
|
||
P: You said earlier about time points, like monitoring systems. If I jsut drop a point at 8:55, then it could merge it with the 9:00 point because the tags match.
|
||
F: OTOH the command at 9:50 could also mean that the first block should go from 8:55 to 9:40. It was originally 45 minutes long. Should it now also be 45 minutes. Or do you want to extend it with 5mins from 8:55 to 9:45?
|
||
P: Your question above: should we move the block intact, or stretch it? I think that’s an important flexibility we need. so both.
|
||
|
||
F: I am thinking if that list would need to contain 1440 entries or if it could be smaller.
|
||
F: Or if we just store less elements in it from the beginning and then just populate them as we add events to the day.
|
||
F: If the list is fully populated then querying and reporting is just traversing and adding per resolution. No fancy time calculations.
|
||
F: Just count how many minute entries you have for a tag combination.
|
||
F: Would that be slow for a week or month?
|
||
P: So initially we define our work week, which to me involves about 8 blocks of time, which is just 16 time points. So that’s nothing.
|
||
P: Well, I can represent an array of 1440 slots very efficiently: 1-1440 tag1
|
||
P: Then you add a block, and it gets bisected: 1-300 tag1, 301-800 tag2, 801-1440 tag1
|
||
F: Exactly.
|
||
P: So with 10 separately billable chunks of time recorded, there would be 11 blocks, and the 8 that define the week. Trivial.
|
||
F: And their length is y-x.
|
||
P: Even easier then: 1 tag1, 301 tag2, 801 tag1
|
||
|
||
--
|