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.
This commit is contained in:
Damien Elmes 2011-09-07 03:02:07 +09:00
parent de8a5b69ed
commit 28d045feef
3 changed files with 55 additions and 38 deletions

View file

@ -94,8 +94,9 @@ class GroupManager(object):
# if it's a top level group, it gets the top level config # if it's a top level group, it gets the top level config
g = defaultTopConf.copy() g = defaultTopConf.copy()
else: else:
# not top level. calling code should ensure parents already exist? # not top level; ensure all parents exist
g = {} g = {}
self._ensureParents(name)
g['name'] = name g['name'] = name
g['conf'] = 1 g['conf'] = 1
while 1: while 1:
@ -122,6 +123,16 @@ class GroupManager(object):
"A list of all groups." "A list of all groups."
return self.groups.values() 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 # Group utils
############################################################# #############################################################
@ -164,8 +175,7 @@ class GroupManager(object):
return return
# save the top level group # save the top level group
name = self.groups[str(gid)]['name'] name = self.groups[str(gid)]['name']
path = name.split("::") self.deck.conf['topGroup'] = self.topFor(name)
self.deck.conf['topGroup'] = self.id(path[0])
# current group # current group
self.deck.conf['curGroup'] = gid self.deck.conf['curGroup'] = gid
# and active groups (current + all children) # and active groups (current + all children)
@ -174,3 +184,8 @@ class GroupManager(object):
if g['name'].startswith(name + "::"): if g['name'].startswith(name + "::"):
actv.append(g['id']) actv.append(g['id'])
self.deck.conf['activeGroups'] = actv self.deck.conf['activeGroups'] = actv
def topFor(self, name):
"The top level gid for NAME."
path = name.split("::")
return self.id(path[0])

View file

@ -108,15 +108,28 @@ order by due""" % self._groupLimit(),
########################################################################## ##########################################################################
def groupCounts(self): def groupCounts(self):
"Returns [groupname, cards, due, new]" "Returns [groupname, hasDue, hasNew]"
# find groups with 1 or more due cards
gids = {} gids = {}
for (gid, all, rev, new) in self.deck.db.execute(""" for g in self.deck.groups.all():
select gid, count(), hasDue = self.deck.db.scalar("""
sum(case when queue = 2 and due <= ? then 1 else 0 end), select 1 from cards where gid = ? and
sum(case when queue = 0 then 1 else 0 end) ((queue = 2 and due <= ?) or (queue = 1 and due < ?)) limit 1""",
from cards group by gid""", self.today): g['id'], self.today, intTime())
gids[gid] = [all, rev, new] top = self.deck.groups.get(
return [[grp['name'], int(gid)]+gids.get(int(gid), [0, 0, 0]) self.deck.groups.topFor(g['name']))
if top['newToday'][0] != self.today:
# it's a new day; reset counts
top['newToday'] = [self.today, 0]
hasNew = max(0, top['newPerDay'] - top['newToday'][1])
if hasNew:
# if the limit hasn't run out, check to see if there are
# actually cards
hasNew = self.deck.db.scalar(
"select 1 from cards where queue = 0 and gid = ? limit 1",
g['id'])
gids[g['id']] = [hasDue or 0, hasNew or 0]
return [[grp['name'], int(gid)]+gids.get(int(gid))
for (gid, grp) in self._orderedGroups()] for (gid, grp) in self._orderedGroups()]
def _orderedGroups(self): def _orderedGroups(self):
@ -145,7 +158,6 @@ from cards group by gid""", self.today):
for (head, tail) in itertools.groupby(grps, key=key): for (head, tail) in itertools.groupby(grps, key=key):
tail = list(tail) tail = list(tail)
gid = None gid = None
all = 0
rev = 0 rev = 0
new = 0 new = 0
children = [] children = []
@ -153,9 +165,8 @@ from cards group by gid""", self.today):
if len(c[0]) == 1: if len(c[0]) == 1:
# current node # current node
gid = c[1] gid = c[1]
all += c[2] rev += c[2]
rev += c[3] new += c[3]
new += c[4]
else: else:
# set new string to tail # set new string to tail
c[0] = c[0][1] c[0] = c[0][1]
@ -163,10 +174,9 @@ from cards group by gid""", self.today):
children = self._groupChildren(children) children = self._groupChildren(children)
# tally up children counts # tally up children counts
for ch in children: for ch in children:
all += ch[2] rev += ch[2]
rev += ch[3] new += ch[3]
new += ch[4] tree.append((head, gid, rev, new, children))
tree.append((head, gid, all, rev, new, children))
return tuple(tree) return tuple(tree)
# Getting the next card # Getting the next card

View file

@ -695,32 +695,24 @@ def test_groupCounts():
d.addFact(f) d.addFact(f)
d.reset() d.reset()
assert d.sched.counts() == (3, 0, 1) assert d.sched.counts() == (3, 0, 1)
assert len(d.groups.groups) == 4 assert len(d.groups.groups) == 5
cnts = d.sched.groupCounts() cnts = d.sched.groupCounts()
assert cnts[0] == ["Default", 1, 1, 0, 1] assert cnts[0] == ["Default", 1, 0, 1]
assert cnts[1] == ["Default::1", default1, 1, 1, 0] assert cnts[1] == ["Default::1", default1, 1, 0]
assert cnts[2] == ["foo::bar", foobar, 1, 0, 1] assert cnts[2] == ["foo", d.groups.id("foo"), 0, 0]
assert cnts[3] == ["foo::baz", foobaz, 1, 0, 1] assert cnts[3] == ["foo::bar", foobar, 0, 1]
assert cnts[4] == ["foo::baz", foobaz, 0, 1]
tree = d.sched.groupCountTree() tree = d.sched.groupCountTree()
assert tree[0][0] == "Default" assert tree[0][0] == "Default"
# sum of child and parent # sum of child and parent
assert tree[0][1] == 1 assert tree[0][1] == 1
assert tree[0][2] == 2 assert tree[0][2] == 1
assert tree[0][3] == 1 assert tree[0][3] == 1
assert tree[0][4] == 1
# child count is just review # child count is just review
assert tree[0][5][0][0] == "1" assert tree[0][4][0][0] == "1"
assert tree[0][5][0][1] == default1 assert tree[0][4][0][1] == default1
assert tree[0][5][0][2] == 1 assert tree[0][4][0][2] == 1
assert tree[0][5][0][3] == 1 assert tree[0][4][0][3] == 0
assert tree[0][5][0][4] == 0
# event if parent group didn't exist, it should have been created with a
# counts summary, with an empty gid
assert tree[1][0] == "foo"
assert tree[1][1] == None
assert tree[1][2] == 2
assert tree[1][3] == 0
assert tree[1][4] == 2
def test_reorder(): def test_reorder():
d = getEmptyDeck() d = getEmptyDeck()