Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deck and note options. #12

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move note options to CardOptions and try to match database field names.
  • Loading branch information
holocronweaver committed Sep 1, 2017
commit bda709744026e401e0818489a5c4a0fb77e05acb
226 changes: 147 additions & 79 deletions genanki/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,35 +152,88 @@ def to_json(self, now_ts, deck_id):
}


class CardOptions:
def __init__(self, stage=0, status=0, due=0, interval=0,
ease_factor=0, reps_left_til_grad=0):
"""SRS learning stage.
0 = new, 1 = learning, 2 = review."""
self.type = stage
"""SRS queue status modifiers.
0 = normal, 1 = suspended, 2 = user buried, 3 = scheduler buried"""
self.status = status
"""Behavior depends on learning stage of note.
new: unused.
learning: due time as integer seconds since Unix epoch.
review: integer days relative to deck creation timestamp."""
self.due = due
"""Time between next review and the one following.
Positive values are in days, negative in seconds."""
self.ivl = interval
"""Integer 'ease' factor used by SRS algorithm.
Example: 2500 = 250%."""
self.factor = ease_factor
"""Repititions remaining until graduation from the learning stage.
Unused during other SRS stages."""
self.left = reps_left_til_grad

@property
def stage(self):
return self.type
@stage.setter
def stage(self, value):
self.type = value
@property
def interval(self):
return self.ivl
@interval.setter
def interval(self, value):
self.ivl = value
@property
def ease_factor(self):
return self.factor
@ease_factor.setter
def ease_factor(self, value):
self.factor = value
@property
def reps_left_til_grad(self):
return self.left
@reps_left_til_grad.setter
def reps_left_til_grad(self, value):
self.left = value


class Card:
def __init__(self, ord_):
def __init__(self, ord_, options=None):
self.ord = ord_
self.options = options or CardOptions()

self.interval = 0
def write_to_db(self, cursor, now_ts, deck_id, note_id):
if self.options.status:
queue = -self.options.status
else:
queue = self.options.type

def write_to_db(self, cursor, now_ts, deck_id, note_id,
stage, queue, due, interval, ease, reps_til_grad):
cursor.execute('INSERT INTO cards VALUES(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);', (
note_id, # nid - note ID
deck_id, # did - deck ID
self.ord, # ord - which card template it corresponds to
now_ts, # mod - modification time as seconds since Unix epoch
-1, # usn - value of -1 indicates need to push to server
stage, # type - 0=new, 1=learning, 2=review
queue, # queue - same as type, but
# -1=suspended, -2=user buried, -3=sched buried
due, # due - new: unused
# learning: due time as integer seconds since Unix epoch
# review: integer days relative to deck creation
interval, # ivl - positive days, negative seconds
ease, # factor - integer ease factor used by SRS, 2500 = 250%
0, # reps - number of reviews
0, # lapses - # times card went from "answered correctly" to "answered incorrectly"
reps_til_grad, # left - reps left until graduation
0, # odue - only used when card is in filtered deck
0, # odid - only used when card is in filtered deck
0, # flags - currently unused
"", # data - currently unused
note_id, # nid - note ID
deck_id, # did - deck ID
self.ord, # ord - which card template it corresponds to
now_ts, # mod - modification time as seconds since Unix epoch
-1, # usn - value of -1 indicates need to push to server
self.options.type, # type - 0=new, 1=learning, 2=review
queue, # queue - same as type, but
# -1=suspended, -2=user buried, -3=sched buried
self.options.due, # due - new: unused
# learning: due time as integer seconds since Unix epoch
# review: integer days relative to deck creation
self.options.ivl, # ivl - positive days, negative seconds
self.options.factor, # factor - integer ease factor used by SRS, 2500 = 250%
0, # reps - number of reviews
0, # lapses - # times card went from "answered correctly" to "answered incorrectly"
self.options.left, # left - reps left until graduation
0, # odue - only used when card is in filtered deck
0, # odid - only used when card is in filtered deck
0, # flags - currently unused
"", # data - currently unused
))


Expand All @@ -196,29 +249,6 @@ def __init__(self, model=None, fields=None, sort_field=None, tags=None, guid=Non
# guid was defined as a property
pass

## Options ##

"""SRS learning stage.
0 = new, 1 = learning, 2 = review."""
self.stage = 0
"""SRS queue status modifiers.
0 = normal, 1 = suspended, 2 = user buried, 3 = scheduler buried"""
self.status = 0
"""Behavior depends on learning stage of note.
new: unused.
learning: due time as integer seconds since Unix epoch.
review: integer days relative to deck creation timestamp."""
self.due = 0
"""Time between next review and the one following.
Positive values are in days, negative in seconds."""
self.interval = 0
"""Integer 'ease' factor used by SRS algorithm.
Example: 2500 = 250%."""
self.ease = 0
"""Repititions remaining until graduation from the learning stage.
Unused during other SRS stages."""
self.reps_til_grad = 0

@property
def sort_field(self):
return self._sort_field or self.fields[0]
Expand Down Expand Up @@ -248,6 +278,18 @@ def guid(self):
def guid(self, val):
self._guid = val

def set_card_options(self, options):
"""If `options` is a single CardOptions, apply it to all cards. If
`options` is a list of CardOptions, apply each CardOptions to the
card of the same index.
"""
try:
for i, card in enumerate(self.cards):
card.options = options[i]
except TypeError:
for card in self.cards:
card.options = options

def write_to_db(self, cursor, now_ts, deck_id):
cursor.execute('INSERT INTO notes VALUES(null,?,?,?,?,?,?,?,?,?,?);', (
self.guid, # guid
Expand All @@ -263,11 +305,8 @@ def write_to_db(self, cursor, now_ts, deck_id):
))

note_id = cursor.lastrowid
queue = -self.status if self.status else self.stage
for card in self.cards:
card.write_to_db(cursor, now_ts, deck_id, note_id,
self.stage, queue, self.due, self.interval,
self.ease, self.reps_til_grad)
card.write_to_db(cursor, now_ts, deck_id, note_id)

def _format_fields(self):
return '\x1f'.join(self.fields)
Expand All @@ -277,39 +316,68 @@ def _format_tags(self):


class OptionsGroup:
def __init__(self, options_id=None, name=None):
def __init__(
self, options_id=None, options_group_name=None,
# Organized according to options window tabs in Anki.
## General ##
max_time_per_answer = 60, # minutes
show_timer = False,
autoplay_audio = True,
replay_audio_for_answer = True,
## New Cards ##
new_steps = [1, 10], # list of minute intervals per learning stage
order = 1, # option selected in dropdown
# (0 = first, 1 = second)
new_cards_per_day = 20, # days
graduating_interval = 1, # days
easy_interval = 4, # days
starting_ease = 2500, # 2500 = 250%
bury_related_new_cards = True,
## Reviews ##
max_reviews_per_day = 100,
easy_bonus = 1.3,
interval_modifier = 1.0,
max_interval = 36500, # days
bury_related_review_cards = True,
## Lapses ##
lapse_steps = [10],
leech_interval_multiplier = 0,
lapse_min_interval = 1,
leech_threshold = 8,
leech_action = 0,
# Used for adding arbitrary options via JSON string. Useful for
# addons.
misc = ''
):
self.options_id = options_id
self.options_group_name = name
# Organized according to options window tabs in Anki.
self.options_group_name = options_group_name
## General ##
self.max_time_per_answer = 60 # minutes
self.show_timer = False
self.autoplay_audio = True
self.replay_audio_for_answer = True
self.max_time_per_answer = max_time_per_answer
self.show_timer = show_timer
self.autoplay_audio = autoplay_audio
self.replay_audio_for_answer = replay_audio_for_answer
## New Cards ##
self.new_steps = [1, 10] # list of minute intervals per learning stage
self.order = 1 # option selected in dropdown (0 = first, 1 = second)
self.new_cards_per_day = 20 # days
self.graduating_interval = 1 # days
self.easy_interval = 4 # days
self.starting_ease = 2500 # 2500 = 250%
self.bury_related_new_cards = True
self.new_steps = new_steps
self.order = order
self.new_cards_per_day = new_cards_per_day
self.graduating_interval = graduating_interval
self.easy_interval = easy_interval
self.starting_ease = starting_ease
self.bury_related_new_cards = bury_related_new_cards
## Reviews ##
self.max_reviews_per_day = 100
self.easy_bonus = 1.3
self.interval_modifier = 1.0
self.max_interval = 36500 # days
self.bury_related_review_cards = True
self.max_reviews_per_day = max_reviews_per_day
self.easy_bonus = easy_bonus
self.interval_modifier = interval_modifier
self.max_interval = max_interval
self.bury_related_review_cards = bury_related_review_cards
## Lapses ##
self.lapse_steps = [10]
self.leech_interval_multiplier = 0
self.lapse_min_interval = 1
self.leech_threshold = 8
self.leech_action = 0

# Used for adding arbitrary options via JSON string. Useful for
# addons.
self.misc = ''
self.lapse_steps = lapse_steps
self.leech_interval_multiplier = leech_interval_multiplier
self.lapse_min_interval = lapse_min_interval
self.leech_threshold = leech_threshold
self.leech_action = leech_action

self.misc = misc

def validate(self):
if self.misc and self.misc[-1] != ',':
Expand Down