334 lines
12 KiB
Org Mode
334 lines
12 KiB
Org Mode
#+TITLE: Chronometrist - an extensible time tracker for Emacs
|
||
#+SUBTITLE: User Manual
|
||
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style.css" />
|
||
|
||
[[https://melpa.org/#/chronometrist][file:https://melpa.org/packages/chronometrist-badge.svg]]
|
||
|
||
A time tracker in Emacs with a nice interface
|
||
|
||
Largely modelled after the Android application, [[https://github.com/netmackan/ATimeTracker][A Time Tracker]]
|
||
|
||
* Benefits
|
||
1. Extremely simple and efficient to use
|
||
2. Displays useful information about your time usage
|
||
3. Support for both mouse and keyboard
|
||
4. Human errors in tracking are easily fixed by editing a plain text file
|
||
5. Hooks to let you perform arbitrary actions when starting/stopping tasks
|
||
|
||
* Limitations
|
||
1. No support (yet) for adding a task without clocking into it.
|
||
2. No support for concurrent tasks.
|
||
|
||
* Comparisons
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: comparisons
|
||
:END:
|
||
** timeclock.el
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: timeclock.el
|
||
:END:
|
||
|
||
Compared to timeclock.el, Chronometrist
|
||
+ stores data in an s-expression format rather than a line-based one
|
||
+ supports attaching tags and arbitrary key-values to time intervals
|
||
+ has commands to shows useful summaries
|
||
+ has more hooks
|
||
|
||
** Org time tracking
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: org-time-tracking
|
||
:END:
|
||
|
||
Chronometrist and Org time tracking seem to be equivalent in terms of capabilities, approaching the same ends through different means.
|
||
+ Chronometrist doesn't have a mode line indicator at the moment. (planned)
|
||
+ Chronometrist doesn't have Org's sophisticated querying facilities. (an SQLite backend is planned)
|
||
+ Org does so many things that keybindings seem to necessarily get longer. Chronometrist has far fewer commands than Org, so most of the keybindings are single keys, without modifiers.
|
||
+ Chronometrist's UI makes keybindings discoverable - they are displayed in the buffers themselves.
|
||
+ Chronometrist's UI is cleaner, since the storage is separate from the display. It doesn't show tasks as trees like Org, but it uses tags and key-values to achieve that. Additionally, navigating a flat list takes fewer user operations than navigating a tree.
|
||
+ Chronometrist data is just s-expressions (plists), and may be easier to parse than a complex text format with numerous use-cases.
|
||
|
||
* Installation
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: installation
|
||
:END:
|
||
** from MELPA
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: install-from-melpa
|
||
:END:
|
||
|
||
1. Set up MELPA - https://melpa.org/#/getting-started
|
||
|
||
(Chronometrist uses semantic versioning and only releases are pushed to the master branch, so using MELPA Stable is recommended and has no effect on frequency of updates.)
|
||
2. =M-x package-install RET chronometrist RET=
|
||
|
||
** from Git
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: install-from-git
|
||
:END:
|
||
|
||
You can get =chronometrist= from https://tildegit.org/contrapunctus/chronometrist
|
||
|
||
=chronometrist= requires
|
||
+ Emacs v25 or higher
|
||
+ [[https://github.com/magnars/dash.el][dash.el]]
|
||
+ [[https://github.com/alphapapa/ts.el][ts.el]]
|
||
|
||
The optional extension =chronometrist-key-values= requires =choice.el=, apart from =chronometrist= itself.
|
||
|
||
Add the "elisp/" subdirectory to your load-path, and =(require 'chronometrist)=.
|
||
|
||
* Usage
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: usage
|
||
:END:
|
||
|
||
** chronometrist
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: chronometrist-1
|
||
:END:
|
||
|
||
Run =M-x chronometrist= to see your projects, the time you spent on them today, which one is active, and the total time clocked today.
|
||
|
||
Click or hit =RET= (=chronometrist-toggle-task=) on a project to start tracking time for it. If it's already clocked in, it will be clocked out.
|
||
|
||
You can also hit =<numeric prefix> RET= anywhere in the buffer to toggle the corresponding project, e.g. =C-1 RET= will toggle the project with index 1.
|
||
|
||
Press =r= to see a weekly report (see =chronometrist-report=)
|
||
|
||
** chronometrist-report
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: chronometrist-report
|
||
:END:
|
||
|
||
Run =M-x chronometrist-report= (or =chronometrist= with a prefix argument of 1, or press =r= in the =chronometrist= buffer) to see a weekly report.
|
||
|
||
Press =b= to look at past weeks, and =f= for future weeks.
|
||
|
||
** chronometrist-statistics
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: chronometrist-statistics
|
||
:END:
|
||
|
||
Run =M-x chronometrist-statistics= (or =chronometrist= with a prefix argument of 2) to view statistics.
|
||
|
||
Press =b= to look at past time ranges, and =f= for future ones.
|
||
|
||
** chronometrist-details
|
||
|
||
** common commands
|
||
In the buffers created by the previous three commands, you can press =l= (=chronometrist-open-log=) to view/edit your =chronometrist-file=, which by default is =~/.emacs.d/chronometrist.sexp=.
|
||
|
||
All of these commands will kill their buffer when run again with the buffer visible, so the keys you bind them to behave as a toggle.
|
||
|
||
All buffers keep themselves updated via an idle timer - no need to frequently press =g= to update.
|
||
|
||
** Time goals/targets
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: time-goalstargets
|
||
:END:
|
||
|
||
If you wish you could define time goals for some tasks, and have Chronometrist notify you when you're approaching the goal, completing it, or exceeding it, check out the extension [[https://github.com/contrapunctus-1/chronometrist-goal/][chronometrist-goal.el]].
|
||
|
||
* How-to
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: customization
|
||
:END:
|
||
|
||
See the Customize groups =chronometrist= and =chronometrist-report= for variables intended to be user-customizable.
|
||
|
||
** How to display a prompt when exiting with an active task
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: prompt-when-exiting-emacs
|
||
:END:
|
||
|
||
Evaluate or add to your init.el the following -
|
||
=(add-hook 'kill-emacs-query-functions 'chronometrist-query-stop)=
|
||
|
||
** How to load the program using literate-elisp
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: how-to-literate-elisp
|
||
:END:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-to-list 'load-path "<directory containing chronometrist.org>")
|
||
|
||
(require 'literate-elisp) ;; or autoload, use-package, ...
|
||
(literate-elisp-load "chronometrist.org")
|
||
#+END_SRC
|
||
|
||
** How to attach tags to time intervals
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: how-to-tags
|
||
:END:
|
||
|
||
1. Add =chronometrist-tags-add= to one or more of these hooks [fn:1] -
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-to-list 'chronometrist-after-in-functions 'chronometrist-tags-add)
|
||
(add-to-list 'chronometrist-before-out-functions 'chronometrist-tags-add)
|
||
(add-to-list 'chronometrist-after-out-functions 'chronometrist-tags-add)
|
||
#+END_SRC
|
||
2. clock in/clock out to trigger the hook.
|
||
|
||
The prompt suggests past combinations you used for the current task, which you can browse with =M-p=/=M-n=. You can leave it blank by pressing =RET=.
|
||
|
||
[fn:1] but not =chronometrist-before-in-functions=
|
||
|
||
** How to attach key-values to time intervals
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: how-to-key-value-pairs
|
||
:END:
|
||
|
||
1. Add =chronometrist-kv-add= to one or more of these hooks [fn:2] -
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-to-list 'chronometrist-after-in-functions 'chronometrist-kv-add)
|
||
(add-to-list 'chronometrist-before-out-functions 'chronometrist-kv-add)
|
||
(add-to-list 'chronometrist-after-out-functions 'chronometrist-kv-add)
|
||
#+END_SRC
|
||
|
||
To exit the prompt, press the key it indicates for quitting - you can then edit the resulting key-values by hand if required. Press =C-c C-c= to accept the key-values, or =C-c C-k= to cancel.
|
||
|
||
[fn:2] but not =chronometrist-before-in-functions=
|
||
|
||
** How to skip running hooks/attaching tags and key values
|
||
Use =M-RET= (=chronometrist-toggle-task-no-hooks=) to clock in/out.
|
||
|
||
** How to open certain files when you start a task
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: open-certain-files-when-you-start-a-task
|
||
:END:
|
||
|
||
An idea from the author's own init -
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun my-start-project (project)
|
||
(pcase project
|
||
("Guitar"
|
||
(find-file-other-window "~/repertoire.org"))
|
||
;; ...
|
||
))
|
||
|
||
(add-hook 'chronometrist-before-in-functions 'my-start-project)
|
||
#+END_SRC
|
||
|
||
** How to warn yourself about uncommitted changes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: uncommitted-changes
|
||
:END:
|
||
|
||
Another one, prompting the user if they have uncommitted changes in a git repository (assuming they use [[https://magit.vc/][Magit]]) -
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(autoload 'magit-anything-modified-p "magit")
|
||
|
||
(defun my-commit-prompt ()
|
||
"Prompt user if `default-directory' is a dirty Git repository.
|
||
Return t if the user answers yes, if the repository is clean, or
|
||
if there is no Git repository.
|
||
|
||
Return nil (and run `magit-status') if the user answers no."
|
||
(cond ((not (magit-anything-modified-p)) t)
|
||
((yes-or-no-p
|
||
(format "You have uncommitted changes in %S. Really clock out? "
|
||
default-directory)) t)
|
||
(t (magit-status) nil)))
|
||
|
||
(add-hook 'chronometrist-before-out-functions 'my-commit-prompt)
|
||
#+END_SRC
|
||
|
||
** How to display the current time interval in the activity indicator
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: current-time-interval-in-activity-indicator
|
||
:END:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun my-activity-indicator ()
|
||
(thread-last (plist-put (chronometrist-last)
|
||
:stop (chronometrist-format-time-iso8601))
|
||
list
|
||
chronometrist-events-to-durations
|
||
(-reduce #'+)
|
||
truncate
|
||
chronometrist-format-time))
|
||
|
||
(setq chronometrist-activity-indicator #'my-activity-indicator)
|
||
#+END_SRC
|
||
|
||
* Explanation
|
||
** Literate Program
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: explanation-literate-program
|
||
:END:
|
||
Chronometrist is a literate program, made using Org - the canonical source is the =chronometrist.org= file, which contains source blocks. These are provided to users after /tangling/ (extracting the source into an Emacs Lisp file).
|
||
|
||
The Org file can also be loaded directly using the [[https://github.com/jingtaozf/literate-elisp][literate-elisp]] package, so that all source links (e.g. =xref=, =describe-function=) lead to the Org file, within the context of the concerned documentation. See [[#how-to-literate-elisp][How to load the program using literate-elisp]].
|
||
=chronometrist.org= is also included in MELPA installs, although not used directly by default, since doing so would interfere with automatic generation of autoloads.
|
||
|
||
* User's reference
|
||
All variables intended for user customization are listed here. They serve as the public API for this project for the purpose of semantic versioning. Any changes to these which require a user to modify their configuration are considered breaking changes.
|
||
|
||
1. =chronometrist-file=
|
||
2. =chronometrist-buffer-name=
|
||
3. =chronometrist-report-buffer-name=
|
||
4. =chronometrist-details-buffer-name=
|
||
5. =chronometrist-sexp-pretty-print-function=
|
||
6. =chronometrist-hide-cursor=
|
||
7. =chronometrist-update-interval=
|
||
8. =chronometrist-activity-indicator=
|
||
|
||
Buffer schemas
|
||
1. =chronometrist-schema=
|
||
2. =chronometrist-details-schema=
|
||
|
||
Hooks
|
||
1. =chronometrist-mode-hook=
|
||
2. =chronometrist-schema-transformers=
|
||
3. =chronometrist-row-transformers=
|
||
4. =chronometrist-before-in-functions=
|
||
5. =chronometrist-after-in-functions=
|
||
6. =chronometrist-before-out-functions=
|
||
7. =chronometrist-after-out-functions=
|
||
8. =chronometrist-file-change-hook=
|
||
9. =chronometrist-timer-hook=
|
||
|
||
* Contributions and contact
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: contributions-and-contact
|
||
:END:
|
||
|
||
Feedback and MRs are very welcome. 🙂
|
||
+ [[file:TODO.org]] has a long list of tasks
|
||
+ [[file:doc/manual.org]] contains an overview of the codebase, explains various mechanisms and decisions, and has a reference of definitions.
|
||
|
||
If you have tried using Chronometrist, I'd love to hear your experiences! Get in touch with the author and other Emacs users in the Emacs channel on the Jabber network - [[https://conversations.im/j/emacs@salas.suchat.org][xmpp:emacs@salas.suchat.org?join]] ([[https://inverse.chat/#converse/room?jid=emacs@salas.suchat.org][web chat]])
|
||
|
||
(For help in getting started with Jabber, [[https://xmpp.org/getting-started/][click here]])
|
||
|
||
* License
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: license
|
||
:END:
|
||
|
||
I dream of a world where all software is liberated - transparent, trustable, and accessible for anyone to use or improve. But I don't want to make demands or threats (e.g. via legal conditions) to get there.
|
||
|
||
I'd rather make a request - please do everything you can to help that dream come true. Please Unlicense as much software as you can.
|
||
|
||
Chronometrist is released under your choice of [[https://unlicense.org/][Unlicense]] or the [[http://www.wtfpl.net/][WTFPL]].
|
||
|
||
(See files [[file:UNLICENSE]] and [[file:WTFPL]]).
|
||
|
||
* Thanks
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: thanks
|
||
:END:
|
||
|
||
wasamasa, bpalmer, aidalgol, pjb and the rest of #emacs for their tireless help and support
|
||
|
||
jwiegley for timeclock.el, which we used as a backend in earlier versions
|
||
|
||
blandest for helping me with the name
|
||
|
||
fiete and wu-lee for testing and bug reports
|