diff --git a/anki/deck.py b/anki/deck.py index 9cf16a524..6720a1354 100644 --- a/anki/deck.py +++ b/anki/deck.py @@ -11,12 +11,12 @@ from anki.hooks import runHook, runFilter from anki.sched import Scheduler from anki.models import ModelRegistry from anki.media import MediaRegistry +from anki.groups import GroupRegistry from anki.consts import * from anki.errors import AnkiError import anki.latex # sets up hook -import anki.cards, anki.facts, anki.template, anki.cram, \ - anki.groups, anki.find +import anki.cards, anki.facts, anki.template, anki.cram, anki.find # Settings related to queue building. These may be loaded without the rest of # the config to check due counts faster on mobile clients. @@ -54,6 +54,7 @@ class _Deck(object): self.clearUndo() self.media = MediaRegistry(self) self.models = ModelRegistry(self) + self.groups = GroupRegistry(self) self.load() if not self.crt: d = datetime.datetime.today() @@ -88,15 +89,14 @@ class _Deck(object): self.qconf, self.conf, models, - self.groups, - self.gconf) = self.db.first(""" + groups, + gconf) = self.db.first(""" select crt, mod, scm, dty, syncName, lastSync, qconf, conf, models, groups, gconf from deck""") self.qconf = simplejson.loads(self.qconf) self.conf = simplejson.loads(self.conf) - self.groups = simplejson.loads(self.groups) - self.gconf = simplejson.loads(self.gconf) self.models.load(models) + self.groups.load(groups, gconf) def flush(self, mod=None): "Flush state to DB, updating mod time." @@ -104,14 +104,13 @@ qconf, conf, models, groups, gconf from deck""") self.db.execute( """update deck set crt=?, mod=?, scm=?, dty=?, syncName=?, lastSync=?, -qconf=?, conf=?, groups=?, gconf=?""", +qconf=?, conf=?""", self.crt, self.mod, self.scm, self.dty, self.syncName, self.lastSync, simplejson.dumps(self.qconf), - simplejson.dumps(self.conf), - simplejson.dumps(self.groups), - simplejson.dumps(self.gconf)) + simplejson.dumps(self.conf)) self.models.flush() + self.groups.flush() def save(self, name=None, mod=None): "Flush, commit DB, and take out another write lock." @@ -420,7 +419,7 @@ select id from facts where id in %s and id not in (select fid from cards)""" % fields[name] = "" fields['Tags'] = data[5] fields['Model'] = model['name'] - fields['Group'] = self.groupName(data[3]) + fields['Group'] = self.groups.name(data[3]) template = model['tmpls'][data[4]] fields['Template'] = template['name'] # render q & a @@ -504,43 +503,6 @@ update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res def delTags(self, ids, tags): self.addTags(ids, tags, False) - # Groups - ########################################################################## - # the id keys are strings because that's the way they're stored in json, - # but the anki code passes around integers - - def groupID(self, name, create=True): - "Add a group with NAME. Reuse group if already exists. Return id." - for id, g in self.groups.items(): - if g['name'].lower() == name.lower(): - return int(id) - if not create: - return None - g = dict(name=name, conf=1, mod=intTime()) - while 1: - id = intTime(1000) - if str(id) in self.groups: - continue - self.groups[str(id)] = g - return id - - def groupName(self, gid): - return self.groups[str(gid)]['name'] - - def groupConf(self, gid): - return self.gconf[str(self.groups[str(gid)]['conf'])] - - def delGroup(self, gid): - self.modSchema() - self.db.execute("update cards set gid = 1 where gid = ?", gid) - self.db.execute("update facts set gid = 1 where gid = ?", gid) - self.db.execute("delete from groups where id = ?", gid) - print "fixme: loop through models and update stale gid references" - - def setGroup(self, cids, gid): - self.db.execute( - "update cards set gid = ? where id in "+ids2str(cids), gid) - # Tag-based selective study ########################################################################## diff --git a/anki/find.py b/anki/find.py index 9111f1fc2..a1e86eece 100644 --- a/anki/find.py +++ b/anki/find.py @@ -192,8 +192,8 @@ order by %s""" % (lim, sort) def _findGroup(self, val, isNeg): extra = "!" if isNeg else "" - id = self.deck.groupID(val, create=False) or 0 - self.lims['card'].append("c.gid %s= %d" % (extra, id)) + id = self.deck.groups.id(val, create=False) or 0 + self.lims['card'].append("c.gid %s= %s" % (extra, id)) def _findTemplate(self, val, isNeg): lims = [] diff --git a/anki/groups.py b/anki/groups.py index 0c56c9617..02f88b0f8 100644 --- a/anki/groups.py +++ b/anki/groups.py @@ -39,3 +39,65 @@ defaultData = { 'activeTags': None, 'inactiveTags': None, } + +class GroupRegistry(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): + 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 + g = dict(name=name, conf=1, mod=intTime()) + while 1: + id = str(intTime(1000)) + if id in self.groups: + continue + self.groups[id] = g + self.save(g) + return int(id) + + def del_(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" + + # 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 setGroup(self, cids, gid): + self.db.execute( + "update cards set gid = ? where id in "+ids2str(cids), gid) diff --git a/anki/sched.py b/anki/sched.py index 3af84e5fd..389947e84 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -154,7 +154,7 @@ from cards group by gid""", self.today): for (gid, grp) in self._orderedGroups()] def _orderedGroups(self): - grps = self.deck.groups.items() + grps = self.deck.groups.groups.items() def key(grp): return grp[1]['name'] grps.sort(key=key) @@ -595,7 +595,7 @@ queue = 2 %s and due <= :lim order by %s limit %d""" % ( ########################################################################## def _cardConf(self, card): - return self.deck.groupConf(card.gid) + return self.deck.groups.conf(card.gid) def _groupLimit(self): l = self.deck.qconf['groups'] diff --git a/anki/stats.py b/anki/stats.py index 3ab8fb4b2..08f899fc7 100644 --- a/anki/stats.py +++ b/anki/stats.py @@ -49,8 +49,8 @@ class CardStats(object): self.addLine(_("Position"), c.due) self.addLine(_("Model"), c.model()['name']) self.addLine(_("Template"), c.template()['name']) - self.addLine(_("Current Group"), self.deck.groupName(c.gid)) - self.addLine(_("Initial Group"), self.deck.groupName(c.fact().gid)) + self.addLine(_("Current Group"), self.deck.groups.name(c.gid)) + self.addLine(_("Home Group"), self.deck.groups.name(c.fact().gid)) self.txt += "" return self.txt diff --git a/tests/test_deck.py b/tests/test_deck.py index ec47fb591..4db191f90 100644 --- a/tests/test_deck.py +++ b/tests/test_deck.py @@ -133,15 +133,15 @@ def test_upgrade(): def test_groups(): deck = getEmptyDeck() # we start with a standard group - assert len(deck.groups) == 1 + assert len(deck.groups.groups) == 1 # it should have an id of 1 - assert deck.groups['1'] + assert deck.groups.name(1) # create a new group - ts = deck.groupID("new group") - assert ts - assert len(deck.groups) == 2 + g = deck.groups.id("new group") + assert g + assert len(deck.groups.groups) == 2 # should get the same id - assert deck.groupID("new group") == ts + assert deck.groups.id("new group") == g # by default, everything should be shown assert not deck.qconf['groups'] diff --git a/tests/test_sched.py b/tests/test_sched.py index b329efe82..321e8947b 100644 --- a/tests/test_sched.py +++ b/tests/test_sched.py @@ -581,7 +581,7 @@ def test_ordcycle(): def test_counts(): d = getEmptyDeck() # add a second group - assert d.groupID("new group") + assert d.groups.id("new group") # for each card type for type in range(3): # and each of the groups @@ -680,7 +680,7 @@ def test_groupCounts(): # and one that's a child f = d.newFact() f['Front'] = u"two" - default1 = f.gid = d.groupID("Default::1") + default1 = f.gid = d.groups.id("Default::1") d.addFact(f) # make it a review card c = f.cards()[0] @@ -690,16 +690,16 @@ def test_groupCounts(): # add one more with a new group f = d.newFact() f['Front'] = u"two" - foobar = f.gid = d.groupID("foo::bar") + foobar = f.gid = d.groups.id("foo::bar") d.addFact(f) # and one that's a sibling f = d.newFact() f['Front'] = u"three" - foobaz = f.gid = d.groupID("foo::baz") + foobaz = f.gid = d.groups.id("foo::baz") d.addFact(f) d.reset() assert d.sched.counts() == (3, 0, 1) - assert len(d.groups) == 4 + assert len(d.groups.groups) == 4 cnts = d.sched.groupCounts() assert cnts[0] == ["Default", 1, 1, 0, 1] assert cnts[1] == ["Default::1", default1, 1, 1, 0]