summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gnosis-algorithm.el28
-rw-r--r--gnosis-dev.el42
-rw-r--r--gnosis.el115
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
diff --git a/gnosis.el b/gnosis.el
index 93ce244..f58e851 100644
--- a/gnosis.el
+++ b/gnosis.el
@@ -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.