diff options
-rw-r--r-- | gnosis-algorithm.el | 29 | ||||
-rw-r--r-- | gnosis.el | 51 |
2 files changed, 62 insertions, 18 deletions
diff --git a/gnosis-algorithm.el b/gnosis-algorithm.el index ad663e4..075e669 100644 --- a/gnosis-algorithm.el +++ b/gnosis-algorithm.el @@ -123,13 +123,18 @@ Optional integer OFFSET is a number of days from the current date." (+ offset (calendar-absolute-from-gregorian now)))))) (list (nth 2 date) (nth 0 date) (nth 1 date))))) -(defun gnosis-algorithm-date-diff (date) - "Find the difference between the current date and the given DATE. +(defun gnosis-algorithm-date-diff (date &optional date2) + "Find the difference between DATE2 and DATE. + +If DATE2 is nil, current date will be used instead. DATE format must be given as (year month day)." - (let ((given-date (encode-time 0 0 0 (caddr date) (cadr date) (car date)))) - (- (time-to-days (current-time)) - (time-to-days given-date)))) + (let* ((given-date (encode-time 0 0 0 (caddr date) (cadr date) (car date))) + (date2 (if date2 (encode-time 0 0 0 (caddr date2) (cadr date2) (car date2)) + (current-time))) + (diff (- (time-to-days date2) + (time-to-days given-date)))) + (if (>= diff 0) diff (error "`DATE2' must be higher than `DATE'")))) (cl-defun gnosis-algorithm-next-ef (&key ef success increase decrease threshold c-successes c-failures) @@ -179,13 +184,21 @@ successful reviews." ;; This should only occur in testing env or when the user has made breaking changes. (cl-assert (> (nth 2 ef) 1) "Total ef value must be above 1") (let* ((ef (nth 2 gnosis-algorithm-ef)) + ;; If last-interval is 0, use 1 instead. + (last-interval (if (<= last-interval 0) 1 last-interval)) (interval (cond ((and (= successful-reviews 0) success) (car initial-interval)) ((and (= successful-reviews 1) success) (cadr initial-interval)) - (t (if success - (* ef last-interval) - (* failure-factor last-interval)))))) + ;; If it's still on initial stage, review the + ;; same day + ((and (< successful-reviews 2) (not success)) 0) + (t (let* ((success-interval (* ef last-interval)) + (failure-interval (* last-interval failure-factor))) + (if success success-interval + ;; Make sure failure interval is never + ;; higher than success + (min success-interval failure-interval))))))) (gnosis-algorithm-date (round interval)))) @@ -112,7 +112,7 @@ When nil, the image will be displayed at its original size." (make-directory gnosis-dir) (make-directory gnosis-images-dir)) -(defconst gnosis-db +(defvar gnosis-db (emacsql-sqlite-open (expand-file-name "gnosis.db" gnosis-dir)) "Gnosis database file.") @@ -486,6 +486,27 @@ Set SPLIT to t to split all input given." "Return id for DECK name." (gnosis-get 'id 'decks `(= name ,deck))) +(defun gnosis-get-deck--note (id &optional name) + "Get deck id for note ID. + +If NAME is t, return name of deck." + (let* ((id-clause `(= id ,id)) + (deck (gnosis-get 'deck-id 'notes id-clause))) + (if name (gnosis--get-deck-name deck) deck))) + +(defun gnosis-get-deck-ff (id) + "Return failure factor for deck of ID." + (let* ((id-clause `(= id ,id)) + (deck-ff (gnosis-get 'failure-factor 'decks id-clause))) + deck-ff)) + +(defun gnosis-get-note-ff (id) + "Return failure factor for note ID." + (let ((deck-ff (gnosis-get-deck-ff (gnosis-get-deck--note id))) + (note-ff (gnosis-get 'ff 'review `(= id ,id)))) + (if (and deck-ff (> deck-ff note-ff)) + deck-ff + note-ff))) (cl-defun gnosis-suspend-note (id) "Suspend note with ID." @@ -1080,20 +1101,33 @@ well." due-notes) :test #'equal))) +(defun gnosis-review--get-offset (id) + "Return offset for note with value of id ID." + (let ((last-rev (gnosis-get 'last-rev 'review-log `(= id ,id)))) + (gnosis-algorithm-date-diff last-rev))) + +(defun gnosis-review-last-interval (id) + "Return last review interval for note ID." + (let* ((where-id-clause `(= id ,id)) + (last-rev (gnosis-get 'last-rev 'review-log where-id-clause)) + (rev-date (gnosis-get 'next-rev 'review-log where-id-clause))) + (gnosis-algorithm-date-diff last-rev rev-date))) + (defun gnosis-review-algorithm (id success) "Return next review date & ef for note with value of id ID. SUCCESS is a boolean value, t for success, nil for failure. Returns a list of the form ((yyyy mm dd) (ef-increase ef-decrease ef-total))." - (let ((ff gnosis-algorithm-ff) + (let ((ff (gnosis-get-note-ff id)) (ef (gnosis-get 'ef 'review `(= id ,id))) (t-success (gnosis-get 't-success 'review-log `(= id ,id))) ;; total successful reviews (c-success (gnosis-get 'c-success 'review-log `(= id ,id))) ;; consecutive successful reviews (c-fails (gnosis-get 'c-fails 'review-log `(= id ,id))) ;; consecutive failed reviews ;; (t-fails (gnosis-get 't-fails 'review-log `(= id ,id))) ;; total failed reviews ;; (review-num (gnosis-get 'n 'review-log `(= id ,id))) ;; total reviews - (last-interval (max (gnosis-review--get-offset id) 1))) ;; last interval + ;; (last-interval (max (gnosis-review--get-offset id) 1)) + (last-interval (gnosis-review-last-interval id))) ;; last interval (list (gnosis-algorithm-next-interval :last-interval last-interval :ef ef :success success @@ -1108,11 +1142,6 @@ Returns a list of the form ((yyyy mm dd) (ef-increase ef-decrease ef-total))." :c-successes c-success :c-failures c-fails)))) -(defun gnosis-review--get-offset (id) - "Return offset for note with value of id ID." - (let ((last-rev (gnosis-get 'last-rev 'review-log `(= id ,id)))) - (gnosis-algorithm-date-diff last-rev))) - (defun gnosis-review--update (id success) "Update review-log for note with value of id ID. @@ -1590,6 +1619,8 @@ to improve readability." (defun gnosis-review () "Start gnosis review session." (interactive) + ;; Refresh modeline + (setq gnosis-due-notes-total (length (gnosis-review-get-due-notes))) (let ((review-type (funcall gnosis-completing-read-function "Review: " '("Due notes" "Due notes of deck" "Due notes of specified tag(s)" @@ -1842,7 +1873,8 @@ DASHBOARD-TYPE: either 'Notes' or 'Decks' to display the respective dashboard." ("notes" (gnosis-dashboard-output-notes (gnosis-collect-note-ids))) ("decks" (gnosis-dashboard-output-decks)) ("tags" (gnosis-dashboard-output-notes (gnosis-collect-note-ids :tags t))) - ("search" (gnosis-dashboard-output-notes (gnosis-collect-note-ids :query (read-string "Search for note: ")))))) + ("search" (gnosis-dashboard-output-notes + (gnosis-collect-note-ids :query (read-string "Search for note: ")))))) (tabulated-list-print t))) (defun gnosis-db-init () @@ -1894,7 +1926,6 @@ DASHBOARD-TYPE: either 'Notes' or 'Decks' to display the respective dashboard." (and (listp item) (eq (car item) :eval) (string-prefix-p " G:" (format "%s" (eval (cadr item)))))) global-mode-string)) - (run-at-time "5 min" 300 #'(lambda () (setq gnosis-due-notes-total (length (gnosis-review-get-due-notes))))) (force-mode-line-update))) (define-derived-mode gnosis-mode special-mode "Gnosis" |