diff options
-rw-r--r-- | gnosis-algorithm.el | 28 | ||||
-rw-r--r-- | gnosis-dev.el | 42 | ||||
-rw-r--r-- | gnosis.el | 115 |
3 files changed, 141 insertions, 44 deletions
diff --git a/gnosis-algorithm.el b/gnosis-algorithm.el index ab94559..4107b8a 100644 --- a/gnosis-algorithm.el +++ b/gnosis-algorithm.el @@ -32,12 +32,14 @@ (require 'calendar) (defcustom gnosis-algorithm-interval '(1 3) - "Gnosis initial interval. + "Gnosis initial interval for successful reviews. -Interval by which a new question is displayed or when it's ef is at 1.3. +First item: First interval, +Second item: Second interval. -First item: First interval -Second item: Second interval." +Note: gnosis-algorithm-interval is ignored after 10 TOTAL reviews or +when ef is above > 3.0, which should only be the case for customized +notes/review sessions." :group 'gnosis :type 'list) @@ -94,6 +96,9 @@ The structure of the given date is (YEAR MONTH DAY)." (+ ef (car gnosis-algorithm-ef))) (t (error "Invalid quality score passed to gnosis-algorithm-e-factor")))) +;; This should be further tested for notes with last-interval of 0 when success 0 +;; For future versions of this algorithm, we should also calculate +;; failures in row to have "leech" like notes as well. (defun gnosis-algorithm-next-interval (last-interval n ef success ff successful-reviews) "Calculate next interval. - LAST-INTERVAL : The number of days since the item was last reviewed. @@ -102,6 +107,7 @@ The structure of the given date is (YEAR MONTH DAY)." - SUCCESS : Success of the recall, ranges from 0 (unsuccessful) to 1 (successful). - FF: Failure factor +- SUCCESSFUL-REVIEWS : Number of successful reviews in a row. Returns a tuple: (INTERVAL N EF) where, - Next review date in (year month day) format. @@ -117,19 +123,21 @@ Returns a tuple: (INTERVAL N EF) where, (interval (cond ;; First successful review -> first interval - ((and (= successful-reviews 0) + ((and (= successful-reviews 1) (= success 1) + (< n 10) (< ef 3.0)) (car gnosis-algorithm-interval)) ;; Second successful review -> second interval - ((and (= successful-reviews 1) + ((and (= successful-reviews 2) + (< n 10) (= success 1) (< ef 3.0)) (cadr gnosis-algorithm-interval)) - ;; TESTING - ;; ((and (= last-interval 0) - ;; (= success 1)) - ;; (* ef 1)) + ;; For custom review sessions. + ((and (= last-interval 0) + (= success 1)) + (* ef 1)) (t (if (= success 1) (* ef last-interval) (* ff last-interval)))))) diff --git a/gnosis-dev.el b/gnosis-dev.el index dadbe04..f5003fc 100644 --- a/gnosis-dev.el +++ b/gnosis-dev.el @@ -59,13 +59,21 @@ by the thoracodorsal nerve." :hint "note" :tags (gnosis-dev-random-items gnosis-dev-tags 2) :extra "extra"))) - (when (y-or-n-p "Add mulit cloze type?") + (when (y-or-n-p "Add note with multiple clozes?") (dotimes (_ num) (gnosis-add-note--cloze :deck testing-deck :note "this is a {c1:note} with multiple {c1:clozes}" :hint "note" :tags (gnosis-dev-random-items gnosis-dev-tags 2) - :extra "extra"))))) + :extra "extra"))) + (when (y-or-n-p "Add note type y-or-n?") + (dotimes (_ num) + (gnosis-add-note--y-or-n :deck testing-deck + :question "Is Codeine recommended in breastfeeding mothers?" + :hint "hint" + :answer 110 + :extra "extra" + :tags (gnosis-dev-random-items gnosis-dev-tags 2)))))) (defun gnosis-dev-test () "Begin/End testing env. @@ -75,27 +83,23 @@ If ask nil, leave development env" (let ((ask (y-or-n-p "Start development env?")) (testing-dir (concat gnosis-dir "/testing"))) (if ask - (progn (unless (file-exists-p testing-dir) - (make-directory testing-dir)) - (setf gnosis-db (emacsql-sqlite (concat testing-dir "/testing.db"))) - (setf gnosis-testing t) - (gnosis-db-init) - (gnosis-dev-add-fields) - (message "Adding testing values...") - (message "Development env is ready for testing.")) + (progn + (unless (file-exists-p testing-dir) + (make-directory testing-dir)) + (setf gnosis-db (emacsql-sqlite (concat testing-dir "/testing.db"))) + (setf gnosis-testing t) + (dolist (table '(notes decks review review-log extras)) + (condition-case nil + (gnosis--drop-table table) + (error (message "No %s table to drop." table)))) + (gnosis-db-init) + (gnosis-dev-add-fields) + (message "Adding testing values...") + (message "Development env is ready for testing.")) (setf gnosis-db (emacsql-sqlite (concat (file-name-as-directory gnosis-dir) "gnosis.db"))) (setf gnosis-testing nil) (message "Exited development env.")))) -(defun gnosis-dev-retest () - "Redo database." - (interactive) - (dolist (table '(notes decks review review-log extras)) - (condition-case nil - (gnosis--drop-table table) - (error (message "No %s table to drop." table)))) - (gnosis-db-init) - (gnosis-dev-test)) (provide 'gnosis-dev) ;;; gnosis-dev.el ends here @@ -75,13 +75,14 @@ :prefix 'gnosis-face) (defface gnosis-face-extra - '((t :inherit markdown-italic-face)) - "Face for extra-notes from note." + '((t :inherit italic + :foreground "#9C91E4")) + "Face for extra-notes." :group 'gnosis-faces) (defface gnosis-face-main '((t :inherit default)) - "Face for main section from note." + "Face for the main section from note." :group 'gnosis-face-faces) (defface gnosis-face-seperator @@ -119,6 +120,11 @@ "Face for user choice." :group 'gnosis-face) +(defface gnosis-face-next-review + '((t :inherit bold)) + "Face for next review." + :group 'gnosis-face) + (cl-defun gnosis-select (value table &optional (restrictions '1=1)) @@ -231,6 +237,21 @@ When SUCCESS nil, display USER-INPUT as well" " " (propertize user-input 'face 'gnosis-face-false)))))) +(cl-defun gnosis-display-y-or-n-answer (&key answer success) + "Display y-or-n answer for note ID. + +ANSWER is the correct answer, either y or n. Answer is either 121 or +110, which are the char values for y & n respectively +SUCCESS is t when user-input is correct, else nil" + (let ((answer (if (equal answer 121) "y" "n"))) + (with-gnosis-buffer + (insert + (concat "\n\n" + (propertize "Answer:" 'face 'gnosis-face-directions) + " " + (propertize answer 'face (if success 'gnosis-face-correct 'gnosis-face-false))))))) + + (defun gnosis-display--hint (hint) "Display HINT." (with-gnosis-buffer @@ -294,6 +315,16 @@ If FALSE t, use gnosis-face-false face" (insert "\n\n") (insert-image image))))) +(defun gnosis-display--next-review (id) + "Display next interval for note ID." + (let ((interval (gnosis-get 'next-rev 'review-log `(= id ,id)))) + (with-gnosis-buffer + (goto-char (point-max)) + (insert (concat "\n\n" + (propertize "Next review:" 'face 'gnosis-face-directions) + " " + (propertize (format "%s" interval) 'face 'gnosis-face-next-review)))))) + (cl-defun gnosis--prompt (prompt &optional (downcase nil) (split nil)) "PROMPT user for input until `q' is given. @@ -519,6 +550,35 @@ Refer to `gnosis-add-note--double' for more." :extra (read-string "Extra: ") :tags (gnosis-tag-prompt))))) +(cl-defun gnosis-add-note--y-or-n (&key deck question hint answer extra (image nil) tags (suspend 0) (second-image nil)) + "Add y-or-n type note. + +DECK: Deck name for note. +QUESTION: Quesiton to display for note. +ANSWER: Answer for QUESTION, either `121' (char value for yes) or `110' + (char value for no). +HINT: Hint to display during review, before user-input. +EXTRA: Extra information to display after user-input/giving an answer. +IMAGE: Image to display before user-input. +TAGS: Tags used to organize notes +SUSSPEND: Binary value of 0 & 1, when 1 note will be ignored. +SECOND-IMAGE: Image to display after user-input." + (gnosis-add-note-fields deck "y-or-n" question hint answer extra tags suspend image second-image)) + +(defun gnosis-add-note-y-or-n () + "Add note(s) of type `y-or-n' interactively to selected deck. + +refer to `gnosis-add-note--y-or-n' for more information about keyword values." + (let ((deck (gnosis--get-deck-name))) + (while (y-or-n-p (format "Add note of type `y-or-n' to `%s' deck? " deck)) + (gnosis-add-note--y-or-n :deck deck + :question (read-string "Question: ") + :answer (read-char-choice "Answer: [y] or [n]? " '(?y ?n)) + :hint (read-string "Hint: ") + :extra (read-string "Extra: ") + :tags (gnosis-tag-prompt))))) + + (cl-defun gnosis-add-note--cloze (&key deck note hint tags (suspend 0) extra (image nil) (second-image nil)) "Add cloze type note. @@ -593,7 +653,7 @@ See `gnosis-add-note--cloze' for more reference." ;;;###autoload (defun gnosis-add-note (type) "Create note(s) as TYPE interactively." - (interactive (list (completing-read "Type: " '(MCQ Cloze Basic Double) nil t))) + (interactive (list (completing-read "Type: " '(MCQ Cloze Basic Double y-or-n) nil t))) (when gnosis-testing (unless (y-or-n-p "You are using a testing environment! Continue?") (error "Aborted"))) @@ -602,6 +662,7 @@ See `gnosis-add-note--cloze' for more reference." ("Cloze" (gnosis-add-note-cloze)) ("Basic" (gnosis-add-note-basic)) ("Double" (gnosis-add-note-double)) + ("y-or-n" (gnosis-add-note-y-or-n)) (_ (message "No such type.")))) (defun gnosis-mcq-answer (id) @@ -828,7 +889,7 @@ Returns a list of the form (ef-increase ef-decrease ef)." "Update review-log for note with value of id ID. SUCCESS is a binary value, 1 is for successful review." - (let ((ef (gnosis-review-new-ef id 1))) + (let ((ef (gnosis-review-new-ef id success))) ;; Update review-log (gnosis-update 'review-log `(= last-rev ',(gnosis-algorithm-date)) `(= id ,id)) (gnosis-update 'review-log `(= next-rev ',(car (gnosis-review--algorithm id success))) `(= id ,id)) @@ -856,7 +917,8 @@ SUCCESS is a binary value, 1 is for successful review." (gnosis-review--update id 0) (message "False")) (gnosis-display--correct-answer-mcq answer user-choice) - (gnosis-display--extra id))) + (gnosis-display--extra id) + (gnosis-display--next-review id))) (defun gnosis-review-basic (id) "Review basic type note for ID." @@ -868,7 +930,21 @@ SUCCESS is a binary value, 1 is for successful review." (success (gnosis-compare-strings answer user-input))) (gnosis-display--basic-answer answer success user-input) (gnosis-display--extra id) - (gnosis-review--update id (if success 1 0)))) + (gnosis-review--update id (if success 1 0)) + (gnosis-display--next-review id))) + +(defun gnosis-review-y-or-p (id) + "Review y-or-n type note for ID." + (gnosis-display--image id) + (gnosis-display--question id) + (gnosis-display--hint (gnosis-get 'options 'notes `(= id ,id))) + (let* ((answer (gnosis-get 'answer 'notes `(= id ,id))) + (user-input (read-char-choice "[y]es or [n]o: " '(?y ?n))) + (success (equal answer user-input))) + (gnosis-display-y-or-n-answer :answer answer :success success :user-input user-input) + (gnosis-display--extra id) + (gnosis-review--update id (if success 1 0)) + (gnosis-display--next-review id))) (defun gnosis-review-cloze--input (cloze) "Prompt for user input during cloze review. @@ -899,6 +975,7 @@ Used to reveal all clozes left with `gnosis-face-cloze-unanswered' face." (if (equal (car input) t) ;; Reveal only one cloze (progn (gnosis-display-cloze-reveal :replace cloze) + (gnosis-review--update id 1) (setf num (1+ num))) ;; Reveal cloze for wrong input, with `gnosis-face-false' (gnosis-display-cloze-reveal :replace cloze :success nil) @@ -908,9 +985,9 @@ Used to reveal all clozes left with `gnosis-face-cloze-unanswered' face." (when (< num clozes-num) (gnosis-review-cloze-reveal-unaswered clozes)) (gnosis-display-cloze-user-answer (cdr input)) (gnosis-review--update id 0) - (cl-return))) - finally (gnosis-review--update id 1))) - (gnosis-display--extra id)) + (cl-return))))) + (gnosis-display--extra id) + (gnosis-display--next-review id)) (defun gnosis-review-note (id) "Start review for note with value of id ID, if note is unsuspended." @@ -922,6 +999,7 @@ Used to reveal all clozes left with `gnosis-face-cloze-unanswered' face." ("mcq" (gnosis-review-mcq id)) ("basic" (gnosis-review-basic id)) ("cloze" (gnosis-review-cloze id)) + ("y-or-n" (gnosis-review-y-or-p id)) (_ (error "Malformed note type"))))))) (defun gnosis-review-commit (note-num) @@ -1038,7 +1116,7 @@ changes." ((and (listp value) (not (equal value nil))) (insert (format ":%s '%s\n" field (format "%s" (cl-loop for item in value - collect (format "\"%s\"" item)))))) + collect (format "\"%s\"" item)))))) ((equal value nil) (insert (format ":%s %s\n" field 'nil))) (t (insert (format ":%s \"%s\"\n" field value))))) @@ -1047,16 +1125,23 @@ changes." (insert "\n;; After finishing editing, save changes with `<C-c> <C-c>'\n;; Do NOT exit without saving.") (indent-region (point-min) (point-max))))) +(defun gnosis-edit-save-exit () + "Save edits and exit." + (interactive) + (eval-buffer) + (kill-buffer) + (exit-recursive-edit)) + +(defvar-keymap gnosis-edit-mode-map + :doc "gnosis-edit keymap" + "C-c C-c" #'gnosis-edit-save-exit) + (define-derived-mode gnosis-edit-mode emacs-lisp-mode "Gnosis EDIT" "Gnosis Edit Mode." :interactive t :lighter " gnosis-edit-mode" :keymap gnosis-edit-mode-map) -(defvar-keymap gnosis-edit-mode-map - :doc "gnosis-edit keymap" - "C-c C-c" #'(lambda () (interactive) (eval-buffer) (kill-buffer) (exit-recursive-edit))) - (cl-defun gnosis-edit-update-note (&key id main options answer tags (extra-notes nil) (image nil) (second-image nil)) "Update note with id value of ID. |