ml729 / org-habit-stats

Stats and visuals for Emacs org-mode habits
GNU General Public License v3.0
69 stars 4 forks source link

feature request: support habit range for streak calculation #7

Open connormclaud opened 8 months ago

connormclaud commented 8 months ago

Current behaviour: I have weekly review habit:

* TODO Weekly review                     :important:urgent:checklist
SCHEDULED: <2024-03-23 Sat ++7d/8d>

Main idea it will be reviewed over weekend, whatever Saturday or Sunday works fine

But with current implementation of stats module it only aware of habit to be closed every 7 days

Weekly review
Repeats every ++7 days
Current Streak 1

So whenever I mark habit as DONE on Sundays I loose my streak which makes me unhappy.

Desired behavior:

Weekly review
Repeats every ++8 days (ideally every 7)
Current Streak 52
leafarbelm commented 6 months ago

Doing something like this seems to work fine

  (defun org-habit-stats-insert-habit-info (habit-data habit-name habit-description)
    (let ((habit-repeat-period (nth 1 habit-data))
          (habit-deadline-repeater (nth 3 habit-data))
          (habit-repeat-string (nth 5 habit-data))
          (habit-next-scheduled (nth 0 habit-data)))
      (insert (propertize habit-name 'face 'org-habit-stats-habit-name)
              "\n")
      (if habit-description
          (insert habit-description "\n"))
      ;; insert habit repeat data, next due date
      (if habit-deadline-repeater
          (insert (format "Repeats every %s%d days (ideally every %s%d days)"
                          habit-repeat-string habit-deadline-repeater habit-repeat-string habit-repeat-period)
                  "\n")
        (insert (format "Repeats every %s%d days"
                        habit-repeat-string habit-repeat-period)
                "\n"))
      (insert (org-habit-stats-format-absolute-time-string "Next Scheduled: %A, %B %d, %Y"
                                                           (org-habit-stats-days-to-time habit-next-scheduled))
              "\n")))

  (defun org-habit-stats--record-streak-full (history _history-rev _habit-data)
    "Return (record-streak . record-day).

The record-day is the last day of the record streak. If the
record streak occurs on multiple days, return the earliest one.

See the docstring of `org-habit-stats-streak' for a description
of HISTORY, HISTORY-REV, HABIT-DATA."
    (let ((record-streak 0)
          (record-day (org-today))
          (curr-streak 0)
          (curr-streak-start 0)
          (deadline-repeater (nth 3 _habit-data)))
      (while history
        (let* ((next-pair (pop history))
               (curr-day (car next-pair))
               (curr-completed (cdr next-pair)))
          (if (= curr-completed 1)
              (progn
                (when (= curr-streak 0)
                  (setq curr-streak-start curr-day))
                (if deadline-repeater ;; reset deadline repeater
                    (setq deadline-repeater (nth 3 _habit-data)))
                (setq curr-streak (1+ curr-streak)))
            (progn
              (if deadline-repeater
                  (setq deadline-repeater (1- deadline-repeater)))
              (if (or (not deadline-repeater) (< deadline-repeater 0))
                  (setq curr-streak 0))))
          (when (> curr-streak record-streak)
            (setq record-streak curr-streak)
            (setq record-day (+ curr-streak-start curr-streak -1)))))
      (cons record-streak record-day)))

  (defun org-habit-stats--streak (history habit-data)
    (let* ((streak 0)
           (deadline-repeater (nth 3 habit-data)))
      (while history
        (if (= (cdr (pop history)) 1)
            (progn
              (setq streak (1+ streak))
              (if deadline-repeater ;; reset deadline repeater
                  (setq deadline-repeater (nth 3 habit-data))))
          (progn
            (if deadline-repeater
                (setq deadline-repeater (1- deadline-repeater)))
            (if (or (not deadline-repeater) (< deadline-repeater 0))
                (setq history '())))
          ))
      streak))

  (defun org-habit-stats-streak (_history history-rev _habit-data)
    (org-habit-stats--streak history-rev _habit-data))

I don't know much about Elisp, but maybe this could be a starting point. I just rewrote some functions, and there are probably better ways of doing this.

I would love to see something like this implemented.