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.
chronometrist/elisp/chronometrist-spark.org

6.6 KiB

chronometrist-spark

Library headers and commentary

;;; chronometrist-spark.el --- Show sparklines in Chronometrist buffers -*- lexical-binding: t; -*-

;; Author: contrapunctus <xmpp:contrapunctus@jabjab.de>
;; Maintainer: contrapunctus <xmpp:contrapunctus@jabjab.de>
;; Keywords: calendar
;; Homepage: https://tildegit.org/contrapunctus/chronometrist
;; Package-Requires: ((emacs "25.1") (chronometrist "0.7.0") (spark "0.1"))
;; Version: 0.1.0

;; This is free and unencumbered software released into the public domain.
;;
;; Anyone is free to copy, modify, publish, use, compile, sell, or
;; distribute this software, either in source code form or as a compiled
;; binary, for any purpose, commercial or non-commercial, and by any
;; means.
;;
;; For more information, please refer to <https://unlicense.org>

"Commentary" is displayed when the user clicks on the package's entry in M-x list-packages.

;;; Commentary:
;;
;; This package adds a column to Chronometrist displaying sparklines for each task.

Dependencies

;;; Code:
;; This file was automatically generated from chronometrist-spark.org.
(require 'chronometrist)
(require 'spark)

Code

custom group   custom group

(defgroup chronometrist-spark nil
  "Show sparklines in `chronometrist'."
  :group 'applications)

length   custom variable

(defcustom chronometrist-spark-length 7
  "Length of each sparkline in number of days."
  :type 'integer)

show-range   custom variable

(defcustom chronometrist-spark-show-range t
  "If non-nil, display range of each sparkline."
  :type 'boolean)

range   function

(defun chronometrist-spark-range (durations)
  "Return range for DURATIONS as a string.
DURATIONS must be a list of integer seconds."
  (let* ((duration-minutes  (--map (/ it 60) durations))
         (durations-nonzero (seq-remove #'zerop duration-minutes))
         (length            (length durations-nonzero)))
    (cond ((not durations-nonzero) "")
          ((> length 1)
           (format "(%sm~%sm)" (apply #'min durations-nonzero)
                   (apply #'max duration-minutes)))
          ((= 1 length)
           ;; This task only had activity on one day in the given
           ;; range of days - these durations, then, cannot really
           ;; have a minimum and maximum range.
           (format "(%sm)" (apply #'max duration-minutes))))))

tests

(ert-deftest chronometrist-spark-range ()
  (should (equal (chronometrist-spark-range '(0 0 0))    ""))
  (should (equal (chronometrist-spark-range '(0 1 2))    ""))
  (should (equal (chronometrist-spark-range '(60 0 0))   "(1m)"))
  (should (equal (chronometrist-spark-range '(60 0 120)) "(1m~2m)")))

durations   function

(defun chronometrist-spark-durations (task length stop-ts)
  "Return a list of durations for time tracked for TASK in the last LENGTH days before STOP-TS."
  (cl-loop for day from (- (- length 1)) to 0
    collect
    (chronometrist-task-time-one-day task (ts-adjust 'day day stop-ts))))

TODO row-transformer   function

if larger than 7 add space after (% length 7)th element then add space after every 7 elements

  • if task has no time tracked for it - ""
  • don't display 0 as the minimum time tracked
  • if task has only one day of time tracked - "(40m)"
(defun chronometrist-spark-row-transformer (row)
  "Add a sparkline cell to ROW.
Used to add a sparkline column to `chronometrist-rows'.

ROW must be a valid element of the list specified by
`tabulated-list-entries'."
  (-let* (((task vector) row)
          (durations (chronometrist-spark-durations task chronometrist-spark-length (ts-now)))
          (sparkline (if (and (not (seq-every-p #'zerop durations))
                              chronometrist-spark-show-range)
                         (format "%s %s" (spark durations) (chronometrist-spark-range durations))
                       (format "%s" (spark durations)))))
    (list task (vconcat vector `[,sparkline]))))

TODO schema-transformer   function

calculate length while accounting for space

(defun chronometrist-spark-schema-transformer (schema)
  "Add a sparkline column to SCHEMA.
Used to add a sparkline column to `chronometrist-schema-transformers'.
SCHEMA should be a vector as specified by `tabulated-list-format'."
  (vconcat schema `[("Graph"
                     ,(if chronometrist-spark-show-range
                        (+ chronometrist-spark-length 12)
                        chronometrist-spark-length)
                     t)]))

setup   writer

(defun chronometrist-spark-setup ()
  "Add `chronometrist-sparkline' functions to `chronometrist' hooks."
  (add-to-list 'chronometrist-row-transformers     #'chronometrist-spark-row-transformer)
  (add-to-list 'chronometrist-schema-transformers  #'chronometrist-spark-schema-transformer))

teardown   writer

(defun chronometrist-spark-teardown ()
  "Remove `chronometrist-sparkline' functions from `chronometrist' hooks."
  (setq chronometrist-row-transformers
        (remove #'chronometrist-spark-row-transformer chronometrist-row-transformers)
        chronometrist-schema-transformers
        (remove #'chronometrist-spark-schema-transformer chronometrist-schema-transformers)))

minor-mode   minor mode

(define-minor-mode chronometrist-spark-minor-mode
  nil nil nil nil
  ;; when being enabled/disabled, `chronometrist-spark-minor-mode' will already be t/nil here
  (if chronometrist-spark-minor-mode (chronometrist-spark-setup) (chronometrist-spark-teardown)))

Provide

(provide 'chronometrist-spark)
;;; chronometrist-spark.el ends here