Anki/anki/groups.py
Damien Elmes 28d045feef rewrite groupCounts()
Instead of collecting the exact number of cards, we just record whether a
group has any reviews or new cards. By not needing to calculate the exact
numbers, it runs a lot faster than before.

Also, changed the group code to ensure parents are automatically created when
a group is added.
2011-09-07 03:02:07 +09:00

191 lines
5.5 KiB
Python

# -*- coding: utf-8 -*-
# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import simplejson
from anki.utils import intTime
from anki.consts import *
# fixmes:
# - make sure lists like new[delays] are not being shared by multiple groups
# - make sure all children have parents (create as necessary)
# - when renaming a group, top level properties should be added or removed as
# appropriate
# configuration only available to top level groups
defaultTopConf = {
'newPerDay': 20,
'newToday': [0, 0], # currentDay, count
'newTodayOrder': NEW_TODAY_ORD,
'newSpread': NEW_CARDS_DISTRIBUTE,
'collapseTime': 1200,
'repLim': 0,
'timeLim': 600,
'curModel': None,
}
# configuration available to all groups
defaultConf = {
'new': {
'delays': [1, 10],
'ints': [1, 7, 4],
'initialFactor': 2500,
},
'lapse': {
'delays': [1, 10],
'mult': 0,
'minInt': 1,
'relearn': True,
'leechFails': 16,
# type 0=suspend, 1=tagonly
'leechAction': 0,
},
'cram': {
'delays': [1, 5, 10],
'resched': True,
'reset': True,
'mult': 0,
'minInt': 1,
},
'rev': {
'ease4': 1.3,
'fuzz': 0.05,
'minSpace': 1,
},
'maxTaken': 60,
'mod': 0,
}
class GroupManager(object):
# Registry save/load
#############################################################
def __init__(self, deck):
self.deck = deck
def load(self, groups, gconf):
self.groups = simplejson.loads(groups)
self.gconf = simplejson.loads(gconf)
self.changed = False
def save(self, g):
"Can be called with either a group or a group configuration."
g['mod'] = intTime()
self.changed = True
def flush(self):
if self.changed:
self.deck.db.execute("update deck set groups=?, gconf=?",
simplejson.dumps(self.groups),
simplejson.dumps(self.gconf))
# Group save/load
#############################################################
def id(self, name, create=True):
"Add a group with NAME. Reuse group if already exists. Return id as int."
for id, g in self.groups.items():
if g['name'].lower() == name.lower():
return int(id)
if not create:
return None
if "::" not in name:
# if it's a top level group, it gets the top level config
g = defaultTopConf.copy()
else:
# not top level; ensure all parents exist
g = {}
self._ensureParents(name)
g['name'] = name
g['conf'] = 1
while 1:
id = intTime(1000)
if str(id) in self.groups:
continue
g['id'] = id
self.groups[str(id)] = g
self.save(g)
return int(id)
def rem(self, gid):
self.deck.modSchema()
self.deck.db.execute("update cards set gid = 1 where gid = ?", gid)
self.deck.db.execute("update facts set gid = 1 where gid = ?", gid)
self.deck.db.execute("delete from groups where id = ?", gid)
print "fixme: loop through models and update stale gid references"
def allNames(self):
"An unsorted list of all group names."
return [x['name'] for x in self.groups.values()]
def all(self):
"A list of all groups."
return self.groups.values()
def _ensureParents(self, name):
path = name.split("::")
s = ""
for p in path[:-1]:
if not s:
s += p
else:
s += "::" + p
self.id(s)
# Group utils
#############################################################
def name(self, gid):
return self.groups[str(gid)]['name']
def conf(self, gid):
return self.gconf[str(self.groups[str(gid)]['conf'])]
def get(self, gid):
return self.groups[str(gid)]
def setGroup(self, cids, gid):
self.db.execute(
"update cards set gid = ? where id in "+ids2str(cids), gid)
# Group selection
#############################################################
def top(self):
"The current top level group as an object, and marks as modified."
g = self.get(self.deck.conf['topGroup'])
self.save(g)
return g
def active(self):
"The currrently active gids."
return self.deck.conf['activeGroups']
def selected(self):
"The currently selected gid, or None if whole collection."
return self.deck.conf['curGroup']
def select(self, gid):
"Select a new group. If gid is None, select whole collection."
if not gid:
self.deck.conf['topGroup'] = 1
self.deck.conf['curGroup'] = None
self.deck.conf['activeGroups'] = []
return
# save the top level group
name = self.groups[str(gid)]['name']
self.deck.conf['topGroup'] = self.topFor(name)
# current group
self.deck.conf['curGroup'] = gid
# and active groups (current + all children)
actv = [gid]
for g in self.all():
if g['name'].startswith(name + "::"):
actv.append(g['id'])
self.deck.conf['activeGroups'] = actv
def topFor(self, name):
"The top level gid for NAME."
path = name.split("::")
return self.id(path[0])