summaryrefslogtreecommitdiff
path: root/README.org
diff options
context:
space:
mode:
Diffstat (limited to 'README.org')
-rw-r--r--README.org464
1 files changed, 464 insertions, 0 deletions
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..817ea24
--- /dev/null
+++ b/README.org
@@ -0,0 +1,464 @@
+* Org Flashcards
+Spaced-repetition system for use with Emacs org-mode.
+
+#+CAPTION: Review Demo
+[[file:images/review.png]]
+
+** Introduction
+In the most abstract sense, this package deals with
+
+1. Attaching timestamped review information to headlines
+2. Querying all headings where reviews are due
+3. Reviewing due *positions* of headings
+
+As mentioned in step 3, a heading can have multiple *positions*,
+e.g. to implement cloze-deletions where multiple items are reviewed
+independently from each other.
+
+In the reviewing step, display functions can be registered by card
+type. This allows easy addition of user-defined card types without
+having to think about storing and updating review data.
+
+Review functions are called with point on the headline of the card
+that should be reviewed and get passed a single argument,
+the position to be reviewed.
+
+They are expected to return either ~'quit~ to end the review or one of
+~'again~, ~'hard~, ~'good~, ~'ease~, to rate the card.
+
+While the primary application is learning information using spaced
+repetition, at the end, the API should be flexible enough to implement
+other kinds of repeating tasks where it is necessary to store data in
+addition to the next date.
+
+One example would be storing one exercise per heading, using the
+positions to store one or more sets and logging the number of
+repetitions done on each "review".
+
+** Stability
+This package should be considered *unstable*. I use it on a daily
+basis but the API might change in breaking ways.
+
+If that happens, I'll add a changelog entry with updating
+instructions. In case the card format changes, I'll include code to
+update existing collections of cards.
+** Prior Art
+There are a few other packages for implementing a SRS based on org-mode.
+
+The biggest difference between this package and the ones I've found so
+far:
+
+1. Use of =awk= for quickly finding cards due for review
+2. Support for multiple *positions* in a card
+
+Below, I've listed a few packages that are actively maintained and
+implement a lot of useful functionality.
+
+- [[https://gitlab.com/phillord/org-drill/][phillord/org-drill]]
+- [[https://github.com/abo-abo/pamparam][abo-abo/pamparam]]
+
+Thanks to the maintainers and all contributors for their work on these
+packages!
+
+*** TODO Mention supermemo, anki, memosyne
+** Design Criteria
+*** Performance
+All user-facing commands (especially during review) should be as fast
+as possible (<300ms).
+
+Using the =awk= indexer, searching 2500 org files (~200k lines in
+total) for due flashcards takes around ~500ms on my laptop (Thinkpad
+L470, SSD).
+
+Using the lisp indexer based on ~org-map-entries~,
+searching a single 6500 line file with 333 flashcards takes ~1000ms,
+indexing the same file with =awk= takes around ~50ms.
+*** All Relevant Data Kept Org Files
+For easy version control
+*** Multiple Cards per Org-Mode Heading
+*** Easy Implementation of Custom Card Types
+** Getting Started
+Before using this package, a few variables have to be set:
+
+- ~org-fc-directories~ :: list of directories to search for flashcards
+- ~org-fc-source-path~ :: should be set to the absolute path of the
+ cloned repository
+
+*** TODO Example setup using =use-package=
+*** TODO Basic Hydra
+*** TODO Demo File
+A file demonstrating all card types is included.
+~M-x org-fc-demo~ starts a review of this file.
+
+Note that the review data of the cards in this file *is not updated*.
+** Marking Headlines as Cards
+A *card* is an org-mode headline with a =:fc:= tag attached to it.
+Each card can have multiple *positions* reviewed independently from
+each other, e.g. one for each hole of a cloze card.
+
+Review data (ease, interval in days, box, due date) is stored in a table
+in a drawer inside the card.
+
+#+begin_src org
+:REVIEW_DATA:
+| position | ease | box | interval | due |
+|----------+------+-----+----------+------------------------|
+| 2 | 2.65 | 6 | 107.13 | 2020-04-07T01:01:00 |
+| 1 | 2.65 | 6 | 128.19 | 2020-04-29T06:44:00 |
+| 0 | 2.95 | 6 | 131.57 | 2020-04-30T18:03:00 |
+:END:
+#+end_src
+
+Review results are appended to a csv file to avoid cluttering the org
+files.
+
+Each card needs at least two properties, an *unique* ~:ID:~ and a
+~:FC_TYPE:~. In addition to that, the date a card was created
+(i.e. the headline was marked as a flashcard) is stored to allow
+making statistics for how many cards were created in the last day /
+week / month.
+
+#+begin_src org
+:PROPERTIES:
+:ID: 4ffe66a7-7b5c-4811-bd3e-02b5c0862f55
+:FC_TYPE: normal
+:FC_CREATED: 2019-10-11T14:08:32
+:END:
+#+end_src
+
+Card types (should) implement a ~org-fc-type-...-init~ command that
+initializes these properties and sets up the review data drawer
+
+All timestamps created and used by org-flashcards use ISO8601 format
+with second precision and without a timezone (timezone UTC0).
+
+This prevents flashcard due dates from showing up in the org-agenda
+and allows filtering for due cards by string-comparing a timestamp
+with one of the current time.
+** Review
+A review session can be started using ~org-fc-review-all~
+to review all cards that are due, or using ~org-fc-review-buffer~ to
+review only cards in the current buffer.
+
+*** Display of Cards during Review
+TODO: Add image
+
+Headlines are presented for review by hiding the all top level
+headings before and after the one the heading to be reviewed is
+located in.
+
+This is done through the function ~org-fc-org-narrow-tree~.
+~org-fc-show-all~ can be used to remove all overlays (i.e. reset the
+display of the buffer).
+
+All parent headings are shown but their body text (~section~) is
+hidden.
+
+If the file has a ~#+TITLE:~ keyword this is shown, too.
+
+To hide the title during review (e.g. for a "Definition" flashcard),
+add a ~:notitle:~ tag to the heading.
+
+To hide the heading text of the current card during review, add a
+~:noheading:~ tag.
+*** Implementation of Card Review
+Review is implemented by storing due cards in a global variable. The
+buffer the card is displayed in never leaves =org-mode=, [[https://github.com/abo-abo/hydra][abo-abo/hydra]]
+is used to show review statistics (number of cards remaining, percent
+again/hard/good/easy) and prompt for user actions.
+
+1. jump to the file + id of the current card
+2. set it up for review (i.e. hiding parts of the buffer)
+3. open a hydra prompting to flip the card
+4. flip the card or quit the review session
+5. open a hydra prompting for a rating
+6. rate the card or quit the review session
+7. set the current card to the next card due
+8. continue at 1.
+
+If an error occurs during review, ~org-fc-review-quit~ can be used to
+reset the current buffer and the review state.
+** (Un)suspending Cards
+Cards can be suspended (excluded from review) by adding a =suspended=
+tag, either by hand or using the ~org-fc-suspend-card~ command.
+
+All cards in the current buffer can be suspended using the
+~org-fc-suspend-buffer~ command.
+
+The reason for using a per-headline tag instead of a file keyword is
+that this way cards stay suspended when moved to another buffer.
+
+Cards can be un-suspended using the ~org-fc-unsuspend-card~ and
+~org-fc-unsuspend-buffer~ commands.
+
+If the card being unsuspended was not due for review yet,
+or was due less than 10% of its interval ago, its review data is not
+reset. If it was due by more than that, its review data is reset to
+the initial values.
+** Statistics
+~org-fc-dashboard~ shows a buffer with statistics for review performance
+and cards / card types.
+*** TODO Replace with R scripts run on the review history / card index
+*** Review History
+The review history is stored in a tsv file, to avoid cluttering org
+files. This makes it easy to calculate review statistics.
+
+At first, I used an org drawer to store the review history but that
+added to much overhead to the files (in one instance 6.5k lines of
+review history for a file of 9.5k lines in total).
+
+Columns:
+1. Date in ISO8601 format, second precision
+2. Filename
+3. Card ID
+4. Position
+5. Ease (before review)
+6. Box (before review)
+7. Interval (before review)
+8. Rating
+
+More advanced review algorithms might need to use the review history
+of a card. In this case, the card ID + position should be used to look
+up the review history, as the filename can change when moving cards
+from file to file.
+** Card Types
+*** Normal Cards
+During review, the heading is shown with its "Back" subheading
+collapsed, when flipping the card, the back heading is shown,
+then the user is asked to rate the review performance.
+
+Positions: =front=
+*** Text-Input Cards
+On review, the user is asked to type in a string which is then
+compared to the one stored in the ~:ANSWER:~ property of the card.
+
+Positions: =front=
+*** Double Cards
+Similar to normal cards, but reviewed both in the "Front -> Back"
+direction and in the "Back -> Front" direction.
+
+Positions: =front=, =back=
+*** Cloze Cards
+The cards text contains one or more *holes*. During review, one hole
+is hidden while the text of (some) remaining ones is shown.
+
+Flipping the card reveals the text of the hidden hole.
+
+Card titles can contain holes, too.
+
+Positions: =0=, =1=, ...
+
+Cloze cards can have a number of sub-types.
+
+**** TODO Document type-specific properties
+**** TODO Implement & document type-changing functions
+**** Deletion ~'deletion~
+Only one hole is hidden.
+**** Enumerations ~'enumeration~
+All holes *behind* the currently review one are hidden, too.
+
+Useful for memorizing lists where the order of items is important.
+**** Context ~'context~
+Holes ~org-fc-type-cloze-context~ (default 1) around the currently
+reviewed one are shown.
+
+Useful for memorizing longer lists where the order of items is important.
+**** Hole Syntax
+Deletions can have the following forms
+
+- ~{{text}}~
+- ~{{text}@id}~
+- ~{{text}{hint}}~
+- ~{{text}{hint}@id}~
+
+~text~ should not contain any "}",
+unless it is part of a ~$latex$~ block.
+In this case, ~latex~ should not contain any "$".
+
+Holes *inside* latex blocks are not handled correctly at the moment.
+As a workaround, create multiple smaller latex blocks and wrap each in
+a hole.
+*** TODO Listening Cards
+When reviewing the card, an audio file is played.
+Flipping the card, a transcription / translation is revealed.
+
+Useful for learning to understand sentences spoken in a foreign
+language.
+*** Compact Cards
+For cards without a "Back" heading, the headline text is considered as
+the front, the main text as the back.
+
+This is useful for cards with a short front text, e.g. when learning
+definitions of words.
+*** Defining Own Card Types
+To define a custom card type,
+you need to implement three functions:
+
+- ~(...-init)~ to initialize a heading as a flashcard of this type,
+ setting up the cards properties & review data.
+ Should be marked as ~(interactive)~.
+- ~(...-setup position)~ to setup ~position~ of the card for review
+- ~(...-flip)~ to flip the card
+- ~(...-update)~ to update the review data of the card, e.g. if a new
+ hole is added to a cloze card
+
+All of these are called with ~(point)~ on the cards heading.
+
+Take a look at the =org-fc-type-<name>.el= files to see how these
+functions could be implemented.
+** TODO Custom Review Spacing Algorithms :longterm:
+The interfaces defined by this package should be flexible enough to
+allow implementing custom review spacing algorithms.
+
+This is not possible at the moment because the awk scripts and the
+functions for reading / updating the review data drawer make strong
+assumptions about the format of the review data.
+
+A good implementation of this should allow using different spacing
+algorithms based on a ~:FC_SPACING:~ property in the card.
+** TODO Sharing Decks :longterm:
+It should be possible to share sets of cards by removing the review
+data and syncing them with git.
+
+At least one of the existing emacs flashcard packages implements this
+functionality.
+** Incremental Reading
+- [[https://github.com/alphapapa/org-web-tools]]
+*** TODO Supermemo link
+** Internals
+If your not interested in implementing your own card types or
+contributing to this package, you can skip this section.
+
+*** Components
+**** =org-flashcards.el=
+Main file.
+**** =org-fc-main.el=
+Main flashcards view displaying card / position / review statistics.
+**** =org-fc-review.el=
+Functions related to reviewing cards, updating the review data drawer
+and logging review results.
+**** =org-fc-sm2.el=
+Implementation of the [[https://www.supermemo.com/en/archives1990-2015/english/ol/sm2][SM2]] review spacing algorithm,
+modified to behave like the algorithm used by [[https://apps.ankiweb.net/docs/manual.html#what-algorithm][Anki]].
+
+It uses four ratings (again, hard, good, easy) instead of the six used
+in the supermemo variant.
+
+The first few reviews are done in fixed intervals
+(0.01 days / approx 15 minutes, 1 day, 6 days).
+
+After these intervals, reviews are scheduled by multiplying the cards
+current interval with its ease (initially 2.5, bound to be >= 1.3 and
+<= 5.0), then multiplying a random factor ~1 to avoid "chunking" of
+flashcards due for review.
+
+All of these parameters can be configured using the variables defined
+in =org-fc-sm2.el=.
+**** =org-fc-tsv.el=
+Functions for parsing the tsv output of awk scripts
+**** =org-fc-awk.el=
+Functions for interacting with the awk indexer / filter / stats scripts.
+**** =org-fc-refactor.el=
+Functions for refactoring collections of cards
+in case the card format changes.
+**** =org-fc-org.el=
+Functions for interacting with org-mode files, mostly for hiding /
+showing parts of them during review.
+**** =org-fc-assert.el=
+Helper functions for writing unit-tests for functions.
+**** =org-fc-type-<name>.el=
+Implementations of flashcard types, for more details, see the "Card
+Types" section of this document.
+**** TODO =org-fc-audio.el=
+Functions for attaching audio files to flashcards and playing them.
+**** TODO =org-fc-benchmark.el=
+Benchmarks to detect performance regressions in the code.
+**** TODO =org-fc-indexer-lisp.el=
+Slow flashcard indexer written in EmacsLisp for use on systems where
+=awk= is not available.
+
+Not working at the moment.
+**** TODO Document core api of each file
+*** Coding Style
+Components are split into multiple smaller files,
+with each function prefixed by the files base-name.
+
+Public functions are named ~basename-functionname~,
+internal helper functions are named ~basename--functionname~.
+*** Testing
+Unit-testing is done using ~org-fc-assert-...~ macros
+defined in =org-fc-assert.el=.
+
+These assertions are placed right after the function definition
+and run when the file is loaded. If an assertion fails,
+an ~'org-fc-assertion-error~ is raised.
+
+**** TODO Integration Testing
+Integration testing is done by providing an input org file, a set of
+operations to be performed on it and an org file with the expected
+output.
+
+Tests are run by copying the input file to a temporary file, executing
+the operations on it, then comparing it to the expected output.
+
+Files for this live in the =fixtures/= folder.
+*** dash.el
+The code in this package uses [[https://github.com/magnars/dash.el#threading-macros][threading macros]] and list functions
+(often in their anaphoric form) from [[https://github.com/magnars/dash.el][magnars/dash.el]].
+
+Make sure to read that documentation before going reading / working on
+the source code.
+*** =awk=
+~find~ is used to generate a list of =.org= files in
+~org-fc-directories~, these are then passed to =awk= scripts
+to generate lists of cards and card-positions.
+
+Only files starting with ~[a-Z0-9_]~ and a ~.org~ extension are
+indexed to exclude temp / hidden files.
+This can be customized with the ~org-fc-find-name~ variable.
+
+[[https://www.gnu.org/software/gawk/][gawk]] is a programming language for processing / parsing text.
+
+Assuming the input org files are well formatted, they can be
+efficiently parsed using regexeps and a small number of state
+variables.
+
+=awk= scripts in this package come in three types:
+
+1. Indexing, for generating lists of cards / positions
+3. Filtering, e.g. for selecting only unsuspended cards due now
+2. Aggregation, for generating statistics from these lists
+
+- =awk/indexer_cards.awk= :: list all card headings
+- =awk/indexer_positions.awk= :: list all card positions
+- =awk/filter_due.awk= :: select only unsuspended cards due right now
+- =awk/stats_cards.awk= :: stats over cards
+- =awk/stats_positions.awk= :: stats over positions
+- =awk/stats_reviews.awk= :: stats over the reviews tsv file
+
+These scripts use the =gawk= version of =awk= which should be
+available on any modern Linux / UNIX distribution.
+
+Configurable tags and properties can be passed to the indexer scripts as
+variables. If a tag or property is not passed to the script,
+a default value is used.
+
+*** Format
+Output is generated in *tab separated* form and *does not* include a
+header with column names. For the indexing scripts, the first two
+columns are the filename and the ID of the heading.
+
+The ~org-fc-tsv-parse~ function can be used to parse a tsv
+string into a plist, given a list of headers with optional type
+specifications.
+
+=0= (false) and =1= (true) are used for boolean values (e.g. for the
+"suspended" column).
+
+Dates are converted to ISO-8601 format, no timezone, minute-precision
+(e.g. =2019-10-09T16:49=).
+
+Unlike the format used by org mode, timestamps in ISO-8601 format can
+be compared lexicographically.
+
+Processing script output *tab separated* key-value pairs with no header.