# Pass/Fail Review: Automatic difficulty rating based on answer time (beta ver.) \[Official Thread\]

**URL:** <https://forums.ankiweb.net/t/pass-fail-review-automatic-difficulty-rating-based-on-answer-time-beta-ver-official-thread/53872>\
**Category:** Add-ons\
**Created:** [January 2, 2025, 11:58pm UTC](https://forums.ankiweb.net/t/pass-fail-review-automatic-difficulty-rating-based-on-answer-time-beta-ver-official-thread/53872 "2025-01-02T23:58:04Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![doubleworker](https://sea2.discourse-cdn.com/flex002/user_avatar/forums.ankiweb.net/doubleworker/32/14967_2.png) [@doubleworker](https://forums.ankiweb.net/u/doubleworker)\
**Post date:** [January 2, 2025, 11:58pm UTC](https://forums.ankiweb.net/t/pass-fail-review-automatic-difficulty-rating-based-on-answer-time-beta-ver-official-thread/53872/1 "2025-01-02T23:58:04Z")

</div>

This is the official thread for the add-on [Pass/Fail Review: Automatic difficulty rating based on answer time (beta ver.)](https://ankiweb.net/shared/info/574884631). Please use this add-on only with an answer confirmation add-on like [Color Confirmation](https://ankiweb.net/shared/info/1084228676) 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](https://forums.ankiweb.net/t/pass-fail-grading-as-default/34147) 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**
>
> ```python
> 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**
>
> ```json
> {
> "easy_time_max": 1,
> "hard_time_min": 7,
> "learning_stage_disabled": true,
> "new_stage_disabled": false,
> "disabled_decks": [],
> "custom_decks": [[]]
> }
> 
> ```

> **config.md**
>
> ```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.
> 
> ```
