Improve the submission function
This commit is contained in:
parent
38e51040f7
commit
795d1e461c
1 changed files with 62 additions and 24 deletions
|
@ -59,6 +59,7 @@
|
||||||
;;
|
;;
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
|
(require 'cl-lib)
|
||||||
(require 'json)
|
(require 'json)
|
||||||
(require 'org-element)
|
(require 'org-element)
|
||||||
(require 'ox)
|
(require 'ox)
|
||||||
|
@ -200,34 +201,67 @@ The result is the path to the newly stored media file."
|
||||||
|
|
||||||
;;; Commands
|
;;; Commands
|
||||||
|
|
||||||
(defun anki-editor-submit ()
|
(defun anki-editor-submit (&optional arg match scope)
|
||||||
"Send notes in current buffer to Anki.
|
"Build notes from headings that can be matched by MATCH within SCOPE and send them to Anki.
|
||||||
|
|
||||||
For each note heading, if there's no note id in property drawer,
|
The default search condition `&ANKI_NOTE_TYPE<>\"\"' will be
|
||||||
create a note, otherwise, update fields and tags of the existing
|
appended to MATCH.
|
||||||
note.
|
|
||||||
|
For notes that already exist in Anki (i.e. has `ANKI_NOTE_ID'
|
||||||
|
property), only their fields and tags will be updated, change of
|
||||||
|
deck or note type are currently not supported.
|
||||||
|
|
||||||
|
If SCOPE is not specified, the following rules are applied to
|
||||||
|
determine the scope:
|
||||||
|
|
||||||
|
- if there's an active region, it will be set to `region'
|
||||||
|
- if called with prefix `C-u', it will be set to `tree'
|
||||||
|
- if called with prefix double `C-u', it will be set to `file'
|
||||||
|
- if called with prefix triple `C-u', will be set to `agenda'
|
||||||
|
|
||||||
|
See doc string of `org-map-entries' for what these different options mean.
|
||||||
|
|
||||||
If one fails, the failure reason will be set in property drawer
|
If one fails, the failure reason will be set in property drawer
|
||||||
of that heading."
|
of that heading."
|
||||||
(interactive)
|
(interactive "P")
|
||||||
(let ((total 0)
|
|
||||||
(failed 0))
|
|
||||||
(org-map-entries
|
|
||||||
(lambda ()
|
|
||||||
(setq total (1+ total))
|
|
||||||
(message "Processing note at %d..." (point))
|
|
||||||
(anki-editor--clear-failure-reason)
|
|
||||||
(condition-case err
|
|
||||||
(anki-editor--process-note-heading)
|
|
||||||
(error (progn
|
|
||||||
(setq failed (1+ failed))
|
|
||||||
(anki-editor--set-failure-reason (error-message-string err))))))
|
|
||||||
(concat anki-editor-prop-note-type "<>\"\""))
|
|
||||||
|
|
||||||
(message (with-output-to-string
|
(unless scope
|
||||||
(princ (format "Submitted %d notes, with %d failed." total failed))
|
(setq scope (cond
|
||||||
(when (> failed 0)
|
((region-active-p) 'region)
|
||||||
(princ " Check property drawers for failure reasons."))))))
|
((equal arg '(4)) 'tree)
|
||||||
|
((equal arg '(16)) 'file)
|
||||||
|
((equal arg '(64)) 'agenda)
|
||||||
|
(t nil))))
|
||||||
|
(setq match (concat match "&" anki-editor-prop-note-type "<>\"\""))
|
||||||
|
|
||||||
|
(let ((total 0)
|
||||||
|
(acc 0)
|
||||||
|
(failed 0))
|
||||||
|
(message "Counting notes...")
|
||||||
|
(org-map-entries (lambda () (incf total)) match scope)
|
||||||
|
(org-map-entries (lambda ()
|
||||||
|
(message "[%d/%d] Processing notes in buffer \"%s\", wait a moment..."
|
||||||
|
(incf acc) total (buffer-name))
|
||||||
|
(anki-editor--clear-failure-reason)
|
||||||
|
(condition-case err
|
||||||
|
(anki-editor--process-note-heading)
|
||||||
|
(error (incf failed)
|
||||||
|
(anki-editor--set-failure-reason (error-message-string err)))))
|
||||||
|
match
|
||||||
|
scope)
|
||||||
|
|
||||||
|
(message (if (= 0 failed)
|
||||||
|
(format "Successfully submitted %d notes to Anki." acc)
|
||||||
|
(format "Submitted %d notes, %d of which are failed. Check property drawers for failure reasons. Once you've fixed the issues, you could use `anki-editor-retry-failure-notes' to re-submit the failed notes."
|
||||||
|
acc
|
||||||
|
failed)))))
|
||||||
|
|
||||||
|
(defun anki-editor-retry-failure-notes (&optional arg scope)
|
||||||
|
"Re-submit notes that were failed.
|
||||||
|
This command just calls `anki-editor-submit' with match string
|
||||||
|
matching non-empty `ANKI_FAILURE_REASON' properties."
|
||||||
|
(interactive "P")
|
||||||
|
(anki-editor-submit arg (concat anki-editor-prop-failure-reason "<>\"\"") scope))
|
||||||
|
|
||||||
(defun anki-editor-insert-deck (&optional arg)
|
(defun anki-editor-insert-deck (&optional arg)
|
||||||
"Insert a deck heading interactively.
|
"Insert a deck heading interactively.
|
||||||
|
@ -304,7 +338,10 @@ bugfixes or new features of AnkiConnect."
|
||||||
(setq note-elem (org-element-at-point)
|
(setq note-elem (org-element-at-point)
|
||||||
note-elem (let ((content (buffer-substring
|
note-elem (let ((content (buffer-substring
|
||||||
(org-element-property :begin note-elem)
|
(org-element-property :begin note-elem)
|
||||||
(org-element-property :end note-elem))))
|
;; in case the buffer is narrowed,
|
||||||
|
;; e.g. by `org-map-entries' when
|
||||||
|
;; scope is `tree'
|
||||||
|
(min (point-max) (org-element-property :end note-elem)))))
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(org-mode)
|
(org-mode)
|
||||||
(insert content)
|
(insert content)
|
||||||
|
@ -435,6 +472,7 @@ Do nothing when JUST-ALIGN is non-nil."
|
||||||
(setq deck (org-entry-get-with-inheritance anki-editor-prop-deck)
|
(setq deck (org-entry-get-with-inheritance anki-editor-prop-deck)
|
||||||
note-id (org-entry-get nil anki-editor-prop-note-id)
|
note-id (org-entry-get nil anki-editor-prop-note-id)
|
||||||
note-type (org-entry-get nil anki-editor-prop-note-type)
|
note-type (org-entry-get nil anki-editor-prop-note-type)
|
||||||
|
;; TODO: use `org-scanner-tags' instead, which is said to be faster
|
||||||
tags (org-get-tags-at)
|
tags (org-get-tags-at)
|
||||||
fields (mapcar #'anki-editor--heading-to-note-field (anki-editor--get-subheadings heading)))
|
fields (mapcar #'anki-editor--heading-to-note-field (anki-editor--get-subheadings heading)))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue