aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThanos Apollo <[email protected]>2024-02-20 13:55:09 +0200
committerThanos Apollo <[email protected]>2024-02-20 13:55:09 +0200
commite2a934c4992c886aeb96ec2507ac3485ea5fde9b (patch)
tree8f809955d3d6ba8fb85d352e147188e5e024f28f
parentf2d56ebcec73817f97561cbb7c2048c0d4952559 (diff)
parenta1a5ac2558a0edb9def2862bee4f6526c7fa173c (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.org33
-rw-r--r--TODO.org44
-rw-r--r--gnosis.el272
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.
+
diff --git a/gnosis.el b/gnosis.el
index fb13b5c..a04bdec 100644
--- a/gnosis.el
+++ b/gnosis.el
@@ -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)