track-values.el 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. ;;; track-values.el --- Track calorie values for various foods
  2. ;; Copyright (C) 2020 Samuel W. Flint <swflint@flintfam.org>
  3. ;; author: Samuel W. Flint <swflint@flintfam.org>
  4. ;; Version: 0.1
  5. ;; Keywords: diet, tracking
  6. ;; URL: https://git.sr.ht/~swflint/track-calorie-values
  7. ;; This file is NOT part of Emacs.
  8. ;;
  9. ;; This file is licensed under the GNU GPLv3 or later.
  10. ;;; Commentary:
  11. ;;
  12. ;;; Code:
  13. (require 'cl-lib)
  14. ;;; Customizeable values
  15. (defgroup track-values nil
  16. "Track calorie values for various foods"
  17. :tag "Track Calorie Values"
  18. :group 'org
  19. :link '(url-link "https://git.sr.ht/~swflint/track-calorie-values")
  20. :prefix "track-values-")
  21. (defcustom track-values-save-file
  22. (locate-user-emacs-file "track-values-data.el")
  23. "File to store calorie values of food items."
  24. :type '(file :must-match t))
  25. ;;; Hashing
  26. ;; Taken from emacs documentation
  27. (defun case-fold-string= (a b)
  28. (eq t (compare-strings a nil nil b nil nil t)))
  29. (defun case-fold-string-hash (a)
  30. (sxhash-equal (upcase a)))
  31. (define-hash-table-test 'case-fold
  32. 'case-fold-string= 'case-fold-string-hash)
  33. ;;; Internal Data
  34. (defvar track-values-tables (list)
  35. "List of known track-values tables.")
  36. (defvar track-values-loaded-p nil
  37. "Have values been loaded?")
  38. (defvar track-values-modified-p t
  39. "Have values been modified?")
  40. (defvar track-values-current-key nil
  41. "Current item being recorded.")
  42. ;;; Table creation
  43. (defun track-values-make-table (name &optional pairs)
  44. (pushnew name track-values-tables)
  45. (let ((table-name (intern (format "track-values-%s-table" name)))
  46. (prompt-name (intern (format "track-values-%s-keys" name))))
  47. (set table-name (make-hash-table :weakness nil :test 'case-fold))
  48. (set prompt-name (mapcar #'first pairs))
  49. (mapcar #'(lambda (x) (puthash (first x) (second x) (symbol-value table-name))) pairs)
  50. (setf track-values-modified-p t)))
  51. ;;; Get and set values
  52. (defun track-values-get-value (table key)
  53. (gethash key (symbol-value (intern (format "track-values-%s-table" table)))))
  54. (defun track-values-set-value (table key value)
  55. (puthash key value (symbol-value (intern (format "track-values-%s-table" table))))
  56. (cl-pushnew key (symbol-value (intern (format "track-values-%s-keys" table))) :test #'string=)
  57. (setf track-values-modified-p t))
  58. ;;; I/O functions
  59. (defun track-values-load-data ()
  60. "Load calorie data."
  61. (when (file-exists-p track-values-save-file)
  62. (load track-values-save-file t t t)
  63. (setf track-values-loaded-p t
  64. track-values-modified-p nil)))
  65. (defun track-values-save-data ()
  66. "Write calorie data."
  67. (when track-values-modified-p
  68. (with-temp-buffer
  69. (insert ";; generated by track-values.el -*- mode: lisp-data -*-\n"
  70. ";; do not modify by hand\n\n")
  71. (let ((standard-output (current-buffer)))
  72. (mapcar #'(lambda (name)
  73. (print `(track-values-make-table ',name ',(mapcar (lambda (x)
  74. (list x (track-values-get-value name x)))
  75. (hash-table-keys (symbol-value (intern (format "track-values-%s-table" name))))))))
  76. track-values-tables))
  77. (write-region (point-min) (point-max) track-values-save-file)
  78. (setf track-values-modified-p nil))))
  79. ;;; Primary interaction functions
  80. (defun track-values-complete (&optional name)
  81. (let ((all-keys (remove-duplicates (loop for name in track-values-tables appending (symbol-value (intern (format "track-values-%s-keys" name)))))))
  82. (if name
  83. (completing-read "Item: " (symbol-value (intern (format "track-values-%s-keys" name)))
  84. #'identity nil nil nil nil t)
  85. (completing-read "Item: " all-keys
  86. #'identity nil nil nil nil t))))
  87. (defun track-values-start (&optional name)
  88. "Get a food item to retrieve or prompt for calories."
  89. (unless track-values-loaded-p
  90. (track-values-load-data))
  91. (let ((key (track-values-complete name)))
  92. (if key
  93. (setf track-values-current-key (downcase key))
  94. (user-error "A key must be provided"))))
  95. (defun track-values-insert (name)
  96. "Get or prompt for the number of calories in the current food item."
  97. (let ((item (track-values-get-value name track-values-current-key)))
  98. (if item
  99. (format "%s" item)
  100. (let ((item (read-string (format "%s in \"%s\": " name track-values-current-key))))
  101. (track-values-set-value name track-values-current-key item)
  102. (format "%s" item)))))
  103. (defun track-values-end ()
  104. (setf track-values-current-key nil))
  105. (provide 'track-values)
  106. ;;; track-values.el ends here