This is the official thread for the add-on Pass/Fail Review: Automatic difficulty rating based on answer time (beta ver.). Please use this add-on only with an answer confirmation add-on like Color Confirmation to ensure this add-on is working as intended.
This isn’t really a “support” thread. I am not a programmer and can’t help if you have problems, sorry! I also don’t use this add-on anymore, so I won’t notice if it breaks. This thread seems to suggest that with FSRS, an add-on of this kind is redundant.
But this add-on has gotten more downloads than I expected it to get, so hopefully if a future update breaks it, someone can let me know here, and I can turn the downloads off.
For convenience’s sake, here’s the code—do whatever you like with it.
__init__.py
import time
from aqt import mw, gui_hooks
from anki.hooks import addHook
from aqt.reviewer import Reviewer
from anki.consts import *
# CONFIGURATION
config = mw.addonManager.getConfig(__name__)
# AUTO DEFAULT RATING
def startTimer(self):
global timer_start
timer_start = time.perf_counter()
def stopTimer(self):
timer_end = time.perf_counter()
global duration
duration = timer_end - timer_start
if mw.col.decks.name(self.did) in config["disabled_decks"]:
Reviewer._defaultEase = normal_ease
elif check_queue_disabled(self):
Reviewer._defaultEase = normal_ease
else:
Reviewer._defaultEase = my_defaultEase
def check_queue_disabled(self):
if config["learning_stage_disabled"]:
if self.queue == QUEUE_TYPE_LRN or self.queue == QUEUE_TYPE_DAY_LEARN_RELEARN:
return True
if config["new_stage_disabled"]:
if self.queue == QUEUE_TYPE_NEW:
return True
return False
def normal_ease(self):
return 3
def my_defaultEase(self):
new_ease = 3
for i in config["custom_decks"]:
if isinstance(i, list) and len(i) == 3 and isinstance(i[1], (float, int)) and isinstance(i[2], (float, int)):
if i[0] == self.mw.col.decks.name(self.card.did):
if 0 <= i[1] < i[2]:
EASY_TIME_MAX = i[1]
HARD_TIME_MIN = i[2]
break
else:
if 0 <= config["easy_time_max"] < config["hard_time_min"]:
EASY_TIME_MAX = config["easy_time_max"]
HARD_TIME_MIN = config["hard_time_min"]
else:
EASY_TIME_MAX = 1
HARD_TIME_MIN = 7
if self.mw.col.sched.answerButtons(self.card) == 4:
if duration <= EASY_TIME_MAX:
new_ease = 4
elif EASY_TIME_MAX < duration < HARD_TIME_MIN:
new_ease = 3
elif HARD_TIME_MIN <= duration:
new_ease = 2
elif self.mw.col.sched.answerButtons(self.card) == 3:
if duration < HARD_TIME_MIN:
new_ease = 3
elif HARD_TIME_MIN <= duration:
new_ease = 2
return new_ease
gui_hooks.reviewer_did_show_question.append(startTimer)
gui_hooks.reviewer_did_show_answer.append(stopTimer)
config.json
{
"easy_time_max": 1,
"hard_time_min": 7,
"learning_stage_disabled": true,
"new_stage_disabled": false,
"disabled_decks": [],
"custom_decks": [[]]
}
config.md
# Tips
**Use this add-on with an answer confirmation add-on like [Color Confirmation](https://ankiweb.net/shared/info/1084228676) to ensure it is working as intended.** For example, if you edit a card, the flip time and rating will be reset, and you might not notice unless you have this type of add-on.
Use Anki’s built-in Preferences to change the “Again” key to `'` (`Anki > Preferences > Review > Answer Keys`), so that you can easily review using just `enter` and `'`.
You can use Anki’s built-in timer, an add-on like [Life Drain](https://ankiweb.net/shared/info/715575551), or a JavaScript timer like the one at the bottom of [this post](https://forums.ankiweb.net/t/automatic-ease-scoring-time-based-default-card-rating-change-default-card-rating-based-on-flip-time-through-custom-scheduling/39769?u=doubleworker), to see how much time has elapsed. The JavaScript timer, which is what I use, also works on mobile.
Although not strictly related to this add-on, I have found the following changes to the default settings to be extremely helpful for my workflow:
Under `Display Order` in the Deck Options:
> `New/review order: Mix with reviews`
> `Review sort order: Ascending intervals`
I find learning new cards to be very motivating, so having new cards mixed in, rather than at the end, encourages me to do a bit of review even if I don’t feel like it. `Mix with reviews` also ensures a healthy ratio between new and review cards, which means that I don’t have to worry about falling behind, as I would if I were to display all new cards first but fail to finish reviews for the day. I use [a bit of javascript](https://forums.ankiweb.net/t/view-new-learning-review-while-rating-card/39879/2?u=doubleworker) to display a `New` label on new cards, so that I don’t waste time trying to remember a card I haven’t yet learned, which was the primary reason I disliked the `mix with reviews` setting in the past. Also turn on `New cards ignore review limit` in the deck options, which works well with the above settings.
Remember that if you change a deck name or move a subdeck from one parent to another, you will have to update this configuration.
# Settings
## Basic Settings
`"easy_time_max": number of seconds`
- If you flip a card _before_ this number of seconds has elapsed, the default rating will be set to “easy.”
- You can prevent the default rating from being set to “easy” by setting this value to `0`.
- This value must be less than `hard_time_min`.
`"hard_time_min": number of seconds`
- If you flip a card _after_ this number of seconds has elapsed, the default rating will be set to “hard.”
- This value must be greater than `easy_time_max`.
## Advanced Settings
`"learning_stage_disabled": true or false`
- This setting disables this add-on for cards in the learning and relearning stages.
- The default and recommended setting is `true`.
`"new_stage_disabled": true or false`
- This setting disables this add-on for new cards.
- The default setting is `false`.
`"disabled_decks": ["Deck 1", "Deck 2::Subdeck", ...]`
- To disable individual decks, write the full deck names in quotation marks and separate them with commas.
- For example:
> `"disabled_decks": ["German", "Spanish", "French::Grammar"]`
- Note that **you must disable subdecks individually, using their full names**; you can’t just disable the parent deck, and you must include the parent name. So if you have a deck called “Spanish” containing multiple subdecks, and you want to disable any or all of them, you will need to write the full name of each subdeck:
> `"disabled_decks": ["Spanish::Grammar", "Spanish::Vocabulary", "Spanish::Phrases"]`
- Remember to come back and update this configuration if you change any deck names, or move a subdeck from one deck to another.
`"custom_decks": [["Deck 1", easy_time_max, hard_time_min], ["Deck 2", easy_time_max, hard_time_min], ...]`
- To give a particular deck its own settings, write the full deck name in quotation marks, followed by the `easy_time_min` and `hard_time_max`, and surround with brackets. If you have multiple custom settings, make sure to put a comma in between sets of brackets.
- For example:
> `"custom_decks": [["Spanish", 1, 5], ["French::Phrases", 2, 7]]`
- You must customize each subdeck individually, and you must include the parent name; **settings that apply to a parent deck will not apply to its subdecks**.
- Remember to come back and update this configuration if you change any deck names, or move a subdeck from one deck to another.
- Note that the syntax for this setting has at least two sets of brackets (`[[]]`), unlike the `disabled_decks` setting.