Browse Source

Add Track-Values version

Samuel W. Flint 3 years ago
parent
commit
b252a5b85f
1 changed files with 144 additions and 0 deletions
  1. 144 0
      track-values.el

+ 144 - 0
track-values.el

@@ -0,0 +1,144 @@
+;;; track-values.el --- Track calorie values for various foods
+
+;; Copyright (C) 2020 Samuel W. Flint <swflint@flintfam.org>
+
+;; author: Samuel W. Flint <swflint@flintfam.org>
+;; Version: 0.1
+;; Keywords: diet, tracking
+;; URL: https://git.sr.ht/~swflint/track-calorie-values
+
+;; This file is NOT part of Emacs.
+;;
+;;  This file is licensed under the GNU GPLv3 or later.
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'cl-lib)
+
+
+;;; Customizeable values
+
+(defgroup track-values nil
+  "Track calorie values for various foods"
+  :tag "Track Calorie Values"
+  :group 'org
+  :link '(url-link "https://git.sr.ht/~swflint/track-calorie-values")
+  :prefix "track-values-")
+
+(defcustom track-values-save-file
+  (locate-user-emacs-file "track-values-data.el")
+  "File to store calorie values of food items."
+  :type '(file :must-match t))
+
+
+;;; Hashing
+
+;; Taken from emacs documentation
+(defun case-fold-string= (a b)
+  (eq t (compare-strings a nil nil b nil nil t)))
+(defun case-fold-string-hash (a)
+  (sxhash-equal (upcase a)))
+
+(define-hash-table-test 'case-fold
+  'case-fold-string= 'case-fold-string-hash)
+
+
+;;; Internal Data
+(defvar track-values-tables (list)
+  "List of known track-values tables.")
+
+(defvar track-values-loaded-p nil
+  "Have values been loaded?")
+
+(defvar track-values-modified-p t
+  "Have values been modified?")
+
+(defvar track-values-current-key nil
+  "Current item being recorded.")
+
+
+;;; Table creation
+
+(defun track-values-make-table (name &optional pairs)
+  (pushnew name track-values-tables)
+  (let ((table-name (intern (format "track-values-%s-table" name)))
+        (prompt-name (intern (format "track-values-%s-keys" name))))
+    (set table-name (make-hash-table :weakness nil :test 'case-fold))
+    (set prompt-name (mapcar #'first pairs))
+    (mapcar #'(lambda (x) (puthash (first x) (second x) (symbol-value table-name))) pairs)
+    (setf track-values-modified-p t)))
+
+
+;;; Get and set values
+
+(defun track-values-get-value (table key)
+  (gethash key (symbol-value (intern (format "track-values-%s-table" table)))))
+
+(defun track-values-set-value (table key value)
+  (puthash key value (symbol-value (intern (format "track-values-%s-table" table))))
+  (cl-pushnew key (symbol-value (intern (format "track-values-%s-keys" table))) :test #'string=)
+  (setf track-values-modified-p t))
+
+
+;;; I/O functions
+
+(defun track-values-load-data ()
+  "Load calorie data."
+  (when (file-exists-p track-values-save-file)
+    (load track-values-save-file t t t)
+    (setf track-values-loaded-p t
+          track-values-modified-p nil)))
+
+(defun track-values-save-data ()
+  "Write calorie data."
+  (when track-values-modified-p
+    (with-temp-buffer
+      (insert ";; generated by track-values.el        -*- mode: lisp-data -*-\n"
+              ";; do not modify by hand\n\n")
+      (let ((standard-output (current-buffer)))
+        (mapcar #'(lambda (name)
+                    (print `(track-values-make-table ,name ',(mapcar (lambda (x)
+                                                                       (list x (track-values-get-value name x)))
+                                                                     (hash-table-keys (symbol-value (intern (format "track-values-%s-table" name))))))))
+                track-values-tables))
+      (write-region (point-min) (point-max) track-values-save-file)
+      (setf track-values-modified-p nil))))
+
+
+;;; Primary interaction functions
+
+(defun track-values-complete (&optional name)
+  (let ((all-keys (remove-duplicates (loop for name in track-values-tables appending (symbol-value (intern (format "track-values-%s-keys" name)))))))
+    (if name
+        (completing-read "Item: " (symbol-value (intern (format "track-values-%s-keys" name)))
+                         #'identity nil nil nil nil t)
+      (completing-read "Item: " all-keys
+                       #'identity nil nil nil nil t))))
+
+(defun track-values-start (&optional name)
+  "Get a food item to retrieve or prompt for calories."
+  (unless track-values-loaded-p
+    (track-values-load-data))
+  (let ((key (track-values-complete name)))
+    (if key
+        (setf track-values-current-key (downcase key))
+      (user-error "A key must be provided"))))
+
+(defun track-values-insert (name)
+  "Get or prompt for the number of calories in the current food item."
+  (let ((item (track-values-get-value name track-values-current-key)))
+    (if item
+        (format "%s" item)
+      (let ((item (read-string (format "%s in \"%s\": " name track-values-current-key))))
+        (track-values-set-key name track-values-current-key item)
+        (format "%s" item)))))
+
+(defun track-values-end ()
+  (setf track-values-current-key nil))
+
+(provide 'track-values)
+
+;;; track-values.el ends here