Stegrapporter med ledger

Det känns lite barockt att använda ett bokföringsverktyg för just bokföring. Att använda det för att hålla koll på sin stegräkning känns givetvis helt rimligt.

Jag har en fil som definierar olika aktiviteter och hur många steg dessa motsvarar. Dessa data har jag lånat från en godkänd sida. Filen kallar jag aktiviteter.prices, och innehåller aktiviteter jag ibland gör och vad dessa motsvarar, nedan ett litet utdrag:

P 2000-01-01 00:00:00 kilo_steg 1000 steg
P 2000-01-01 00:00:00 h_stå 2400 steg
P 2000-01-01 00:00:00 h_gym_latt 4800 steg

Denna fil läser jag sedan in i min huvudfil, steg.ledger.

include aktiviteter.prices

2019-04-01 * Måndag
    Totalt
    Stå                                           -2 h_stå
    Steg                                        -9.4 kilo_steg

2019-04-02 * Tisdag
    Totalt
    Stå                                           -2 h_stå
    Steg                                        -7.5 kilo_steg

2019-04-03 * Onsdag
    Totalt
    Stå                                           -3 h_stå
    Steg                                      -13556 steg

Notera precis hur många dagar jag använde enheten ”kilo_steg”!

När jag varje månad kollar in hur många steg det har blivit summerar jag med ett anrop till ledger som nedan:

ledger -f steg.ledger --period "this month" --value --monthly reg Totalt

Om jag inte tar med ”Totalt” på slutet får jag en summering av de olika posterna och hur de har bidragit. Sätter jag perioden till ”this year” får jag summerat hur varje månad har blivit totalt, hur många steg/motsvarande steg jag har tagit och så vidare. Det är ingen avancerad funktionalitet direkt, men det är genomtänkt implementerat och enkelt att använda.

Tidrapporter med ledger

Jag har ju en förkärlek till att göra saker dels krångliga, men även att göra dem krångligare med hjälp av Emacs. Nu har jag äntligen hittat ett sätt att blanda in ytterligare ett steg i kedjan, nämligen ledger!

Jag för min tiddagbok för jobbet med hjälp av en textfil som tolkas av programmet ledger, som i sin tur rapporterar på lite olika sätt. Formatet är jätteenkelt, i princip:

i 2019-08-23 08:00:00 Agio:InternTid
o 2019-08-23 12:00:00

Ovan är också en ren lögn – jag är aldrig på jobbet så tidigt! Men ovan är att jag kom in vid 08:00:00 och startade med något internt för jobbet, och gick på lunch vid 12. Jag behöver inte fördefiniera några kunder eller kostnadsställen, utan jag fyller bara på. Formatet är tekniskt sett inte ett rent ledger-format, men ledger har arbetat in stöd för det ändå.

Men, för att göra livet lite lättare skrev jag lite elisp, se nedan. Står jag i en buffer där major-mode är satt till ledger-mode så kallar jag bara på den interaktiva funktionen monotux/check-in och svarar på frågorna, så infogas svaren längst ner i filen. Koden är inte vacker, Men Den Funkar Bra Nog(tm) så vi kör på den!

;; https://emacs.stackexchange.com/a/7150
(defun monotux/re-seq (regexp string)
  "Get a list of all REGEXP matches in a STRING."
  (save-match-data
    (let ((pos 0)
          matches)
      (while (string-match regexp string pos)
        (let ((payee (match-string 1 string)))
          (unless (member payee matches)
            (push (match-string 1 string) matches)))
        (setq pos (match-end 0)))
      matches)))

(defun monotux/get-timeclock-candidates (buffer)
  "Extracts available payee candidates from provided BUFFER."
  (let ((regex-payee "^i [0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\} \\(.*\\)$"))
    (monotux/re-seq regex-payee buffer)))

(defun monotux/check-in ()
  "Checka in på jobbet!"
  (interactive)
  (when (derived-mode-p 'ledger-mode)
    (let* ((pbuf (current-buffer))
           (clock-in-time (concat (read-string "När kom du till jobbet? (HH:MM) ") ":00"))
           (current-date (format-time-string "%Y-%m-%d"))
           complete-line)
      (dolist (e `("i" ,current-date ,clock-in-time))
        (add-to-list 'complete-line e t))
      (let* ((payee-candidates (monotux/get-timeclock-candidates (buffer-string)))
             (payee-chosen (completing-read "Vilket konto? " payee-candidates)))
        (add-to-list 'complete-line payee-chosen t))
      (goto-char (point-max))
      (insert (concat "\n" (string-join complete-line " ") "\n")))))

(defun monotux/check-out ()
  "Checka ut också."
  (interactive)
  (when (derived-mode-p 'ledger-mode)
    (let ((clock-out-time (concat (read-string "När checkade du ut? (HH:MM) ") ":00"))
          (current-date (format-time-string "%Y-%m-%d"))
          complete-line)
      (dolist (e `("o" ,current-date ,clock-out-time))
        (add-to-list 'complete-line e t))
      (goto-char (point-max))
      (insert (concat (string-join complete-line " ") "\n")))))

För att sammanställa en veckorapport av min tid kan en använda en kommandorad som nedan:

$ ledger -f time.ledger --daily --period "this week" reg
19-Aug-23 - 19-Aug-23    (Agio:InternTid)       4.00h     4.00h

Det roliga kommer givetvis när en har fler rapporter över fler dagar. Men det lämnas som en övning för läsaren att sammanställa själv! 🙂