1192 lines
38 KiB
Plaintext
1192 lines
38 KiB
Plaintext
\input texinfo @c -*- texinfo -*-
|
|
@c %**start of header
|
|
@setfilename manual.info
|
|
@settitle The Chronometrist Manual
|
|
@documentencoding UTF-8
|
|
@documentlanguage en
|
|
@c %**end of header
|
|
|
|
@finalout
|
|
@titlepage
|
|
@title The Chronometrist Manual
|
|
@author contrapunctus
|
|
@end titlepage
|
|
|
|
@contents
|
|
|
|
@ifnottex
|
|
@node Top
|
|
@top The Chronometrist Manual
|
|
|
|
The structure of this manual was inspired by @uref{https://documentation.divio.com/}
|
|
@end ifnottex
|
|
|
|
@menu
|
|
* How to@dots{}:: Step-by-step guides to achieve specific tasks
|
|
* Explanation:: The design, the implementation, and a little history
|
|
* Reference:: A list of definitions, with some type information
|
|
|
|
@detailmenu
|
|
--- The Detailed Node Listing ---
|
|
|
|
How to@dots{}
|
|
|
|
* How to set up Emacs to contribute::
|
|
|
|
Explanation
|
|
|
|
* Design goals:: Some vague objectives which guided the project
|
|
* Terminology:: Explanation of some terms used later
|
|
* Project overview:: A broad overview of the code
|
|
* Chronometrist:: The primary command and its associated buffer.
|
|
* Midnight-spanning events:: Events starting on one day and ending on another
|
|
* Point restore behaviour:: The desired behaviour of point in Chronometrist
|
|
* chronometrist-report date range logic:: Deriving dates in the current week
|
|
* Tags and Key-Values:: How tags and key-values are implemented
|
|
|
|
Chronometrist
|
|
|
|
* Optimization::
|
|
|
|
Midnight-spanning events
|
|
|
|
* Check the code of the first event of the day (timeclock format):: When the code of the first event in the day is "o", it's a midnight-spanning event.
|
|
* Split them at the file level::
|
|
* Split them at the hash-table-level::
|
|
* Split them at the data-consumer level (e.g. when calculating time for one day/getting events for one day): Split them at the data-consumer level (eg when calculating time for one day/getting events for one day).
|
|
|
|
Tags and Key-Values
|
|
|
|
* User input::
|
|
* History::
|
|
|
|
Reference
|
|
|
|
* Legend of currently-used time formats::
|
|
* chronometrist-common.el: chronometrist-commonel.
|
|
* chronometrist-custom.el: chronometrist-customel.
|
|
* chronometrist-diary-view.el: chronometrist-diary-viewel.
|
|
* chronometrist.el: chronometristel.
|
|
* chronometrist-events.el: chronometrist-eventsel.
|
|
* chronometrist-migrate.el: chronometrist-migrateel.
|
|
* chronometrist-plist-pp.el: chronometrist-plist-ppel.
|
|
* chronometrist-queries.el: chronometrist-queriesel.
|
|
* chronometrist-report-custom.el: chronometrist-report-customel.
|
|
* chronometrist-report.el: chronometrist-reportel.
|
|
* chronometrist-key-values.el: chronometrist-key-valuesel.
|
|
* chronometrist-statistics-custom.el: chronometrist-statistics-customel.
|
|
* chronometrist-statistics.el: chronometrist-statisticsel.
|
|
* chronometrist-time.el: chronometrist-timeel.
|
|
* chronometrist-timer.el: chronometrist-timerel.
|
|
* chronometrist-goal::
|
|
* chronometrist-sexp::
|
|
|
|
Legend of currently-used time formats
|
|
|
|
* ts::
|
|
* iso-timestamp::
|
|
* iso-date::
|
|
* seconds::
|
|
* minutes::
|
|
* list-duration::
|
|
|
|
@end detailmenu
|
|
@end menu
|
|
|
|
@node How to@dots{}
|
|
@chapter How to@dots{}
|
|
|
|
@menu
|
|
* How to set up Emacs to contribute::
|
|
@end menu
|
|
|
|
@node How to set up Emacs to contribute
|
|
@section How to set up Emacs to contribute
|
|
|
|
All of these are optional, but recommended for the best experience.
|
|
@enumerate
|
|
@item
|
|
Use @uref{https://github.com/Malabarba/Nameless, nameless-mode} for easier reading of Emacs Lisp code, and
|
|
@item
|
|
Use @uref{https://github.com/joostkremers/visual-fill-column, visual-fill-column-mode} to soft-wrap lines in Org/Markdown files.
|
|
@samp{org-indent-mode} (for Org files) and @uref{https://elpa.gnu.org/packages/adaptive-wrap.html, adaptive-prefix-mode} (for Markdown and other files) will further enhance the experience.
|
|
@item
|
|
Get the sources from @uref{https://github.com/contrapunctus-1/chronometrist} and read this manual in the Org format (doc/manual.org), so links to identifiers can take you to their location in the source.
|
|
@item
|
|
Install @uref{https://github.com/cask/cask, Cask} to easily byte-compile and test the project.
|
|
From the project root, you can now run
|
|
@enumerate
|
|
@item
|
|
@samp{cask} to install the project dependencies in a sandbox
|
|
@item
|
|
@samp{cask exec buttercup -L . --traceback pretty} to run tests.
|
|
@end enumerate
|
|
@end enumerate
|
|
|
|
@node Explanation
|
|
@chapter Explanation
|
|
|
|
@menu
|
|
* Design goals:: Some vague objectives which guided the project
|
|
* Terminology:: Explanation of some terms used later
|
|
* Project overview:: A broad overview of the code
|
|
* Chronometrist:: The primary command and its associated buffer.
|
|
* Midnight-spanning events:: Events starting on one day and ending on another
|
|
* Point restore behaviour:: The desired behaviour of point in Chronometrist
|
|
* chronometrist-report date range logic:: Deriving dates in the current week
|
|
* Tags and Key-Values:: How tags and key-values are implemented
|
|
@end menu
|
|
|
|
@node Design goals
|
|
@section Design goals
|
|
|
|
@enumerate
|
|
@item
|
|
Don't make assumptions about the user's profession
|
|
@itemize
|
|
@item
|
|
e.g. timeclock seems to assume you're using it for a 9-to-5/contractor job
|
|
@end itemize
|
|
@item
|
|
Incentivize use
|
|
@itemize
|
|
@item
|
|
Hooks allow the time tracker to automate tasks and become a useful part of your workflow
|
|
@end itemize
|
|
@item
|
|
Make it easy to edit data using existing, familiar tools
|
|
@itemize
|
|
@item
|
|
We don't use an SQL database, where changing a single field is tricky @footnote{As indicated by exploratory work in the @samp{parsimonious-reading} branch, where I made a loop to only @samp{read} and collect s-expressions from the file. It was near-instant@dots{}until I added event splitting to it.}
|
|
@item
|
|
We use a text file containing s-expressions (easy for humans to read and write)
|
|
@item
|
|
We use ISO-8601 for timestamps (easy for humans to read and write) rather than UNIX epoch time
|
|
@end itemize
|
|
@item
|
|
Reduce human errors in tracking
|
|
@item
|
|
Have a useful, informative, interactive interface
|
|
@item
|
|
Support mouse and keyboard use equally
|
|
|
|
[1] I still have doubts about this. Having SQL as a query language would be very useful in perusing the stored data. Maybe we should have tried to create a companion mode to edit SQL databases interactively?
|
|
@end enumerate
|
|
|
|
@node Terminology
|
|
@section Terminology
|
|
|
|
For lack of a better term, events are how we refer to time intervals. They are stored as plists; each contains at least a @samp{:name "<name>"}, a @samp{:start "<iso-timestamp>"}, and (except in case of an ongoing task) a @samp{:stop "<iso-timestamp>"}.
|
|
|
|
@node Project overview
|
|
@section Project overview
|
|
|
|
Chronometrist has three components, and each has a file containing major mode definitions and user-facing commands.
|
|
@enumerate
|
|
@item
|
|
@uref{../elisp/chronometrist.el, chronometrist.el}
|
|
@item
|
|
@uref{../elisp/chronometrist-report.el, chronometrist-report.el}
|
|
@item
|
|
@uref{../elisp/chronometrist-statistics.el, chronometrist-statistics.el}
|
|
@end enumerate
|
|
|
|
All three of these use @samp{(info "(elisp)Tabulated List Mode")}. Each of them also contains a "-print-non-tabular" function, which prints the non-tabular parts of the buffer.
|
|
|
|
Each of them has a corresponding @samp{-custom} file, which contain the Customize group and custom variable definitions for user-facing variables -
|
|
@itemize
|
|
@item
|
|
@uref{../elisp/chronometrist-custom.el, chronometrist-custom.el}
|
|
@item
|
|
@uref{../elisp/chronometrist-report-custom.el, chronometrist-report-custom.el}
|
|
@item
|
|
@uref{../elisp/chronometrist-statistics-custom.el, chronometrist-statistics-custom.el}
|
|
@end itemize
|
|
|
|
@uref{../elisp/chronometrist-common.el, chronometrist-common.el} contains definitions common to all components.
|
|
|
|
All three components use timers to keep their buffers updated. @uref{../elisp/chronometrist-timer.el, chronometrist-timer.el} contains all timer-related code.
|
|
|
|
Note - sometimes, when hacking or dealing with errors, timers may result in subtle bugs which are very hard to debug. Using @samp{chronometrist-force-restart-timer} or restarting Emacs can fix them, so try that as a first sanity check.
|
|
|
|
@node Chronometrist
|
|
@section Chronometrist
|
|
|
|
@menu
|
|
* Optimization::
|
|
@end menu
|
|
|
|
@node Optimization
|
|
@subsection Optimization
|
|
|
|
It is of great importance that Chronometrist be responsive -
|
|
@itemize
|
|
@item
|
|
A responsive program is more likely to be used; recall our design goal of 'incentivizing use'.
|
|
@item
|
|
Being an Emacs program, freezing the UI for any human-noticeable length of time is unacceptable - it prevents the user from working on anything in their environment.
|
|
@end itemize
|
|
Thus, I have considered various optimization strategies, and so far implemented two.
|
|
|
|
@enumerate
|
|
@item
|
|
Prevent excess creation of file watchers
|
|
|
|
|
|
One of the earliest 'optimizations' of great importance turned out to simply be a bug - turns out, if you run an identical call to @uref{(describe-function 'file-notify-add-watch), @samp{file-notify-add-watch}} twice, you create @emph{two} file watchers and your callback will be called @emph{twice.} We were creating a file watcher @emph{each time the chronometrist command was run.} 🤦 This was causing humongous slowdowns each time the file changed. 😅
|
|
@itemize
|
|
@item
|
|
It was fixed in v0.2.2 by making the watch creation conditional, using @uref{../elisp/chronometrist-common.el, @samp{chronometrist--fs-watch}} to store the watch object.
|
|
@end itemize
|
|
|
|
@item
|
|
Preserve state
|
|
|
|
|
|
The next one was released in v0.5. Till then, any time the @uref{../elisp/chronometrist-custom.el, @samp{chronometrist-file}} was modified, we'd clear the @uref{../elisp/chronometrist-events.el, @samp{chronometrist-events}} hash table and read data into it again. The reading itself is nearly-instant, even with ~2 years' worth of data @footnote{As indicated by exploratory work in the @samp{parsimonious-reading} branch, where I made a loop to only @samp{read} and collect s-expressions from the file. It was near-instant@dots{}until I added event splitting to it.} (it uses Emacs' @uref{(describe-function 'read), @samp{read}}, after all), but the splitting of @ref{Midnight-spanning events, , midnight-spanning events} is the real performance killer.
|
|
|
|
After the optimization@dots{}
|
|
@enumerate
|
|
@item
|
|
Two backend functions (@uref{../elisp/chronometrist-sexp.el, @samp{chronometrist-sexp-new}} and @uref{../elisp/chronometrist-sexp.el, @samp{chronometrist-sexp-replace-last}}) were modified to set a flag (@uref{../elisp/chronometrist.el, @samp{chronometrist--inhibit-read-p}}) before saving the file.
|
|
@item
|
|
If this flag is non-nil, @uref{../elisp/chronometrist.el, @samp{chronometrist-refresh-file}} skips the expensive calls to @samp{chronometrist-events-populate}, @samp{chronometrist-tasks-from-table}, and @samp{chronometrist-tags-history-populate}, and resets the flag.
|
|
@item
|
|
Instead, the aforementioned backend functions modify the relevant variables - @samp{chronometrist-events}, @samp{chronometrist-task-list}, and @samp{chronometrist-tags-history} - via@dots{}
|
|
@itemize
|
|
@item
|
|
@samp{chronometrist-events-add} / @samp{chronometrist-events-replace-last}
|
|
@item
|
|
@samp{chronometrist-task-list-add}, and
|
|
@item
|
|
@samp{chronometrist-tags-history-add} / @samp{chronometrist-tags-history-replace-last}, respectively.
|
|
@end itemize
|
|
@end enumerate
|
|
|
|
There are still some operations which @uref{../elisp/chronometrist.el, @samp{chronometrist-refresh-file}} runs unconditionally - which is to say there is scope for further optimization, if or when required.
|
|
|
|
@item
|
|
Checking hashes
|
|
|
|
|
|
@samp{chronometrist-file-change-type}
|
|
@samp{chronometrist-file-hash-length}
|
|
@samp{chronometrist--file-hashes}
|
|
|
|
Issues -
|
|
@enumerate
|
|
@item
|
|
Whitespace changes after last expression return @samp{:append} instead of nil.
|
|
@end enumerate
|
|
|
|
@item
|
|
Other strategies [25%]
|
|
|
|
|
|
@enumerate
|
|
@item
|
|
Defer (tag/key/value) history generation from file-change-time to prompt-time, and make it per-task instead of all tasks at once
|
|
@itemize
|
|
@item
|
|
The biggest resource hog is splitting of midnight-spanning intervals, however.
|
|
@item
|
|
Reduce memory use by allowing user to restrict number of s-expressions read.
|
|
@item
|
|
Per-task history generation will create problems - e.g. values for a given key for one task won't be suggested for values for the same key in another 🤦
|
|
@itemize
|
|
@item
|
|
Tags and keys are already task-sensitive; just don't make values task-sensitive.
|
|
@end itemize
|
|
@end itemize
|
|
@item
|
|
Compare partial hashes of file to know what has changed - only update memory when necessary.
|
|
@item
|
|
In-memory cache - don't store entire file into memory; instead, split midnight-spanning events just for the requested data.
|
|
@itemize
|
|
@item
|
|
Will increase load time for the first time @samp{chronometrist}, @samp{chronometrist-report}, or @samp{chronometrist-statistics} are run (including forward/backward commands in the latter two)
|
|
@itemize
|
|
@item
|
|
Can try pre-emptively loading data for the latter two
|
|
@end itemize
|
|
@item
|
|
Will reduce memory use.
|
|
@end itemize
|
|
@item
|
|
Mix of 2 and 3 - in-memory cache with partial updates
|
|
@item
|
|
Split and save midnight-spanning intervals to disk - remove the need for an in-memory version of data with split midnight-spanning intervals.
|
|
@itemize
|
|
@item
|
|
Least memory use?
|
|
@item
|
|
Might make the file harder for a user to edit.
|
|
@end itemize
|
|
@item
|
|
Save timestamps as UNIX epoch time.
|
|
@itemize
|
|
@item
|
|
Will (probably) greatly speed up time parsing and interval splitting.
|
|
@item
|
|
Will greatly impede human editing of the file, too. 🤔
|
|
@itemize
|
|
@item
|
|
An editing UI could help - pretty sure every timestamp edit I've ever made has been for the last interval, or at most an interval in today's data.
|
|
@end itemize
|
|
@end itemize
|
|
@end enumerate
|
|
@end enumerate
|
|
|
|
@node Midnight-spanning events
|
|
@section Midnight-spanning events
|
|
|
|
A unique problem in working with Chronometrist, one I had never foreseen, was tasks which start on one day and end on another. These mess up data consumption (especially interval calculations and acquiring data for a specific date) in all sorts of unforeseen ways.
|
|
|
|
There are a few different approaches of dealing with them. (Currently, Chronometrist uses #3.)
|
|
|
|
@menu
|
|
* Check the code of the first event of the day (timeclock format):: When the code of the first event in the day is "o", it's a midnight-spanning event.
|
|
* Split them at the file level::
|
|
* Split them at the hash-table-level::
|
|
* Split them at the data-consumer level (e.g. when calculating time for one day/getting events for one day): Split them at the data-consumer level (eg when calculating time for one day/getting events for one day).
|
|
@end menu
|
|
|
|
@node Check the code of the first event of the day (timeclock format)
|
|
@subsection Check the code of the first event of the day (timeclock format)
|
|
|
|
@itemize
|
|
@item
|
|
Advantage - very simple to detect
|
|
@item
|
|
Disadvantage - "in" and "out" events must be represented separately
|
|
@end itemize
|
|
|
|
@node Split them at the file level
|
|
@subsection Split them at the file level
|
|
|
|
@itemize
|
|
@item
|
|
Advantage - operation is performed only once for each such event + simpler data-consuming code + reduced post-parsing load.
|
|
@item
|
|
What happens when the user changes their day-start-time? The split-up events are now split wrongly, and the second event may get split @emph{again.}
|
|
Possible solutions -
|
|
@enumerate
|
|
@item
|
|
Add function to check if, for two events A and B, the :stop of A is the same as the :start of B, and that all their other tags are identical. Then we can re-split them according to the new day-start-time.
|
|
@item
|
|
Add a :split tag to split events. It can denote that the next event was originally a part of this one.
|
|
@item
|
|
Re-check and update the file when the day-start-time changes.
|
|
@itemize
|
|
@item
|
|
Possible with @code{add-variable-watcher} or @code{:custom-set} in Customize (thanks bpalmer)
|
|
@end itemize
|
|
@end enumerate
|
|
@end itemize
|
|
|
|
@node Split them at the hash-table-level
|
|
@subsection Split them at the hash-table-level
|
|
|
|
Handled by @code{chronometrist-sexp-events-populate}
|
|
@itemize
|
|
@item
|
|
Advantage - simpler data-consuming code.
|
|
@end itemize
|
|
|
|
@node Split them at the data-consumer level (eg when calculating time for one day/getting events for one day)
|
|
@subsection Split them at the data-consumer level (e.g. when calculating time for one day/getting events for one day)
|
|
|
|
@itemize
|
|
@item
|
|
Advantage - reduced repetitive post-parsing load.
|
|
@end itemize
|
|
|
|
@node Point restore behaviour
|
|
@section Point restore behaviour
|
|
|
|
After hacking, always test for and ensure the following -
|
|
@enumerate
|
|
@item
|
|
Toggling the buffer via @samp{chronometrist=/=chronometrist-report=/=chronometrist-statistics} should preserve point
|
|
@item
|
|
The timer function should preserve point when the buffer is current
|
|
@item
|
|
The timer function should preserve point when the buffer is not current, but is visible in another window
|
|
@item
|
|
The next/previous week keys and buttons should preserve point.
|
|
@end enumerate
|
|
|
|
@node chronometrist-report date range logic
|
|
@section chronometrist-report date range logic
|
|
|
|
A quick description, starting from the first time @uref{../elisp/chronometrist-report.el, @samp{chronometrist-report}} is run in an Emacs session -
|
|
@enumerate
|
|
@item
|
|
We get the current date as a ts struct @samp{(chronometrist-date)}.
|
|
@item
|
|
The variable @samp{chronometrist-report-week-start-day} stores the day we consider the week to start with. The default is "Sunday".
|
|
|
|
We check if the date from #2 is on the week start day, else decrement it till we are, using @samp{(chronometrist-report-previous-week-start)}.
|
|
@item
|
|
We store the date from #3 in the global variable @samp{chronometrist-report--ui-date}.
|
|
@item
|
|
By counting up from @samp{chronometrist-report--ui-date}, we get dates for the days in the next 7 days using @samp{(chronometrist-report-date->dates-in-week)}. We store them in @samp{chronometrist-report--ui-week-dates}.
|
|
|
|
The dates in @samp{chronometrist-report--ui-week-dates} are what is finally used to query the data displayed in the buffer.
|
|
@item
|
|
To get data for the previous/next weeks, we decrement/increment the date in @samp{chronometrist-report--ui-date} by 7 days and repeat the above process (via @samp{(chronometrist-report-previous-week)=/=(chronometrist-report-next-week)}).
|
|
@end enumerate
|
|
|
|
@node Tags and Key-Values
|
|
@section Tags and Key-Values
|
|
|
|
@uref{../elisp/chronometrist-key-values.el, chronometrist-key-values.el} deals with adding additional information to events, in the form of key-values and tags.
|
|
|
|
Key-values are stored as plist keywords and values. The user can add any keywords except @samp{:name}, @samp{:tags}, @samp{:start}, and @samp{:stop}. @footnote{To remove this restriction, I had briefly considered making a keyword called @samp{:user}, whose value would be another plist containing all user-defined keyword-values. But in practice, this hasn't been a big enough issue yet to justify the work.} Values can be any readable Lisp values.
|
|
|
|
Similarly, tags are stored using a @samp{:tags (<tag>*)} keyword-value pair. The tags themselves (the elements of the list) can be any readable Lisp value.
|
|
|
|
@menu
|
|
* User input::
|
|
* History::
|
|
@end menu
|
|
|
|
@node User input
|
|
@subsection User input
|
|
|
|
The entry points are @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-add}} and @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-add}}. The user adds these to the desired hooks, and they prompt the user for tags/key-values.
|
|
|
|
Both have corresponding functions to create a prompt -
|
|
@itemize
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-key-prompt}},
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-value-prompt}}, and
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-prompt}}.
|
|
@end itemize
|
|
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-add}}'s way of reading key-values from the user is somewhat different from most Emacs prompts - it creates a new buffer, and uses the minibuffer to alternatingly ask for keys and values in a loop. Key-values are inserted into the buffer as the user enters/selects them. The user can break out of this loop with an empty input (the keys to accept an empty input differ between completion systems, so we try to let the user know about them using @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-completion-quit-key}}). After exiting the loop, they can edit the key-values in the buffer, and use the commands @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-accept}} to accept the key-values (which uses @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-append-to-last}} to add them to the last plist in @samp{chronometrist-file}) or @uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-kv-reject}} to discard them.
|
|
|
|
@node History
|
|
@subsection History
|
|
|
|
All prompts suggest past user inputs. These are queried from three history hash tables -
|
|
@itemize
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-key-history}},
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-value-history}}, and
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-history}}.
|
|
@end itemize
|
|
|
|
Each of these has a corresponding function to clear it and fill it with values -
|
|
@itemize
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-key-history-populate}}
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-value-history-populate}}, and
|
|
@item
|
|
@uref{../elisp/chronometrist-key-values.el, @samp{chronometrist-tags-history-populate}}.
|
|
@end itemize
|
|
|
|
@node Reference
|
|
@chapter Reference
|
|
|
|
@menu
|
|
* Legend of currently-used time formats::
|
|
* chronometrist-common.el: chronometrist-commonel.
|
|
* chronometrist-custom.el: chronometrist-customel.
|
|
* chronometrist-diary-view.el: chronometrist-diary-viewel.
|
|
* chronometrist.el: chronometristel.
|
|
* chronometrist-events.el: chronometrist-eventsel.
|
|
* chronometrist-migrate.el: chronometrist-migrateel.
|
|
* chronometrist-plist-pp.el: chronometrist-plist-ppel.
|
|
* chronometrist-queries.el: chronometrist-queriesel.
|
|
* chronometrist-report-custom.el: chronometrist-report-customel.
|
|
* chronometrist-report.el: chronometrist-reportel.
|
|
* chronometrist-key-values.el: chronometrist-key-valuesel.
|
|
* chronometrist-statistics-custom.el: chronometrist-statistics-customel.
|
|
* chronometrist-statistics.el: chronometrist-statisticsel.
|
|
* chronometrist-time.el: chronometrist-timeel.
|
|
* chronometrist-timer.el: chronometrist-timerel.
|
|
* chronometrist-goal::
|
|
* chronometrist-sexp::
|
|
@end menu
|
|
|
|
@node Legend of currently-used time formats
|
|
@section Legend of currently-used time formats
|
|
|
|
@menu
|
|
* ts::
|
|
* iso-timestamp::
|
|
* iso-date::
|
|
* seconds::
|
|
* minutes::
|
|
* list-duration::
|
|
@end menu
|
|
|
|
@node ts
|
|
@subsection ts
|
|
|
|
ts.el struct
|
|
@itemize
|
|
@item
|
|
Used by nearly all internal functions
|
|
@end itemize
|
|
|
|
@node iso-timestamp
|
|
@subsection iso-timestamp
|
|
|
|
"YYYY-MM-DDTHH:MM:SSZ"
|
|
@itemize
|
|
@item
|
|
Used in the s-expression file format
|
|
@item
|
|
Read by chronometrist-sexp-events-populate
|
|
@item
|
|
Used in the plists in the chronometrist-events hash table values
|
|
@end itemize
|
|
|
|
@node iso-date
|
|
@subsection iso-date
|
|
|
|
"YYYY-MM-DD"
|
|
@itemize
|
|
@item
|
|
Used as hash table keys in chronometrist-events - can't use ts structs for keys, you'd have to make a hash table predicate which uses ts=
|
|
@end itemize
|
|
|
|
@node seconds
|
|
@subsection seconds
|
|
|
|
integer seconds as duration
|
|
@itemize
|
|
@item
|
|
Used for most durations
|
|
@item
|
|
May be changed to floating point to allow larger durations. The minimum range of `most-positive-fixnum` is 536870911, which seems to be enough to represent durations of 17 years.
|
|
@item
|
|
Used for update intervals (chronometrist-update-interval, chronometrist-change-update-interval)
|
|
@end itemize
|
|
|
|
@node minutes
|
|
@subsection minutes
|
|
|
|
integer minutes as duration
|
|
@itemize
|
|
@item
|
|
Used for goals (chronometrist-goals-list, chronometrist-get-goal) - minutes seems like the ideal unit for users to enter
|
|
@end itemize
|
|
|
|
@node list-duration
|
|
@subsection list-duration
|
|
|
|
(hours minute seconds)
|
|
@itemize
|
|
@item
|
|
Only returned by chronometrist-seconds-to-hms, called by chronometrist-format-time
|
|
@end itemize
|
|
|
|
@node chronometrist-commonel
|
|
@section chronometrist-common.el
|
|
|
|
@enumerate
|
|
@item
|
|
Variable - chronometrist-empty-time-string
|
|
@item
|
|
Variable - chronometrist-date-re
|
|
@item
|
|
Variable - chronometrist-time-re-ui
|
|
@item
|
|
Variable - chronometrist-task-list
|
|
@item
|
|
Function - chronometrist-task-list-add (task)
|
|
@item
|
|
Internal Variable - chronometrist--fs-watch
|
|
@item
|
|
Function - chronometrist-current-task ()
|
|
@item
|
|
Function - chronometrist-format-time (seconds &optional (blank " "))
|
|
@itemize
|
|
@item
|
|
seconds -> "h:m:s"
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-common-file-empty-p (file)
|
|
@item
|
|
Function - chronometrist-common-clear-buffer (buffer)
|
|
@item
|
|
Function - chronometrist-format-keybinds (command map &optional firstonly)
|
|
@item
|
|
Function - chronometrist-events->ts-pairs (events)
|
|
@itemize
|
|
@item
|
|
(plist @dots{}) -> ((ts . ts) @dots{})
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-ts-pairs->durations (ts-pairs)
|
|
@itemize
|
|
@item
|
|
((ts . ts) @dots{}) -> seconds
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-previous-week-start (ts)
|
|
@itemize
|
|
@item
|
|
ts -> ts
|
|
@end itemize
|
|
@end enumerate
|
|
|
|
@node chronometrist-customel
|
|
@section chronometrist-custom.el
|
|
|
|
@enumerate
|
|
@item
|
|
Custom variable - chronometrist-file
|
|
@item
|
|
Custom variable - chronometrist-buffer-name
|
|
@item
|
|
Custom variable - chronometrist-hide-cursor
|
|
@item
|
|
Custom variable - chronometrist-update-interval
|
|
@item
|
|
Custom variable - chronometrist-activity-indicator
|
|
@item
|
|
Custom variable - chronometrist-day-start-time
|
|
@end enumerate
|
|
|
|
@node chronometrist-diary-viewel
|
|
@section chronometrist-diary-view.el
|
|
|
|
@enumerate
|
|
@item
|
|
Variable - chronometrist-diary-buffer-name
|
|
@item
|
|
Internal Variable - chronometrist-diary--current-date
|
|
@item
|
|
Function - chronometrist-intervals-on (date)
|
|
@item
|
|
Function - chronometrist-diary-tasks-reasons-on (date)
|
|
@item
|
|
Function - chronometrist-diary-refresh (&optional ignore-auto noconfirm date)
|
|
@item
|
|
Major Mode - chronometrist-diary-view-mode
|
|
@item
|
|
Command - chronometrist-diary-view (&optional date)
|
|
@end enumerate
|
|
|
|
@node chronometristel
|
|
@section chronometrist.el
|
|
|
|
@enumerate
|
|
@item
|
|
Internal Variable - chronometrist--task-history
|
|
@item
|
|
Internal Variable - chronometrist--point
|
|
@item
|
|
Keymap - chronometrist-mode-map
|
|
@item
|
|
Command - chronometrist-open-log (&optional button)
|
|
@item
|
|
Function - chronometrist-common-create-file ()
|
|
@item
|
|
Function - chronometrist-task-active? (task)
|
|
@itemize
|
|
@item
|
|
String -> Boolean
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-use-goals? ()
|
|
@item
|
|
Function - chronometrist-activity-indicator ()
|
|
@item
|
|
Function - chronometrist-entries ()
|
|
@item
|
|
Function - chronometrist-task-at-point ()
|
|
@item
|
|
Function - chronometrist-goto-last-task ()
|
|
@item
|
|
Function - chronometrist-print-keybind (command &optional description firstonly)
|
|
@item
|
|
Function - chronometrist-print-non-tabular ()
|
|
@item
|
|
Function - chronometrist-goto-nth-task (n)
|
|
@item
|
|
Function - chronometrist-refresh (&optional ignore-auto noconfirm)
|
|
@item
|
|
Internal Variable - chronometrist--inhibit-read-p
|
|
@item
|
|
Internal Variable - chronometrist--file-hashes
|
|
@item
|
|
Function - chronometrist-file-hash-length (&optional start end)
|
|
@item
|
|
Function - chronometrist-file-change-type (hashes)
|
|
@item
|
|
Function - chronometrist-refresh-file (fs-event)
|
|
@item
|
|
Command - chronometrist-query-stop ()
|
|
@item
|
|
Command - chronometrist-in (task &optional _prefix)
|
|
@item
|
|
Command - chronometrist-out (&optional _prefix)
|
|
@item
|
|
Variable - chronometrist-before-in-functions
|
|
@item
|
|
Variable - chronometrist-after-in-functions
|
|
@item
|
|
Variable - chronometrist-before-out-functions
|
|
@item
|
|
Variable - chronometrist-after-out-functions
|
|
@item
|
|
Function - chronometrist-run-functions-and-clock-in (task)
|
|
@item
|
|
Function - chronometrist-run-functions-and-clock-out (task)
|
|
@item
|
|
Keymap - chronometrist-mode-map
|
|
@item
|
|
Major Mode - chronometrist-mode
|
|
@item
|
|
Function - chronometrist-toggle-task-button (button)
|
|
@item
|
|
Function - chronometrist-add-new-task-button (button)
|
|
@item
|
|
Command - chronometrist-toggle-task (&optional prefix inhibit-hooks)
|
|
@item
|
|
Command - chronometrist-toggle-task-no-hooks (&optional prefix)
|
|
@item
|
|
Command - chronometrist-add-new-task ()
|
|
@item
|
|
Command - chronometrist (&optional arg)
|
|
@end enumerate
|
|
|
|
@node chronometrist-eventsel
|
|
@section chronometrist-events.el
|
|
|
|
@enumerate
|
|
@item
|
|
Variable - chronometrist-events
|
|
@itemize
|
|
@item
|
|
keys - iso-date
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-day-start (timestamp)
|
|
@itemize
|
|
@item
|
|
iso-timestamp -> encode-time
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-file-clean ()
|
|
@itemize
|
|
@item
|
|
commented out, unused
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-events-maybe-split (event)
|
|
@item
|
|
Function - chronometrist-events-populate ()
|
|
@item
|
|
Function - chronometrist-tasks-from-table ()
|
|
@item
|
|
Function - chronometrist-events-add (plist)
|
|
@item
|
|
Function - chronometrist-events-replace-last (plist)
|
|
@item
|
|
Function - chronometrist-events-subset (start end)
|
|
@itemize
|
|
@item
|
|
ts ts -> hash-table
|
|
@end itemize
|
|
@end enumerate
|
|
|
|
@node chronometrist-migrateel
|
|
@section chronometrist-migrate.el
|
|
|
|
@enumerate
|
|
@item
|
|
Variable - chronometrist-migrate-table
|
|
@item
|
|
Function - chronometrist-migrate-populate (in-file)
|
|
@item
|
|
Function - chronometrist-migrate-timelog-file->sexp-file (&optional in-file out-file)
|
|
@item
|
|
Function - chronometrist-migrate-check ()
|
|
@end enumerate
|
|
|
|
@node chronometrist-plist-ppel
|
|
@section chronometrist-plist-pp.el
|
|
|
|
@enumerate
|
|
@item
|
|
Variable - chronometrist-plist-pp-keyword-re
|
|
@item
|
|
Variable - chronometrist-plist-pp-whitespace-re
|
|
@item
|
|
Function - chronometrist-plist-pp-longest-keyword-length ()
|
|
@item
|
|
Function - chronometrist-plist-pp-buffer-keyword-helper ()
|
|
@item
|
|
Function - chronometrist-plist-pp-buffer ()
|
|
@item
|
|
Function - chronometrist-plist-pp-to-string (object)
|
|
@item
|
|
Function - chronometrist-plist-pp (object &optional stream)
|
|
@end enumerate
|
|
|
|
@node chronometrist-queriesel
|
|
@section chronometrist-queries.el
|
|
|
|
@enumerate
|
|
@item
|
|
Function - chronometrist-last ()
|
|
@itemize
|
|
@item
|
|
-> plist
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-task-time-one-day (task &optional (ts (ts-now)))
|
|
@itemize
|
|
@item
|
|
String &optional ts -> seconds
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-active-time-one-day (&optional ts)
|
|
@itemize
|
|
@item
|
|
&optional ts -> seconds
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-statistics-count-active-days (task &optional (table chronometrist-events))
|
|
@item
|
|
Function - chronometrist-task-events-in-day (task ts)
|
|
@end enumerate
|
|
|
|
@node chronometrist-report-customel
|
|
@section chronometrist-report-custom.el
|
|
|
|
@enumerate
|
|
@item
|
|
Custom variable - chronometrist-report-buffer-name
|
|
@item
|
|
Custom variable - chronometrist-report-week-start-day
|
|
@item
|
|
Custom variable - chronometrist-report-weekday-number-alist
|
|
@end enumerate
|
|
|
|
@node chronometrist-reportel
|
|
@section chronometrist-report.el
|
|
|
|
@enumerate
|
|
@item
|
|
Internal Variable - chronometrist-report--ui-date
|
|
@item
|
|
Internal Variable - chronometrist-report--ui-week-dates
|
|
@item
|
|
Internal Variable - chronometrist-report--point
|
|
@item
|
|
Function - chronometrist-report-date ()
|
|
@item
|
|
Function - chronometrist-report-date->dates-in-week (first-date-in-week)
|
|
@itemize
|
|
@item
|
|
ts-1 -> (ts-1 @dots{} ts-7)
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-report-date->week-dates ()
|
|
@item
|
|
Function - chronometrist-report-entries ()
|
|
@item
|
|
Function - chronometrist-report-print-keybind (command &optional description firstonly)
|
|
@item
|
|
Function - chronometrist-report-print-non-tabular ()
|
|
@item
|
|
Function - chronometrist-report-refresh (&optional _ignore-auto _noconfirm)
|
|
@item
|
|
Function - chronometrist-report-refresh-file (@math{_fs}-event)
|
|
@item
|
|
Keymap - chronometrist-report-mode-map
|
|
@item
|
|
Major Mode - chronometrist-report-mode
|
|
@item
|
|
Function - chronometrist-report (&optional keep-date)
|
|
@item
|
|
Function - chronometrist-report-previous-week (arg)
|
|
@item
|
|
Function - chronometrist-report-next-week (arg)
|
|
@end enumerate
|
|
|
|
@node chronometrist-key-valuesel
|
|
@section chronometrist-key-values.el
|
|
|
|
@enumerate
|
|
@item
|
|
Internal Variable - chronometrist--tag-suggestions
|
|
@item
|
|
Internal Variable - chronometrist--value-suggestions
|
|
@item
|
|
Function - chronometrist-plist-remove (plist &rest keys)
|
|
@item
|
|
Function - chronometrist-maybe-string-to-symbol (list)
|
|
@item
|
|
Function - chronometrist-maybe-symbol-to-string (list)
|
|
@item
|
|
Function - chronometrist-append-to-last (tags plist)
|
|
@item
|
|
Variable - chronometrist-tags-history
|
|
@item
|
|
Function - chronometrist-map-file (file fn)
|
|
@item
|
|
Function - chronometrist-history-prep (key history-table)
|
|
@item
|
|
Function - chronometrist-tags-history-populate (task history-table file)
|
|
@item
|
|
Function - chronometrist-key-history-populate (task history-table file)
|
|
@item
|
|
Function - chronometrist-value-history-populate (history-table file)
|
|
@item
|
|
Function - chronometrist-tags-history-add (plist)
|
|
@item
|
|
Function - chronometrist-tags-history-replace-last (plist)
|
|
@item
|
|
Function - chronometrist-tags-history-combination-strings (task)
|
|
@item
|
|
Function - chronometrist-tags-history-individual-strings (task)
|
|
@item
|
|
Function - chronometrist-tags-prompt (task &optional initial-input)
|
|
@item
|
|
Function - chronometrist-tags-add (&rest args)
|
|
@item
|
|
Custom Variable - chronometrist-kv-buffer-name
|
|
@item
|
|
Variable - chronometrist-key-history
|
|
@item
|
|
Variable - chronometrist-value-history
|
|
@item
|
|
Keymap - chronometrist-kv-read-mode-map
|
|
@item
|
|
Major Mode - chronometrist-kv-read-mode
|
|
@item
|
|
Function - chronometrist-kv-completion-quit-key ()
|
|
@item
|
|
Function - chronometrist-string-has-whitespace-p (string)
|
|
@item
|
|
Function - chronometrist-key-prompt (used-keys)
|
|
@item
|
|
Function - chronometrist-value-prompt (key)
|
|
@item
|
|
Function - chronometrist-value-insert (value)
|
|
@item
|
|
Function - chronometrist-kv-add (&rest args)
|
|
@item
|
|
Command - chronometrist-kv-accept ()
|
|
@item
|
|
Command - chronometrist-kv-reject ()
|
|
@item
|
|
Internal Variable - chronometrist--skip-detail-prompts
|
|
@item
|
|
Function - chronometrist-skip-query-prompt (task)
|
|
@item
|
|
Function - chronometrist-skip-query-reset (@math{_task})
|
|
@end enumerate
|
|
|
|
@node chronometrist-statistics-customel
|
|
@section chronometrist-statistics-custom.el
|
|
|
|
@enumerate
|
|
@item
|
|
Custom variable - chronometrist-statistics-buffer-name
|
|
@end enumerate
|
|
|
|
@node chronometrist-statisticsel
|
|
@section chronometrist-statistics.el
|
|
|
|
@enumerate
|
|
@item
|
|
Internal Variable - chronometrist-statistics--ui-state
|
|
@item
|
|
Internal Variable - chronometrist-statistics--point
|
|
@item
|
|
Function - chronometrist-statistics-count-average-time-spent (task &optional (table chronometrist-events))
|
|
@itemize
|
|
@item
|
|
string &optional hash-table -> seconds
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-statistics-entries-internal (table)
|
|
@item
|
|
Function - chronometrist-statistics-entries ()
|
|
@item
|
|
Function - chronometrist-statistics-print-keybind (command &optional description firstonly)
|
|
@item
|
|
Function - chronometrist-statistics-print-non-tabular ()
|
|
@item
|
|
Function - chronometrist-statistics-refresh (&optional ignore-auto noconfirm)
|
|
@item
|
|
Keymap - chronometrist-statistics-mode-map
|
|
@item
|
|
Major Mode - chronometrist-statistics-mode
|
|
@item
|
|
Command - chronometrist-statistics (&optional preserve-state)
|
|
@item
|
|
Command - chronometrist-statistics-previous-range (arg)
|
|
@item
|
|
Command - chronometrist-statistics-next-range (arg)
|
|
@end enumerate
|
|
|
|
@node chronometrist-timeel
|
|
@section chronometrist-time.el
|
|
|
|
@enumerate
|
|
@item
|
|
Function - chronometrist-iso-timestamp->ts (timestamp)
|
|
@itemize
|
|
@item
|
|
iso-timestamp -> ts
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-iso-date->ts (date)
|
|
@itemize
|
|
@item
|
|
iso-date -> ts
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-date (&optional (ts (ts-now)))
|
|
@itemize
|
|
@item
|
|
&optional ts -> ts (with time 00:00:00)
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-format-time-iso8601 (&optional unix-time)
|
|
@item
|
|
Function - chronometrist-midnight-spanning-p (start-time stop-time)
|
|
@item
|
|
Function - chronometrist-seconds-to-hms (seconds)
|
|
@itemize
|
|
@item
|
|
seconds -> list-duration
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-interval (event)
|
|
@itemize
|
|
@item
|
|
event -> duration
|
|
@end itemize
|
|
@end enumerate
|
|
|
|
@node chronometrist-timerel
|
|
@section chronometrist-timer.el
|
|
|
|
@enumerate
|
|
@item
|
|
Internal Variable - chronometrist--timer-object
|
|
@item
|
|
Function - chronometrist-timer ()
|
|
@item
|
|
Command - chronometrist-stop-timer ()
|
|
@item
|
|
Command - chronometrist-maybe-start-timer (&optional interactive-test)
|
|
@item
|
|
Command - chronometrist-force-restart-timer ()
|
|
@item
|
|
Command - chronometrist-change-update-interval (arg)
|
|
@end enumerate
|
|
|
|
@node chronometrist-goal
|
|
@section chronometrist-goal
|
|
|
|
@enumerate
|
|
@item
|
|
Internal Variable - chronometrist-goal--timers-list
|
|
@item
|
|
Custom Variable - chronometrist-goal-list nil
|
|
@item
|
|
Function - chronometrist-goal-run-at-time (time repeat function &rest args)
|
|
@item
|
|
Function - chronometrist-goal-seconds->alert-string (seconds)
|
|
@itemize
|
|
@item
|
|
seconds -> string
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-goal-approach-alert (task goal spent)
|
|
@itemize
|
|
@item
|
|
string minutes minutes
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-goal-complete-alert (task goal spent)
|
|
@itemize
|
|
@item
|
|
string minutes minutes
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-goal-exceed-alert (task goal spent)
|
|
@itemize
|
|
@item
|
|
string minutes minutes
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-goal-no-goal-alert (task goal spent)
|
|
@itemize
|
|
@item
|
|
string minutes minutes
|
|
@end itemize
|
|
@item
|
|
Custom Variable - chronometrist-goal-alert-functions
|
|
@itemize
|
|
@item
|
|
each function is passed - string minutes minutes
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-goal-get (task &optional (goal-list chronometrist-goal-list))
|
|
@itemize
|
|
@item
|
|
String &optional List -> minutes
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-goal-run-alert-timers (task)
|
|
@item
|
|
Function - chronometrist-goal-stop-alert-timers (&optional _task)
|
|
@item
|
|
Function - chronometrist-goal-on-file-change ()
|
|
@end enumerate
|
|
|
|
@node chronometrist-sexp
|
|
@section chronometrist-sexp
|
|
|
|
@enumerate
|
|
@item
|
|
Custom variable - chronometrist-sexp-pretty-print-function
|
|
@item
|
|
Macro - chronometrist-sexp-in-file (file &rest body)
|
|
@item
|
|
Function - chronometrist-sexp-open-log ()
|
|
@item
|
|
Function - chronometrist-sexp-between (&optional (ts-beg (chronometrist-date)) (ts-end (ts-adjust 'day +1 (chronometrist-date))))
|
|
@item
|
|
Function - chronometrist-sexp-query-till (&optional (date (chronometrist-date)))
|
|
@item
|
|
Function - chronometrist-sexp-last ()
|
|
@itemize
|
|
@item
|
|
-> plist
|
|
@end itemize
|
|
@item
|
|
Function - chronometrist-sexp-current-task ()
|
|
@item
|
|
Function - chronometrist-sexp-events-populate ()
|
|
@item
|
|
Function - chronometrist-sexp-create-file ()
|
|
@item
|
|
Function - chronometrist-sexp-new (plist &optional (buffer (find-file-noselect chronometrist-file)))
|
|
@item
|
|
Function - chronometrist-sexp-delete-list (&optional arg)
|
|
@item
|
|
Function - chronometrist-sexp-replace-last (plist)
|
|
@item
|
|
Command - chronometrist-sexp-reindent-buffer ()
|
|
@end enumerate
|
|
|
|
@bye |