Skip to content

Instantly share code, notes, and snippets.

@eshapard
Last active February 11, 2023 20:18
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eshapard/960f2508c2096ff663af3b6ebc925f16 to your computer and use it in GitHub Desktop.
Save eshapard/960f2508c2096ff663af3b6ebc925f16 to your computer and use it in GitHub Desktop.
Anki 2.0 addon to dynamically create learning steps. Starting at 15 minutes and then one day, each additional step is `easeFactor * lastStep` until we pass 20 days. Sets graduating and *Easy* interval to the next logical interval in the series. See this post for rationale: https://eshapard.github.io/anki/anki-learning-steps-with-feedback.html
# Auto Learning Steps
# Anki 2.0 addon
# Author EJS
# https://eshapard.github.io/
#
# Sets the learning steps sequence of each deck options group.
from anki.hooks import addHook
from aqt import mw
#from aqt.utils import showInfo
#import time
ignoreList = ['Default', 'OnHold', 'Parent Category'] #Deck options groups to ignore
# run this on profile load
def updateLearningSteps():
#find all deck option groups
dconf = mw.col.decks.dconf
#cycle through them one by one
for k in dconf:
if dconf[k]["name"] not in ignoreList:
#showInfo(dconf[k]["name"])
ease = dconf[k]["new"]["initialFactor"]/1000.0
#create learning steps
tempList = [15]
for i in range(10):
l = int(1440*ease**i)
if l < 28800:
tempList.append(l)
else:
gradInts = [int(l/1440),int(l/1440)]
break
#showInfo(str(tempList))
#showInfo(str(gradInts))
mw.col.decks.dconf[k]["new"]["delays"] = tempList
mw.col.decks.dconf[k]["new"]["ints"] = gradInts
mw.col.decks.save(mw.col.decks.dconf[k])
mw.reset()
# add hook to 'profileLoaded'
addHook("profileLoaded", updateLearningSteps)
@woolfog
Copy link

woolfog commented Feb 25, 2020

@eshapard Can confirm that I was using it on anki 2.1 and that was what was causing the int division to result in a float.
@Willz7 Yes, it works, I just did what eshapard said, made a folder in the addons directory (you can name it whatever you want), made a init.py file and copy pasted the contents from here into it.

@Willz7
Copy link

Willz7 commented Feb 27, 2020

Update: Managed to change the autoLearningSteps addon from 2.0 to 2.1 with the help of @eshapard and @woolfog

2020_02_27_00_44_57_Window
@eshapard

This error is related to the autoLapseNewInterval addon and managed to fix it by deleting the line 31 code.

@galantra
Copy link

Can we use this addon with anki 2.1?

I think so. You have to create a subfolder in the addons directory and save the .py file as init.py within the subfolder.

https://ankitects.github.io/addon-docs/#/porting2.0

Shouldn't it be init.py? With underscores.

@Willz7
Copy link

Willz7 commented Apr 27, 2020

Add-ons, last update check: 2020-04-26 21:16:36

Caught exception:
Traceback (most recent call last):
File "aqt\main.py", line 253, in onOpenProfile
File "aqt\main.py", line 398, in loadProfile
File "aqt\gui_hooks.py", line 1559, in call
File "lib\site-packages\anki\hooks.py", line 583, in runHook
File "C:\Users\William\AppData\Roaming\Anki2\addons21\AutoLearningSteps_init_.py", line 17, in updateLearningSteps
dconf = mw.col.decks.dconf
AttributeError: 'DeckManager' object has no attribute 'dconf'

I am getting this error above when using anki 2.1.24 beta 8. Anyone else getting the same error?

@BeJa1996
Copy link

BeJa1996 commented May 3, 2020

Hey esyhapard,
First thank you for alle your contributions. I love them!

Now my question:
I use one Parent Deck and Some Childdecks. The Parent Deck is in a different option group than the child decks (because I wanted from the parent deck to take 60 new cards each day and the child decks to contribute each 20). I saw that your Script changed the options in my parent deck but the options in my child decks remained the same. Some hours before I also noticed that changes in the parent deck seem not to apply when I review. Only changes to the child decks. Therefore I am worried that it doesn't work.
Do you see a solution?

@eshapard
Copy link
Author

eshapard commented May 3, 2020 via email

@galantra
Copy link

galantra commented May 3, 2020

BeJa, check your subdeck options group – is it "Default"? It is written in the add-on code that the Default group is exempt.

@BeJa1996
Copy link

BeJa1996 commented May 4, 2020

Hello Eshapard, Hello Galantra,

It was the Exception list Galantra wrote about.
Thank you both!

@Willz7
Copy link

Willz7 commented May 5, 2020

Has anyone managed to get AutoLearningSteps working for Anki 2.1.25 ?

@cloak17
Copy link

cloak17 commented May 5, 2020

Hmm... I don't use Anki anymore

Hey Eshapard, I may have missed it, but what do you use now (if anything)? Did you transfer your cards? Why did you drop it? Thanks!

@setne01
Copy link

setne01 commented May 16, 2020

Add-ons, last update check: 2020-04-26 21:16:36

Caught exception:
Traceback (most recent call last):
File "aqt\main.py", line 253, in onOpenProfile
File "aqt\main.py", line 398, in loadProfile
File "aqt\gui_hooks.py", line 1559, in call
File "lib\site-packages\anki\hooks.py", line 583, in runHook
File "C:\Users\William\AppData\Roaming\Anki2\addons21\AutoLearningSteps__init__.py", line 17, in updateLearningSteps
dconf = mw.col.decks.dconf
AttributeError: 'DeckManager' object has no attribute 'dconf'

I am getting this error above when using anki 2.1.24 beta 8. Anyone else getting the same error?

I am having the same error but haven't been able to solve it. Has anyone else had any luck?

@MetalFir
Copy link

Did anyone get this to work?

I'm getting the error message:

Caught exception:
Traceback (most recent call last):
File "aqt\main.py", line 253, in onOpenProfile
File "aqt\main.py", line 398, in loadProfile
File "aqt\gui_hooks.py", line 1559, in call
File "lib\site-packages\anki\hooks.py", line 583, in runHook
File "C:\Users\William\AppData\Roaming\Anki2\addons21\AutoLearningSteps__init__.py", line 17, in updateLearningSteps
dconf = mw.col.decks.dconf
AttributeError: 'DeckManager' object has no attribute 'dconf'

on Anki 2.1.26

@galantra
Copy link

For me it's working again. Not sure why, though.

@MetalFir
Copy link

For me it's working again. Not sure why, though.

Oh that's great!

Did you edit the code in any way?

What Anki are you running? I deliberately downgraded to Anki 2.1.26 to get the a version of Anki with autoLapseNewInterval.

@galantra
Copy link

Ah forgot to say that. I'm on 2.1.22.

I made no significant changes to the original code (only changed if l < 28800:).

@MetalFir
Copy link

MetalFir commented Nov 19, 2020

😢 I tried again and it didn't work unfortunately. I will probably downgrade to 2.1.22 to see if it works if enough of my other add ons work.

I keep getting this.

Debug info:
Anki 2.1.26 (70784154) Python 3.8.0 Qt 5.13.1 PyQt 5.14.1
Platform: Mac 10.16
Flags: frz=True ao=True sv=2
Add-ons, last update check: 2020-11-18 20:31:16
Add-ons possibly involved: ⁨updateLearningSteps⁩

Caught exception:
Traceback (most recent call last):
File "aqt/main.py", line 253, in onOpenProfile
File "aqt/main.py", line 398, in loadProfile
File "aqt/gui_hooks.py", line 1559, in call
File "anki/hooks.py", line 583, in runHook
File "/Users/xxxxxxx/Library/Application Support/Anki2/addons21/updateLearningSteps/init.py", line 17, in updateLearningSteps
dconf = mw.col.decks.dconf
AttributeError: 'DeckManager' object has no attribute 'dconf

@galantra
Copy link

It would be interesting to see if it works on 2.1.22 but not on 2.1.26, or if your problem lies somewhere else.

I don't upgrade from 2.1.22 exactly because some add-ons don't work on 2.1.26. I don't know about this specific add-on.

@MetalFir
Copy link

@galantra

Thank you so much I've got it to work on 2.1.22.

Oddly though it changed some of my settings.

Old Settings: Steps (in mins) are 15 1440 8640. Graduating interval is 15 days. Easy interval is 60 days.

New: Step (in mins) are 15 . Graduating interval is 1 day. Easy interval is 1 day.

I assume I change the intervals back to 15, 60 but do I leave the Step at 15?

Ah forgot to say that. I'm on 2.1.22.

I made no significant changes to the original code (only changed if l < 28800:).

@MetalFir
Copy link

MetalFir commented Nov 20, 2020

Oh I've worked it out...

When changing l < 28800 to if i < 4

I didn't realise that the L changed to an i

If anyone had the same problem as me

@galantra

Thank you so much I've got it to work on 2.1.22.

Oddly though it changed some of my settings.

Old Settings: Steps (in mins) are 15 1440 8640. Graduating interval is 15 days. Easy interval is 60 days.

New: Step (in mins) are 15 . Graduating interval is 1 day. Easy interval is 1 day.

I assume I change the intervals back to 15, 60 but do I leave the Step at 15?

Ah forgot to say that. I'm on 2.1.22.
I made no significant changes to the original code (only changed if l < 28800:).

@woolfog
Copy link

woolfog commented Dec 12, 2020

Here's an attempt at an updated version for 2.1.26, hopefully it also works as intended (did not do much testing, seems to be working tho):
EDIT1: Fixed graduation interval.

# Auto Learning Steps
# Anki 2.0 addon
# Author EJS 
# Update by woolfog
# https://eshapard.github.io/
#
# Sets the learning steps sequence of each deck options group.
from anki.hooks import addHook
from aqt import mw
#from aqt.utils import showInfo
#import time

ignoreList = ['Default', 'OnHold', 'Parent Category'] #Deck options groups to ignore

# run this on profile load
def updateLearningSteps():
    #find all deck option groups
    all_config = mw.col.decks.all_config()
    #cycle through them one by one
    for k in all_config:
        if k["name"] not in ignoreList:
            #showInfo(dconf[k]["name"])
            ease = k["new"]["initialFactor"]/1000.0
            #create learning steps
            tempList = [15]
            for i in range(10):
                l = int(1440*ease**i)
                if l < 28800:
                    tempList.append(l)
                else:
                    gradInts = [(int(l/1440)),(int(l/1440))]
                    gradInts.append(0)
                    break
            #showInfo(str(tempList))
            #showInfo(str(gradInts))
            k["new"]["delays"] = tempList
            k["new"]["ints"] = gradInts
            mw.col.decks.save(k)
    mw.reset()

# add hook to 'profileLoaded'
addHook("profileLoaded", updateLearningSteps)

@MetalFir
Copy link

@woolfog Thanks for spending your time to edit the addon. After using it for 2.1.26 the learning steps are produced are fine but oddly my graduating interval and easy interval change to "1" and "4" respectively. Any idea what is causing this?

@woolfog
Copy link

woolfog commented Jan 1, 2021

@woolfog Thanks for spending your time to edit the addon. After using it for 2.1.26 the learning steps are produced are fine but oddly my graduating interval and easy interval change to "1" and "4" respectively. Any idea what is causing this?

Thanks for the bug report, I've updated the code in my post above with a fix. Seems like Anki stores another value in the intervals list now, I have no idea what it is being used for though, I'm setting it to 0 for now, hopefully it does not break anything.

@jledesma28
Copy link

such an interesting thread here. So it work on the latest version of Anki?

@onekum
Copy link

onekum commented Nov 25, 2021

@woolfog Not sure if anyone here is still active, but I get an error with your code on Anki startup.

Caught exception:
Traceback (most recent call last):
File "aqt\progress.py", line 54, in handler
File "aqt\main.py", line 149, in on_window_init
File "aqt\main.py", line 197, in setupProfileAfterWebviewsLoaded
File "aqt\main.py", line 244, in setupProfile
File "aqt\main.py", line 425, in loadProfile
File "aqt\hooks_gen.py", line 2985, in call
File "anki\hooks.py", line 34, in runHook
File "C:\Users\******\AppData\Roaming\Anki2\addons21\autosteps_init_.py", line 37, in updateLearningSteps
k["new"]["ints"] = gradInts
UnboundLocalError: local variable 'gradInts' referenced before assignment

@galantra
Copy link

galantra commented Nov 4, 2022

such an interesting thread here. So it work on the latest version of Anki?

Yes

@Ankilli
Copy link

Ankilli commented Feb 10, 2023

In the article it says "Maybe I’ll add an option to select the number of learning steps you want to use." at the very end of it.
Is there a way to tweak the script that way?
I tried playing around with the 28.800 but it didn't change the amount of steps :/
(sry for noob question 😅)

@eshapard
Copy link
Author

eshapard commented Feb 10, 2023

In the article it says "Maybe I’ll add an option to select the number of learning steps you want to use." at the very end of it.
Is there a way to tweak the script that way?
I tried playing around with the 28.800 but it didn't change the amount of steps :/
(sry for noob question 😅)

You'd have to change the 'for i in range(10):" to a different number in the range() function and possibly change the maximum length of a learning step (currently set to 28800).

The number in the range() function defines the maximum learning steps that are generated. Learning steps keep being generated until you either reach that number, or the learning step goes beyond the maximum length.

@Ankilli
Copy link

Ankilli commented Feb 10, 2023

Ah yes, nice. Thanks a lot for the quick reply :)

And is there a way to cut out some of the steps while keeping the others?
So in the following the marked (->) steps would be taken out, could I do it with this script?
15
1440 (1 day)
-> 1440 * Starting Ease
1440 * Starting Ease^2
-> 1440 * Starting Ease^3
Graduating Interval = 1440 * Starting Ease^3

eventually becoming this:
15
1440 (1 day)
1440 * Starting Ease^2
Graduating Interval = 1440 * Starting Ease^3

regards 😄

@eshapard
Copy link
Author

you could do something like:

l = int(1440*ease**(i+1))

instead of

l = int(1440*ease**i)

@Ankilli
Copy link

Ankilli commented Feb 11, 2023

Thank you! 👍
Also helped me a lot to understand the code better! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment