Synopsis: Below I report the implementation of a spaced repetition system entirely in DT/DTTG. At first, the description of the setup might look complicated, but once the initial steps are done, this works without further ado:
- Add any card/file by replicating it into the review folder, that’s it!
- Daily, just look at all cards in the review folder and rate them wrong/right, that’s it!
Spaced repetition practice can be very helpful to learn and maintain knowledge. There are well-known standalone programs for computers and handheld devices, for example Anki and SuperMemo. However, the bulk of my information is inside Devonthink databases, and I did not want to create yet another, unrelated system. In addition, I am not interested in zipping through vocabulary flashcards. As a physicist about a decade from retirement, I want to review a mix of self-created cards with facts that I try to remember as well as documents such as scientific literature that I periodically want to remind myself of. In other words, any document could be a “card”. I also want to study and review mostly on my iPad and iPhone, while maintaining the system on the Mac.
I’ve now implemented such a system in DT/DTTG:
Here is what’s needed to get this rolling:
- Create a group
SRP
and subgroupsForReview
andNotDueYet
.- I put
SRP
at the DB root, and if you do the same, the smart rule Apple Scripts will not need modification). UPDATE: Wrong, unless your DB is calledres2
, you need to edit the paths in both AppleScripts.
- I put
- Create a DT custom meta-data field (multi-line text) called
srp
. - Set up the two smart rules
srp1
andsrp2
(details below) and make them act hourly.
That’s it!
Loading content
“Cards” (really any documents to review) are then loaded by replicating them from anywhere in the same DB into ForPractice
, making sure that the item’s rating is set to zero. From then on, this card will remain in the review loop.
Every day, open ForReview
and go through all the cards. If you answer incorrectly, set the rating to 1. If your answer was right, set the rating to > 1.
Within the hour, the smartrule srp1
will reschedule the cards using the DT reminder due date. Cards that were reviewed today are parked in NotDueYet
, and smart rule srp2
will bring due cards back into ForReview
according to the spaced repetition algorithm (inside the srp1
AppleScript).
Reviewing on iOS is facilitated by setting the view options for the ForReview
group to “sort by rating (ascending)” and for “Summary” choose to show Property Icons so that you can see the rating. Then, during your review, the unrated (hence unreviewed) cards will stay at the top. Keep reviewing the top-most card, unless there is no unrated one left.
When reviewing on iOS, of course, sync has to be on and DT on the Mac running, executing the smart rules prior to the next morning, when the next round of reviews becomes available.
Here are the smart rules and the AppleScripts contained in them:
srp1
:
(* SRP srp1.scpt
This smart rule runs hourly looks for items in group "ForReview" which
have a rating > 0 and delivers them to this script.
The script extracts the review rating (1 for incorrect answer or >1 for
correct answer) for an item. It then reads the review history of the item
from a custom metadata field "srp" (multi-line text) and extracts the
latest review level. From this information, the script determines the date
of the next review and sets the DT due date accordingly. Finally the
item is moved to the group "NotYetDue" until it is due (note: this is done
in the script instead of in the smart rule, because if the card has replicants
outside of the SRP group, the smart rule "move to" command moves all
replicant instances of an item to the same place; I found this way of doing
it on the DT Forum in a post by user Korm, I believe).
*)
on performSmartRule(theRecords)
tell application id "DNtp"
-- the due date/time is set to be at 5am on a given day to avoid time zone issues
-- with doing things around midnight
set currDate to current date
set currTime to time of currDate
set currDate5am to currDate - currTime + (5 * hours)
-- this is the spaced repetition algorithm, so to speak
-- I used a very simple, increasing, sequence here, this can be modified
-- and expanded to arbitary complexity a la SuperMemo, if wanted/needed
-- interval_list determines the spacings in days after successive, succesful
-- reviews
set interval_list to {1, 2, 5, 11, 23, 52, 113, 249} -- 2.2th power
-- comment out the next line if used in a smart rule
-- set theRecords to (selection as list)
repeat with theRecord in theRecords
-- read the rating of the record
-- rating = 1: answer was incorrect, rating > 1: answer was correct
set myRating to the rating of theRecord
-- read the existing SRP level for this item
-- the custom metadata field (multi-line text) "srp" pre-pends after each
-- review the level at which the card currently is (i.e. how many
-- successive correct reviews)
-- use "try" to avoid error when new item has no history yet
try
set histSRP to get custom meta data for "srp" from theRecord
-- extract the last level (the number prior to the first comma)
set oldDelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to ","
set prevSRP to text item 1 of histSRP
set AppleScript's text item delimiters to oldDelims
on error
set prevSRP to 0
set histSRP to ""
end try
-- now we have the SRP history
-- if current review yielded an incorrect answer (rating 1_
-- the card is reset to level 1
-- if the review was successful, the card's level is upped by 1
-- but the mechanism saturates at level 8
if myRating is 1 then
set newSRP to 1
else
set newSRP to prevSRP + 1
if newSRP > 8 then set newSRP to 8
end if
-- updated the review history in the custom metadata field "srp"
set updatedSRP to (newSRP as string) & "," & histSRP
-- write this string to the custom metadata field
add custom meta data updatedSRP for "srp" to theRecord
-- set the new due date
set jump to item (newSRP) of interval_list
-- randomize the jump a bit so that not all cards advance exactly the same
set randJump to (jump * (1 + 0.2 * (random number from -50 to 50) / 100)) as integer
set newDueDate to currDate5am + (jump * days)
tell theRecord to make new reminder with properties {schedule:once, alarm:notification, due date:newDueDate}
-- move the modified items into the "NotYetDue" group
-- this version makes sure that only this replicant is moved over
-- all other replicants outside SRP are untouched
set theFrom to create location "/SRP/ForReview" in database "res2"
set theTo to create location "/SRP/NotDueYet" in database "res2"
set theResult to move record theRecord from theFrom to theTo
end repeat
end tell
end performSmartRule
srp2
:
(* Smart rule srp2 is run hourly and monitors items in
the group "NotYetDue" for their due date. If an item
has become due, it will be moved to the group
"ForReview" and its rating will be reset to zero
Smart rule settings:
- search in NotYetDue
- date due within last 10000 days
- perform hourly and on demand
- change rating to zero stars
- execute script - apple script - this script
The items are moved to the group "ForReview" in the
script instead of in the smart rule, because if the card
has replicants outside of the SRP group, the smart
rule "move to" command moves all replicant instances
of an item to the same place; I found this way of doing
it on the DT Forum in a post by user Korm, I believe.
*)
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set theFrom to create location "/SRP/NotDueYet" in database "res2"
set theTo to create location "/SRP/ForReview" in database "res2"
set theResult to move record theRecord from theFrom to theTo
end repeat
end tell
end performSmartRule
Some tips for creating cards
Create a Keynote document with two pages, first one for question, second for answer. Choose a portrait format for the slides so that the first slide is filling the screen on the iPad, not to reveal the answer on the second page. Keep such a blank file in the SRP group, duplicate it, edit it, and move it into ForReview
to create a card. Using Keynote for this, the Apple Pencil can be easily used to create content or annotate right on the iPad. Furthermore, it is trivial to add rich components such as images and LaTex-based formulae (caveat: hyperlinks will not function in the DTTG-internal view of the Keynote file).
The spacing algorithm
- In my script, the intervals between reviews are set to 1, 2, 5, 11, 23, 52, 113, 249 days. So not really an “algorithm” at all, just a list.
- First view of an item after it is placed in the system is not a review yet. So even if you answer correctly, the card will be offered for a first review on the next day.
- If the card is then again successfully reviewed, it comes back after 2 days, then 5, etc.
- The system tops out at 249 days, the spacing will not go further out.
- I have not tested this system nearly that far out, so I have no idea yet whether this spacing is sensible.
- Any time the card is not answered correctly, it is reset to the 1 day interval.
This method can be arbitrarily modified by editing the srp1
AppleScript. Since we use the ratings to provide feedback, a more differentiated feedback could be provided, e.g. 1= wrong, 2 = struggling, but kind of correct, …, 5 = super easy. Based on the level of correctness, the spacing could be fine tuned.
I’m not an expert, and have not used my system for a long time yet, but after reading the literature, I became a bit skeptical of all these ingenious, fine-tuned (and sometimes super-secret), spacing algorithms. I would not be surprised if applying the KISS principle is the way to go.
Things I don’t like
Unfortunately, in DTTG V3, the ratings and other flags have been relegated one menu level down, since the Info menu is now inside of the “three-dot-in-a-circle” top level menu. So it is a bit cumbersome to set the rating for each card. That might be a deal breaker for quick-studies. It would be ideal to have something at top level.
Further reading
Gwern’s review of spaced repetition practice is an excellent starting point to learn more.