From 28d045feef641279a3000bb7cf6a97588f1787b1 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 7 Sep 2011 03:02:07 +0900 Subject: [PATCH] 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. --- anki/groups.py | 21 ++++++++++++++++++--- anki/sched.py | 42 ++++++++++++++++++++++++++---------------- tests/test_sched.py | 30 +++++++++++------------------- 3 files changed, 55 insertions(+), 38 deletions(-) diff --git a/anki/groups.py b/anki/groups.py index 1a27bbe0e..aec4f8c8e 100644 --- a/anki/groups.py +++ b/anki/groups.py @@ -94,8 +94,9 @@ class GroupManager(object): # if it's a top level group, it gets the top level config g = defaultTopConf.copy() else: - # not top level. calling code should ensure parents already exist? + # not top level; ensure all parents exist g = {} + self._ensureParents(name) g['name'] = name g['conf'] = 1 while 1: @@ -122,6 +123,16 @@ class GroupManager(object): "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 ############################################################# @@ -164,8 +175,7 @@ class GroupManager(object): return # save the top level group name = self.groups[str(gid)]['name'] - path = name.split("::") - self.deck.conf['topGroup'] = self.id(path[0]) + self.deck.conf['topGroup'] = self.topFor(name) # current group self.deck.conf['curGroup'] = gid # and active groups (current + all children) @@ -174,3 +184,8 @@ class GroupManager(object): 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]) diff --git a/anki/sched.py b/anki/sched.py index 188f5c3d2..6925e2be7 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -108,15 +108,28 @@ order by due""" % self._groupLimit(), ########################################################################## def groupCounts(self): - "Returns [groupname, cards, due, new]" + "Returns [groupname, hasDue, hasNew]" + # find groups with 1 or more due cards gids = {} - for (gid, all, rev, new) in self.deck.db.execute(""" -select gid, count(), -sum(case when queue = 2 and due <= ? then 1 else 0 end), -sum(case when queue = 0 then 1 else 0 end) -from cards group by gid""", self.today): - gids[gid] = [all, rev, new] - return [[grp['name'], int(gid)]+gids.get(int(gid), [0, 0, 0]) + for g in self.deck.groups.all(): + hasDue = self.deck.db.scalar(""" +select 1 from cards where gid = ? and +((queue = 2 and due <= ?) or (queue = 1 and due < ?)) limit 1""", + g['id'], self.today, intTime()) + top = self.deck.groups.get( + 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()] def _orderedGroups(self): @@ -145,7 +158,6 @@ from cards group by gid""", self.today): for (head, tail) in itertools.groupby(grps, key=key): tail = list(tail) gid = None - all = 0 rev = 0 new = 0 children = [] @@ -153,9 +165,8 @@ from cards group by gid""", self.today): if len(c[0]) == 1: # current node gid = c[1] - all += c[2] - rev += c[3] - new += c[4] + rev += c[2] + new += c[3] else: # set new string to tail c[0] = c[0][1] @@ -163,10 +174,9 @@ from cards group by gid""", self.today): children = self._groupChildren(children) # tally up children counts for ch in children: - all += ch[2] - rev += ch[3] - new += ch[4] - tree.append((head, gid, all, rev, new, children)) + rev += ch[2] + new += ch[3] + tree.append((head, gid, rev, new, children)) return tuple(tree) # Getting the next card diff --git a/tests/test_sched.py b/tests/test_sched.py index 390ce67b3..502431c53 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -695,32 +695,24 @@ def test_groupCounts(): d.addFact(f) d.reset() assert d.sched.counts() == (3, 0, 1) - assert len(d.groups.groups) == 4 + assert len(d.groups.groups) == 5 cnts = d.sched.groupCounts() - assert cnts[0] == ["Default", 1, 1, 0, 1] - assert cnts[1] == ["Default::1", default1, 1, 1, 0] - assert cnts[2] == ["foo::bar", foobar, 1, 0, 1] - assert cnts[3] == ["foo::baz", foobaz, 1, 0, 1] + assert cnts[0] == ["Default", 1, 0, 1] + assert cnts[1] == ["Default::1", default1, 1, 0] + assert cnts[2] == ["foo", d.groups.id("foo"), 0, 0] + assert cnts[3] == ["foo::bar", foobar, 0, 1] + assert cnts[4] == ["foo::baz", foobaz, 0, 1] tree = d.sched.groupCountTree() assert tree[0][0] == "Default" # sum of child and parent assert tree[0][1] == 1 - assert tree[0][2] == 2 + assert tree[0][2] == 1 assert tree[0][3] == 1 - assert tree[0][4] == 1 # child count is just review - assert tree[0][5][0][0] == "1" - assert tree[0][5][0][1] == default1 - assert tree[0][5][0][2] == 1 - assert tree[0][5][0][3] == 1 - 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 + assert tree[0][4][0][0] == "1" + assert tree[0][4][0][1] == default1 + assert tree[0][4][0][2] == 1 + assert tree[0][4][0][3] == 0 def test_reorder(): d = getEmptyDeck()