This repository has been archived on 2022-05-13. You can view files and clone it, but cannot push or open issues or pull requests.
Go to file
contrapunctus 059579e841 TODO: Remove async.el discussion, add tasks 2022-04-01 20:26:07 +05:30
doc Add screenshot 2022-02-20 15:54:53 +05:30
elisp Fix empty output and emacsql-close error 2022-04-01 20:23:47 +05:30
features Reindent ecukes feature definition 2020-05-28 00:59:16 +05:30
tests Add more test cases for insert 2022-01-14 21:48:32 +05:30
.dir-locals.el Reword point, add deprecation notice 2022-02-11 00:19:18 +05:30
.gitignore Update .gitignore 2019-08-05 01:00:36 +05:30
CHANGELOG.md Add key-value presets 2022-02-23 20:47:46 +05:30
Makefile Merge branch 'dev' into sqlite 2022-03-21 09:19:17 +05:30
README.org Use manual.org as readme 2022-02-20 15:54:53 +05:30
TODO.org TODO: Remove async.el discussion, add tasks 2022-04-01 20:26:07 +05:30
UNLICENSE Rename license files 2020-06-30 08:48:54 +05:30
WTFPL Rename license files 2020-06-30 08:48:54 +05:30
manual.org Remove MELPA Stable recommendation 2022-02-20 16:46:48 +05:30
org-doom-molokai.css [CSS] use heading colors for links in headings 2021-05-31 09:22:07 +05:30

README.org

Chronometrist - an extensible time tracker for Emacs

Donate using Liberapay

Chronometrist is a friendly and powerful personal time tracker and analyzer for Emacs.

/contrapunctus/chronometrist/media/branch/async-migrate/doc/2022-02-20%2013-26-53.png
The main Chronometrist buffer, with the enabled extensions chronometrist-goal ("Targets" column + alerts) and chronometrist-spark ("Graph" column displaying the activity for the past 4 weeks).

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
  6. Fancy graphs with the chronometrist-spark extension

Limitations

  1. No support for concurrent tasks.

Comparisons

timeclock.el

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

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 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

from MELPA

  1. Set up MELPA - https://melpa.org/#/getting-started
  2. M-x package-install RET chronometrist RET

from Git

You can get chronometrist from https://tildegit.org/contrapunctus/chronometrist or https://codeberg.org/contrapunctus/chronometrist

chronometrist requires

Add the "elisp/" subdirectory to your load-path, and (require 'chronometrist).

Usage

chronometrist

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

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

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

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 chronometrist-goal.el.

How-to

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

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

(add-to-list 'load-path "<directory containing chronometrist.org>")

(require 'literate-elisp) ;; or autoload, use-package, ...
(literate-elisp-load "chronometrist.org")

How to attach tags to time intervals

  1. Add chronometrist-tags-add to one or more of these hooks 1 -

    (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)
  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.

How to attach key-values to time intervals

  1. Add chronometrist-kv-add to one or more of these hooks 1 -

    (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)

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.

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

An idea from the author's own init -

(defun my-start-project (project)
  (pcase project
    ("Guitar"
     (find-file-other-window "~/repertoire.org"))
    ;; ...
    ))

(add-hook 'chronometrist-before-in-functions 'my-start-project)

How to warn yourself about uncommitted changes

Another one, prompting the user if they have uncommitted changes in a git repository (assuming they use Magit) -

(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)

How to display the current time interval in the activity indicator

(defun my-activity-indicator ()
  (--> (chronometrist-latest-record (chronometrist-active-backend))
       (plist-put it :stop (chronometrist-format-time-iso8601))
       (list it)
       (chronometrist-events-to-durations it)
       (-reduce #'+ it)
       (truncate it)
       (chronometrist-format-duration it)))

(setq chronometrist-activity-indicator #'my-activity-indicator)

How to back up your Chronometrist data

I suggest backing up Chronometrist data on each save using the async-backup package.2 Here's how you can do that.

  1. Add the following to your init.

    (use-package async-backup)
  2. Open your Chronometrist file and add async-backup to a buffer-local after-save-hook.

    M-x chronometrist-open-log
    M-x add-file-local-variable-prop-line RET eval RET (add-hook 'after-save-hook #'async-backup nil t) RET
    
  3. Optionally, configure backup-directory-alist to set a specific directory for the backups.

How to configure Vertico for use with Chronometrist

By default, Vertico uses its own sorting function - for some commands (such as chronometrist-key-values-unified-prompt) this results in worse suggestions, since Chronometrist sorts suggestions in most-recent-first order.

You can either disable Vertico's sorting entirely -

(setq vertico-sort-function nil)

Or use vertico-multiform to disable sorting for only specific commands -

(use-package vertico-multiform
  :init (vertico-multiform-mode)
  :config
  (setq vertico-multiform-commands
        '((chronometrist-toggle-task          (vertico-sort-function . nil))
          (chronometrist-toggle-task-no-hooks (vertico-sort-function . nil))
          (chronometrist-key-values-unified-prompt      (vertico-sort-function . nil)))))

Explanation

Literate Program

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 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 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

Feedback and MRs are very welcome. 🙂

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 - xmpp:emacs@salas.suchat.org?join (web chat)

(For help in getting started with Jabber, click here)

License

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 Unlicense or the WTFPL.

(See files UNLICENSE and WTFPL).

Thanks

The main buffer and the report buffer are copied from the Android application, A Time Tracker

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


1

but not chronometrist-before-in-functions

2

It is possible to use Emacs' built-in backup system to do it, but since it is synchronous, doing so will greatly slow down saving of the Chronometrist file.