diff options
author | Thanos Apollo <[email protected]> | 2024-02-20 13:55:09 +0200 |
---|---|---|
committer | Thanos Apollo <[email protected]> | 2024-02-20 13:55:09 +0200 |
commit | e2a934c4992c886aeb96ec2507ac3485ea5fde9b (patch) | |
tree | 8f809955d3d6ba8fb85d352e147188e5e024f28f | |
parent | f2d56ebcec73817f97561cbb7c2048c0d4952559 (diff) | |
parent | a1a5ac2558a0edb9def2862bee4f6526c7fa173c (diff) |
Release version 0.1.8: Merge branch '0.1.8-dev'0.1.8
- Add dashboard
- Major bug fixes
- Rewrite exporting & editing notes
-rw-r--r-- | CONTRIBUTING.org | 33 | ||||
-rw-r--r-- | TODO.org | 44 | ||||
-rw-r--r-- | gnosis.el | 272 |
3 files changed, 169 insertions, 180 deletions
diff --git a/CONTRIBUTING.org b/CONTRIBUTING.org index eda1a7e..6b617a1 100644 --- a/CONTRIBUTING.org +++ b/CONTRIBUTING.org @@ -22,36 +22,3 @@ $ git format-patch HEAD^1 #+end_src /You can find my email on the git log/ - - -* Tasks -** TODO Add Dashboard -+ Create a dashboard to view all notes created, user can edit & - suspend notes. Use tabulated-list-mode, preferably. - -** TODO Algorithm: changes for ef increase/decrease factors -+ After 3 successful in a row reviews increase ef increase factor by 5% & vice versa - -** DONE Refactor =completing-read= UI choices -CLOSED: [2024-02-17 Sat 21:59] -/DONE on version 0.1.7/ - -=completing-read= is not an ideal solution as a UI. If user has not -enabled a completion system, such as vertico, this would make gnosis -unusable. - -One possible solution is to create defcustom =gnosis-completing-read-function= -that has ido-completing-read by default if vertico/ivy/helm is not -enabled - -*** Fix - -Implemented =gnosis-completing-read-function= - -** DONE Use vc instead git shell commands -CLOSED: [2024-02-17 Sat 21:59] - -/DONE on version 0.1.7/ - -Implemented =gnosis-git-*= functions to handle git commands. - diff --git a/TODO.org b/TODO.org new file mode 100644 index 0000000..1cc3771 --- /dev/null +++ b/TODO.org @@ -0,0 +1,44 @@ +#+title: TODO's for Gnosis +#+author: Thanos Apollo + +* Gnosis TODO's + +** DONE Add Dashboard +CLOSED: [2024-02-20 Tue 13:33] ++ Create a dashboard to view all notes created, user can edit & + suspend notes. Use tabulated-list-mode, preferably. +*** Notes after completion ++ Dashboard added using tabulated-list-mode. Performance is not ideal + when 20K > notes, values should be cached + +** TODO Dashboard: Improve Performance +** TODO Dashboard: Add filtering/search + +** TODO Algorithm: changes for ef increase/decrease factors ++ After 3 successful in a row reviews increase ef increase factor by 5% & vice versa + + +** DONE Refactor =completing-read= UI choices +CLOSED: [2024-02-17 Sat 21:59] +/DONE on version 0.1.7/ + +=completing-read= is not an ideal solution as a UI. If user has not +enabled a completion system, such as vertico, this would make gnosis +unusable. + +One possible solution is to create defcustom =gnosis-completing-read-function= +that has ido-completing-read by default if vertico/ivy/helm is not +enabled + +*** Notes + +Implemented =gnosis-completing-read-function= + + +** DONE Use vc instead git shell commands +CLOSED: [2024-02-17 Sat 21:59] + +/DONE on version 0.1.7/ + +Implemented =gnosis-git-*= functions to handle git commands. + @@ -5,7 +5,7 @@ ;; Author: Thanos Apollo <[email protected]> ;; Keywords: extensions ;; URL: https://thanosapollo.org/projects/gnosis -;; Version: 0.1.7 +;; Version: 0.1.8 ;; Package-Requires: ((emacs "27.2") (compat "29.1.4.2") (emacsql "20240124")) @@ -224,7 +224,7 @@ Example: (defun gnosis-display-mcq-options (id) "Display answer options for mcq note ID." - (let ((options (apply #'append (gnosis-select 'options 'notes `(= id 1) t))) + (let ((options (apply #'append (gnosis-select 'options 'notes `(= id ,id) t))) (option-num 1)) (insert "\n\n" (propertize "Options:" 'face 'gnosis-face-directions)) (cl-loop for option in options @@ -1091,31 +1091,6 @@ NOTES: List of note ids" ;; Editing notes (defun gnosis-edit-note (id) - "Edit note with value of id ID." - (pcase (funcall gnosis-completing-read-function "Edit: " '("contents" "ef") nil t) - ("contents" (gnosis-edit-note-contents id)) - ("ef" (gnosis-edit-ef id)) - (_ (message "No such value.")))) - -(defun gnosis-edit-ef (id) - "Edit easiness factor values for note with id value ID." - (let ((ef-full (caar (gnosis-select 'ef 'review `(= id ,id)))) - (old-value-index (pcase (funcall gnosis-completing-read-function "Change Factor: " - '("Increase" "Decrease" "Total")) - ("Total" 2) - ("Decrease" 1) - ("Increase" 0))) - (new-value (float (string-to-number (read-string "New value: "))))) - ;; error checking. - (cond ((>= 0 new-value) (error "New value needs to be a number & higher than `0'")) - ;; Check if when total-ef is selected, new value is higher than 1.3 - ((and (>= 1.3 new-value) (= old-value-index 2) (error "New total ef needs to be higher than `1.3'")))) - ;; Use `gnosis-replace-item-at-index' to generate new list with - ;; new ef value. Change ef value at gnosis-db using - ;; `gnosis-update' - (gnosis-update 'review `(= ef ',(gnosis-replace-item-at-index old-value-index new-value ef-full)) `(= id ,id)))) - -(defun gnosis-edit-note-contents (id) "Edit the contents of a note with the given ID. This function creates an Emacs Lisp buffer named *gnosis-edit* and populates it @@ -1138,49 +1113,25 @@ The note fields that will be shown in the buffer are: The buffer automatically indents the expressions for readability. After finishing editing, evaluate the entire expression to apply the changes." - (let ((id (gnosis-get 'id 'notes `(= id ,id))) - (main (gnosis-get 'main 'notes `(= id ,id))) - (options (gnosis-get 'options 'notes `(= id ,id))) - (answer (gnosis-get 'answer 'notes `(= id ,id))) - (tags (gnosis-get 'tags 'notes `(= id ,id))) - (extra-notes (gnosis-get 'extra-notes 'extras `(= id ,id))) - (image (gnosis-get 'images 'extras `(= id ,id))) - (second-image (gnosis-get 'extra-image 'extras `(= id ,id)))) - (with-current-buffer (switch-to-buffer (get-buffer-create "*gnosis-edit*")) - (gnosis-edit-mode) - (erase-buffer) - (insert ";;\n;; You are editing a gnosis note. DO NOT change the value of id.\n\n") - (insert "(gnosis-edit-update-note ") - (cl-loop for (field value) in `((id ,id) - (main ,main) - (options ,options) - (answer ,answer) - (tags ,tags) - (extra-notes ,extra-notes) - (image ,image) - (second-image ,second-image)) - do (cond ((eq field 'id) - (insert (format ":id %s \n" (propertize (number-to-string value) 'read-only t)))) - ((numberp value) - (insert (format ":%s %s\n" field value))) - ((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)))))) - ((null value) - (insert (format ":%s %s\n" field 'nil))) - (t (insert (format ":%s \"%s\"\n" field value))))) - (delete-char -1) ;; delete extra line - (insert ")") - (insert "\n;; After finishing editing, save changes with `<C-c> <C-c>'\n;; Do NOT exit without saving.") - (indent-region (point-min) (point-max))))) + (with-current-buffer (switch-to-buffer (get-buffer-create "*gnosis-edit*")) + (gnosis-edit-mode) + (erase-buffer) + (insert ";;\n;; You are editing a gnosis note. DO NOT change the value of id.\n\n") + (insert "(gnosis-edit-update-note ") + (gnosis-export-note id) + (insert ")") + (insert "\n\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)) + ;; exit recursive edit if we are in one + (if (>= (recursion-depth) 1) + (exit-recursive-edit) + (gnosis-dashboard))) (defvar-keymap gnosis-edit-mode-map :doc "gnosis-edit keymap" @@ -1192,8 +1143,8 @@ changes." :lighter " Gnosis Edit" :keymap gnosis-edit-mode-map) - -(cl-defun gnosis-edit-update-note (&key id main options answer tags (extra-notes nil) (image nil) (second-image nil)) +(cl-defun gnosis-edit-update-note (&key id main options answer tags (extra-notes nil) (image nil) (second-image nil) + ef ff suspend) "Update note with id value of ID. ID: Note id @@ -1213,19 +1164,26 @@ SECOND-IMAGE: Image to display after user-input" (tags . ,tags) (extra-notes . ,extra-notes) (image . ,image) - (second-image . ,second-image)) + (second-image . ,second-image) + (ef . ',ef) + (ff . ,ff) + (suspend . ,suspend)) when value do (cond ((memq field '(extra-notes image second-image)) (gnosis-update 'extras `(= ,field ,value) `(= id ,id))) + ((memq field '(ef ff)) + (gnosis-update 'review `(= ,field ,value) `(= id ,id))) + ((eq field 'suspend) + (gnosis-update 'review-log `(= ,field ,value) `(= id ,id))) ((listp value) (gnosis-update 'notes `(= ,field ',value) `(= id ,id))) (t (gnosis-update 'notes `(= ,field ,value) `(= id ,id)))))) (cl-defun gnosis-get-notes-for-deck (&optional (deck (gnosis--get-deck-id))) "Return a list of ID vlaues for each note with value of deck-id DECK." - (gnosis-select 'id 'notes `(= deck-id ,deck) '1=1 t)) + (gnosis-select 'id 'notes `(= deck-id ,deck) t)) -(defun gnosis-export-note (id) +(cl-defun gnosis-export-note (id &optional (export-for-deck nil)) "Export fields for note with value of id ID. ID: Identifier of the note to export. @@ -1250,37 +1208,28 @@ quotes. The final exported note is indented using the `indent-region' function to improve readability." - (let ((type (gnosis-get 'type 'notes `(= id ,id))) - (main (gnosis-get 'main 'notes `(= id ,id))) - (options (gnosis-get 'options 'notes `(= id ,id))) - (answer (gnosis-get 'answer 'notes `(= id ,id))) - (tags (gnosis-get 'tags 'notes `(= id ,id))) - (extra-notes (gnosis-get 'extra-notes 'extras `(= id ,id))) - (image (gnosis-get 'images 'extras `(= id ,id))) - (second-image (gnosis-get 'extra-image 'extras `(= id ,id)))) - (cl-loop for (field . value) in `((type . ,type) - (main . ,main) - (options . ,options) - (answer . ,answer) - (tags . ,tags) - (extra-notes . ,extra-notes) - (image . ,image) - (second-image . ,second-image)) - do (cond ((member field '(extra-notes image second-image)) - (insert (format ":%s \"%s\"\n" field value))) - ((numberp value) - (insert (format ":%s %s\n" field value))) - ((listp value) - (insert (format ":%s %s\n" field (format "%s" (cl-loop for item in value - collect (format "\"%s\"" item)))))) - ((equal value nil) - (insert (format ":%s %s\n" field 'nil))) - (t (insert (format ":%s \"%s\"\n" field value)) - (indent-region (point-min) (point-max))))))) - -(defun gnosis-export-deck (filename) + (let ((values (append (gnosis-select '[id main options answer tags] 'notes `(= id ,id) t) + (gnosis-select '[extra-notes images extra-image] 'extras `(= id ,id) t) + (gnosis-select '[ef ff] 'review `(= id ,id) t) + (gnosis-select 'suspend 'review-log `(= id ,id) t))) + (fields '(:id :main :options :answer :tags :extra-notes :image :second-image :ef :ff :suspend))) + (when export-for-deck + (setf values (append (gnosis-select 'type 'notes `(= id ,id) t) + (butlast (cdr values) 3))) + (setf fields (append '(:type) (butlast (cdr fields) 3)))) + (cl-loop for value in values + for field in fields + do (insert + (cond ((listp value) + (format "\n%s '%s" (symbol-name field) (prin1-to-string value))) + (t (format "\n%s %s" (symbol-name field) (prin1-to-string value)))))))) + +;; TODO: Fix export of deck! +(defun gnosis-export-deck (deck export-deck-name filename) "Export notes for deck in FILENAME. +WARNING: This function is not yet implemented. + FILENAME: The name of the file to save the exported deck. This function prompts the user to provide a deck name and allows the @@ -1296,61 +1245,38 @@ resulting code is saved to a file with the provided FILENAME and a Each note is exported using the `gnosis-export-note` function. The generated code includes a call to `gnosis-define-deck` with the deck name and all notes formatted as nested lists" - (interactive (list (read-string "Filename: "))) - (let ((notes (gnosis-get-notes-for-deck)) - (deck-name (read-string "Export deck as (name): "))) - (with-temp-file (concat filename ".el") - (insert "(gnosis-define-deck " "'" deck-name " '(") - (cl-loop for note in notes - do (insert "(") (gnosis-export-note note) (insert ")" "\n") - finally (insert "))"))))) + ;; (interactive (list (gnosis-get-notes-for-deck) + ;; (read-string "Export deck as (name): ") + ;; (read-string "Filename: "))) + (with-temp-file (concat filename ".el") + (insert "(gnosis-define-deck " "'" export-deck-name " '(") + (cl-loop for note in deck + do (insert "(") (gnosis-export-note note t) (insert ")" "\n") + finally (insert "))")))) ;; TODO: Add defcustom to have suspended as 0 or 1 depending on ;; gnosis-add-decks-suspended t or nil (cl-defun gnosis-define-deck (deck notes &optional (suspended 0)) - "Define DECK consisting of NOTES, optionally add them as SUSPENDED. - -The `gnosis-define-deck` function adds a new deck with the specified -name to `gnosis-db'. It also adds each note from the given list -of `notes` to the deck. The function takes three optional arguments: -`deck`, `notes`, and `suspended`. - -- `deck`: The name of the deck to be added. It should be provided as a - symbol. - -- `notes`: A list containing the notes to be added to the deck. Each - note should be represented as a property list with the - following keys: `:type`, `:main`, `:options`, `:answer` - -- extras include :`:extra-notes`, `:tags`, `:image`, and `:second-image`. - -- `suspended`: An optional argument specifying whether the deck should - be created in a suspended state. A non-zero value - suspends the deck, while a value of 0 (default) creates - the deck in an active state. - -When calling `gnosis-define-deck`, the deck is added to the Gnosis -system by calling `gnosis-add-deck`. Each note is added to the deck -using `gnosis-add-note-fields`. The function iterates over the list of -`notes` and extracts the necessary fields from each note's property -list before adding them to the deck. - -The purpose of this function is to create a full deck with its -associated notes in `gnosis-db', ready for further processing or -review." + "Define DECK consisting of NOTES, optionally add them as SUSPENDED." (gnosis-add-deck (symbol-name deck)) - (sit-for 0.1) ;; + (sit-for 0.1) (cl-loop for note in notes - do (gnosis-add-note-fields (symbol-name deck) - (plist-get note :type) - (plist-get note :main) - (plist-get note :options) - (plist-get note :answer) - (plist-get note :extra-notes) - (plist-get note :tags) - suspended - (plist-get note :image) - (plist-get note :second-image)))) + do (let ((type (plist-get note :type)) + (main (plist-get note :main)) + (options (plist-get note :options)) + (answer (plist-get note :answer)) + (extra-notes (plist-get note :extra-notes)) + (tags (plist-get note :tags)) + (suspend (plist-get note :suspend)) + (image (plist-get note :image)) + (second-image (plist-get note :second-image))) + (gnosis-add-note-fields deck type main options answer extra-notes tags suspend image second-image)) + collect note)) + +;; Rewrite this similarly to gnosis +(cl-defun gnosis-define-deck--note (&keys deck type main options answer extra-notes tags image second-image) + "Define a note for DECK." + (gnosis-add-note-fields deck type main options answer extra-notes tags 0 image second-image)) ;;;###autoload @@ -1408,6 +1334,58 @@ review." (:foreign-key [id] :references notes [id] :on-delete :cascade))) +;; Dashboard +(defun gnosis-dashboard-output-note (id) + "Output note contents formatted for gnosis dashboard." + (cl-loop for item in (append (gnosis-select '[main options answer tags] 'notes `(= id ,id) t) + (gnosis-select 'suspend 'review-log `(= id ,id) t)) + if (listp item) + collect (mapconcat #'identity item ", ") + else + collect (prin1-to-string item))) + +(defun gnosis-dashboard-output-notes () + "Return note contents for gnosis dashboard." + (let ((max-id (apply 'max (gnosis-select 'id 'notes '1=1 t)))) + (cl-loop for id from 1 to max-id collect + (list (number-to-string id) (vconcat (gnosis-dashboard-output-note id)))))) + +(defun gnosis-dashboard-edit-note () + "Get note id from tabulated list and edit it." + (interactive) + (let ((id (tabulated-list-get-id))) + (gnosis-edit-note (string-to-number id)) + (message "Editing note with id: %s" id))) + +(defvar-keymap gnosis-dashboard-mode-map + :doc "gnosis-dashboard keymap" + "e" #'gnosis-dashboard-edit-note + "q" #'quit-window) + +(define-derived-mode gnosis-dashboard-mode tabulated-list-mode "Gnosis Dashboard" + "Major mode for displaying Gnosis dashboard." + :keymap gnosis-dashboard-mode-map + (interactive) + (display-line-numbers-mode 0) + (setq tabulated-list-format [("Main" 30 t) + ("Options" 20 t) + ("Answer" 25 t) + ("Tags" 25 t) + ("Suspend" 5 t)]) + (setq tabulated-list-padding 2 + tabulated-list-sort-key nil) + (tabulated-list-init-header)) + +;;;###autoload +(defun gnosis-dashboard () + "Display gnosis dashboard." + (interactive) + (pop-to-buffer "*gnosis-dashboard*" nil) + (gnosis-dashboard-mode) + (setq tabulated-list-entries + (gnosis-dashboard-output-notes)) + (tabulated-list-print t)) + (defun gnosis-db-init () "Create gnosis essential directories & database." (unless (length= (emacsql gnosis-db [:select name :from sqlite-master :where (= type table)]) 6) |