changies
This commit is contained in:
207
org/index.org
207
org/index.org
@@ -1,6 +1,211 @@
|
||||
#+TITLE: Index
|
||||
* Hello There!
|
||||
Welcome to my website! I'm [[./about.org][akk0]]. I blog about Emacs, programming, meditation, otaku stuff, and other things that interest me. You can view the full list of pages on the [[./sitemap.org][sitemap]].
|
||||
|
||||
This site launched recently and is still under construction; please pardon the dust! I maintain a [[./projects/blog.org][somewhat up-to-date TO-DO list]]; don't hestitate to reach out to me through the email address in the footer for feedback or feature suggestions ^^".
|
||||
|
||||
Find me on [[https://bsky.app/profile/webbieweb.org][bsky]]!
|
||||
Here's this week's [[./journal/w43-2025.org][journal]]!
|
||||
|
||||
** /Pro Tips/
|
||||
- You can fold and unfold sections by clicking the headline. Try it!
|
||||
- Click on any cell with a dotted outline in the habit tracker to see my journal entry for it. The currently selected cell will be highlighted in pink.
|
||||
|
||||
** Habits
|
||||
#+BEGIN_CENTER
|
||||
/Today is Saturday, 25th October./
|
||||
#+END_CENTER
|
||||
#+BEGIN_SRC emacs-lisp :exports results :results value html :cache no
|
||||
(defun akk0/org-to-html (org-string)
|
||||
"Convert ORG-STRING to HTML."
|
||||
(with-temp-buffer
|
||||
(insert org-string)
|
||||
(org-mode)
|
||||
(org-export-as 'html nil nil t nil)))
|
||||
|
||||
(defun akk0/sort-habits (habit-alist)
|
||||
"Sort habit-alist by a predefined order of custom-ids."
|
||||
(let ((order '("dailies-blogging" "dailies-meditation" "dailies-french" "dailies-engineering"
|
||||
"dailies-exercise" "dailies-drawing" "dailies-reading" "dailies-social")))
|
||||
(sort (copy-sequence habit-alist)
|
||||
(lambda (a b)
|
||||
(let ((pos-a (or (cl-position (car a) order :test #'equal) 999))
|
||||
(pos-b (or (cl-position (car b) order :test #'equal) 999)))
|
||||
(< pos-a pos-b))))))
|
||||
|
||||
|
||||
(defun akk0/extract-habits (file)
|
||||
"Extract habits with date context from FILE.
|
||||
Returns a list of plists with :custom-id, :todo-state, :date, :day-of-year."
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(org-mode)
|
||||
(let (results)
|
||||
(org-element-map (org-element-parse-buffer) 'headline
|
||||
(lambda (hl)
|
||||
(let ((custom-id (org-element-property :CUSTOM_ID hl))
|
||||
(todo-state (org-element-property :todo-keyword hl)))
|
||||
(when (and custom-id todo-state)
|
||||
;; Get parent properties for context
|
||||
(let* ((parent (org-element-property :parent hl))
|
||||
(day-of-year (or (org-element-property :DAILIES-DAY parent)
|
||||
(let ((grandparent (org-element-property :parent parent)))
|
||||
(when grandparent
|
||||
(org-element-property :DAILIES-DAY grandparent)))))
|
||||
(body (org-element-interpret-data
|
||||
(org-element-contents hl))))
|
||||
(push (list :custom-id custom-id
|
||||
:todo-state todo-state
|
||||
:day-of-year day-of-year
|
||||
:file file
|
||||
:body body)
|
||||
results))))))
|
||||
(nreverse results))))
|
||||
|
||||
(defun akk0/extract-all-habits (files)
|
||||
(mapcan #'akk0/extract-habits files))
|
||||
|
||||
(setq akk0/journal-files
|
||||
(directory-files "~/Blog/org/journal/" t "^w.*\\.org$"))
|
||||
|
||||
(defun akk0/habits-alist (habits)
|
||||
"Transform HABITS list into nested alists: custom-id → day-of-year → habit-data."
|
||||
(let (result)
|
||||
(dolist (habit habits)
|
||||
(let* ((custom-id (plist-get habit :custom-id))
|
||||
(day-of-year (plist-get habit :day-of-year))
|
||||
(todo-state (plist-get habit :todo-state))
|
||||
(body (plist-get habit :body))
|
||||
;; Get the alist for this custom-id
|
||||
(inner-alist (alist-get custom-id result nil nil #'equal))
|
||||
;; Store full data instead of just todo-state
|
||||
(habit-data (list :todo-state todo-state :body body :day-of-year day-of-year))
|
||||
;; Update the inner alist
|
||||
(updated-inner (cons (cons day-of-year habit-data) inner-alist)))
|
||||
;; Update result
|
||||
(setf (alist-get custom-id result nil nil #'equal) updated-inner)))
|
||||
result))
|
||||
|
||||
|
||||
(setq habit-alist (akk0/habits-alist (akk0/extract-all-habits akk0/journal-files)))
|
||||
|
||||
(defun akk0/get-habit-history (habit-alist custom-id day-number days-back window-size)
|
||||
(let* ((inner-alist (alist-get custom-id habit-alist nil nil #'equal))
|
||||
(result nil)
|
||||
(all-states nil)
|
||||
(score-for-state (lambda (state)
|
||||
(cond ((equal state "NO") -1)
|
||||
((equal state "YES") 1)
|
||||
((equal state "EXCELLENT") 2)
|
||||
(t 0)))))
|
||||
(dotimes (i days-back)
|
||||
(let* ((current-day (+ (- day-number days-back) i 1))
|
||||
(current-day-str (number-to-string current-day))
|
||||
(habit-data (alist-get current-day-str inner-alist nil nil #'equal))
|
||||
(todo-state (if habit-data
|
||||
(plist-get habit-data :todo-state)
|
||||
"NODATA"))
|
||||
(body (if habit-data
|
||||
(plist-get habit-data :body)
|
||||
""))
|
||||
|
||||
(doy (if habit-data
|
||||
(plist-get habit-data :day-of-year)
|
||||
""))
|
||||
)
|
||||
(push todo-state all-states)
|
||||
(let* ((window-states (seq-take all-states window-size))
|
||||
(rolling-score (apply #'+ (mapcar score-for-state window-states))))
|
||||
(push (list :todo-state todo-state
|
||||
:score (max 1 (min 5 (/ (+ rolling-score 5) 2)))
|
||||
:body body
|
||||
:doy doy)
|
||||
result))))
|
||||
(nreverse result)))
|
||||
|
||||
(defun akk0/habits-to-html-table (habit-alist day-number days-back window-size)
|
||||
"Generate HTML table of habits with rolling scores.
|
||||
Rows are custom-ids, columns are days."
|
||||
(let ((color-map '(("NODATA" . "grey")
|
||||
("YES" . "green")
|
||||
("NO" . "red")
|
||||
("FREED" . "purple")
|
||||
("EXCELLENT" . "blue")))
|
||||
(symbol-map '(("NODATA" . "")
|
||||
("YES" . "●")
|
||||
("NO" . "×")
|
||||
("FREED" . "♣")
|
||||
("EXCELLENT" . "♦")))
|
||||
|
||||
(sorted-habits (akk0/sort-habits habit-alist))
|
||||
(html ""))
|
||||
;; Start table
|
||||
(setq html (concat html "<table style='margin-left: auto; margin-right:auto; margin-bottom: 0.8rem;'>\n"))
|
||||
|
||||
;; Header row with day numbers
|
||||
(setq html (concat html " <tr>\n <th></th>\n"))
|
||||
(dotimes (i days-back)
|
||||
(let ((day (+ (- day-number days-back) i 1)))
|
||||
(setq html (concat
|
||||
html
|
||||
(cond ((= day day-number) "<th>●</th>")
|
||||
((= 0 (% (- day day-number) 7)) "<th>○</th>")
|
||||
(t "<th />"))))))
|
||||
(setq html (concat html " </tr>\n"))
|
||||
|
||||
|
||||
;; Data rows - one per habit
|
||||
(dolist (entry sorted-habits)
|
||||
(let* ((custom-id (car entry))
|
||||
(history (akk0/get-habit-history habit-alist custom-id day-number days-back window-size)))
|
||||
(setq html (concat html (format " <tr>\n <td style='padding-right: 20px; padding-top: 5px; padding-bottom: 5px;'><i>%s</i></td>\n" (capitalize (string-remove-prefix "dailies-" custom-id)))))
|
||||
|
||||
;; Cell for each day
|
||||
(dolist (day-data history)
|
||||
(let* ((todo-state (plist-get day-data :todo-state))
|
||||
(score (plist-get day-data :score))
|
||||
(body (plist-get day-data :body))
|
||||
(doy (plist-get day-data :doy))
|
||||
(body-html (if (and body (not (string-empty-p body)))
|
||||
(akk0/org-to-html body)
|
||||
""))
|
||||
(color (alist-get todo-state color-map nil nil #'equal))
|
||||
(symbol (alist-get todo-state symbol-map nil nil #'equal))
|
||||
(class (format "habit-brightness-%d" score))
|
||||
(style-var (format "--%s%d" color score))
|
||||
(escaped-body (replace-regexp-in-string "\"" """
|
||||
(replace-regexp-in-string "\n" " " body-html)))
|
||||
)
|
||||
(setq html (concat html (format " <td class=\"%s habit-cell\" style=\"background-color:var(%s)\" data-body=\"%s\" onclick=\"showHabitPopup(this)\" data-doy=\"%s\" data-activity=\"%s\"
|
||||
data-status=\"%s\">%s</td>\n"
|
||||
class style-var
|
||||
escaped-body
|
||||
doy
|
||||
custom-id
|
||||
todo-state
|
||||
symbol)))))
|
||||
|
||||
(setq html (concat html " </tr>\n"))))
|
||||
|
||||
;; End table
|
||||
(setq html (concat html "</table>\n"))
|
||||
|
||||
html))
|
||||
|
||||
(akk0/habits-to-html-table habit-alist 298 30 5)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_EXPORT html
|
||||
<span class="center"><b>Key:</b>
|
||||
<span style="color: var(--grey3);">Unknown</span> |
|
||||
<span style="color: var(--red3);">× No</span> |
|
||||
<span style="color: var(--green3);">● Yes</span> |
|
||||
<span style="color: var(--blue3);">♦ Excellent</span> |
|
||||
<span style="color: var(--purple3);">♣ Freed Up</span>
|
||||
</span>
|
||||
<hr />
|
||||
<div class="habit-popup" id="habitPopup">
|
||||
<div class="habit-popup-content" id="habitPopupContent">
|
||||
<span class='center'><i>This section intentionally left blank.</i></span>
|
||||
</div>
|
||||
</div>
|
||||
#+END_EXPORT
|
||||
|
||||
Reference in New Issue
Block a user