diff --git a/anki/deck.py b/anki/deck.py index 347344c89..892293d4b 100644 --- a/anki/deck.py +++ b/anki/deck.py @@ -13,7 +13,6 @@ from anki.utils import parseTags, tidyHTML, genID, ids2str, hexifyID, \ canonifyTags, joinTags, addTags, checksum, fieldChecksum from anki.history import CardHistoryEntry from anki.models import Model, CardModel, formatQA -from anki.stats import dailyStats, globalStats, genToday from anki.fonts import toPlatformFont from anki.tags import initTagTables, tagIds from operator import itemgetter @@ -26,7 +25,7 @@ from anki.upgrade import upgradeSchema, updateIndices, upgradeDeck, DECK_VERSION import anki.latex # sets up hook # ensure all the DB metadata in other files is loaded before proceeding -import anki.models, anki.facts, anki.cards, anki.stats +import anki.models, anki.facts, anki.cards import anki.history, anki.media # rest @@ -206,6 +205,13 @@ class Deck(object): # global counts self.cardCount = self.s.scalar("select count(*) from cards") self.factCount = self.s.scalar("select count(*) from facts") + # day counts + (self.repsToday, self.newSeenToday) = self.s.first(""" +select count(), sum(case when reps = 1 then 1 else 0 end) from reviewHistory +where time > :t""", t=self.failedCutoff-86400) + self.newSeenToday = self.newSeenToday or 0 + print "newSeenToday in answer(), reset called twice" + print "newSeenToday needs to account for drill mode too." # due counts self.rebuildFailedCount() self.rebuildRevCount() @@ -251,7 +257,7 @@ class Deck(object): "and combinedDue < :lim"), lim=self.dueCutoff) def _rebuildNewCount(self): - self.newCount = self.s.scalar( + self.newAvail = self.s.scalar( self.cardLimit( "newActive", "newInactive", "select count(*) from cards c where type = 2 " @@ -260,9 +266,9 @@ class Deck(object): self.spacedCards = [] def _updateNewCountToday(self): - self.newCountToday = max(min( - self.newCount, self.newCardsPerDay - - self.newCardsDoneToday()), 0) + self.newCount = max(min( + self.newAvail, self.newCardsPerDay - + self.newSeenToday), 0) def _fillFailedQueue(self): if self.failedSoonCount and not self.failedQueue: @@ -285,7 +291,7 @@ limit %d""" % (self.revOrder(), self.queueLimit)), lim=self.dueCutoff) self.revQueue.reverse() def _fillNewQueue(self): - if self.newCountToday and not self.newQueue and not self.spacedCards: + if self.newCount and not self.newQueue and not self.spacedCards: self.newQueue = self.s.all( self.cardLimit( "newActive", "newInactive", """ @@ -355,7 +361,7 @@ produce the problem. Counts %d %d %d Queue %d %d %d Card info: %d %d %d -New type: %s""" % (self.failedSoonCount, self.revCount, self.newCountToday, +New type: %s""" % (self.failedSoonCount, self.revCount, self.newCount, len(self.failedQueue), len(self.revQueue), len(self.newQueue), card.reps, card.successive, oldSuc, `newType`)) @@ -443,9 +449,6 @@ when type >= 0 then relativeDelay else relativeDelay - 3 end) self.dueCutoff = time.time() def reset(self): - # setup global/daily stats - self._globalStats = globalStats(self) - self._dailyStats = dailyStats(self) # recheck counts self.rebuildCounts() # empty queues; will be refilled by getCard() @@ -455,9 +458,9 @@ when type >= 0 then relativeDelay else relativeDelay - 3 end) self.spacedFacts = {} # determine new card distribution if self.newCardSpacing == NEW_CARDS_DISTRIBUTE: - if self.newCountToday: + if self.newCount: self.newCardModulus = ( - (self.newCountToday + self.revCount) / self.newCountToday) + (self.newCount + self.revCount) / self.newCount) # if there are cards to review, ensure modulo >= 2 if self.revCount: self.newCardModulus = max(2, self.newCardModulus) @@ -474,7 +477,7 @@ when type >= 0 then relativeDelay else relativeDelay - 3 end) def checkDay(self): # check if the day has rolled over - if genToday(self) != self._dailyStats.day: + if time.time() > self.failedCutoff: self.updateCutoff() self.reset() @@ -531,7 +534,7 @@ order by combinedDue limit %d""" % self.queueLimit), lim=self.dueCutoff) self.scheduler = "learnMore" def _rebuildLearnMoreCount(self): - self.newCount = self.s.scalar( + self.newAvail = self.s.scalar( self.cardLimit( "newActive", "newInactive", "select count(*) from cards c where type = 2 " @@ -539,7 +542,7 @@ order by combinedDue limit %d""" % self.queueLimit), lim=self.dueCutoff) self.spacedCards = [] def _updateLearnMoreCountToday(self): - self.newCountToday = self.newCount + self.newCount = self.newAvail # Cramming ########################################################################## @@ -611,8 +614,8 @@ order by combinedDue limit %d""" % self.queueLimit), lim=self.dueCutoff) self.failedCramQueue.pop() def _rebuildCramNewCount(self): + self.newAvail = 0 self.newCount = 0 - self.newCountToday = 0 def _cramCardLimit(self, active, inactive, sql): # inactive is (currently) ignored @@ -683,7 +686,7 @@ limit %s""" % (self.cramOrder, self.queueLimit))) if self.revNoSpaced(): return self.revQueue[-1][0] # new cards left? - if self.newCountToday: + if self.newCount: id = self.getNewCard() if id: return id @@ -706,7 +709,7 @@ limit %s""" % (self.cramOrder, self.queueLimit))) def _timeForNewCard(self): "True if it's time to display a new card when distributing." - if not self.newCountToday: + if not self.newCount: return False if self.newCardSpacing == NEW_CARDS_LAST: return False @@ -797,7 +800,7 @@ limit %s""" % (self.cramOrder, self.queueLimit))) elif oldQueue == 1: self.revCount -= 1 else: - self.newCount -= 1 + self.newAvail -= 1 # card stats anki.cards.Card.updateStats(card, ease, oldState) # update type & ensure past cutoff @@ -811,9 +814,6 @@ limit %s""" % (self.cramOrder, self.queueLimit))) # save card.combinedDue = card.due card.toDB(self.s) - # global/daily stats - anki.stats.updateAllStats(self.s, self._globalStats, self._dailyStats, - card, ease, oldState) # review history entry = CardHistoryEntry(card, ease, lastDelay) entry.writeSQL(self.s) @@ -1187,13 +1187,6 @@ where type between -3 and -1 and id in %s""" % select 1 from cards where combinedDue < :now and type between 0 and 1 limit 1""", now=self.dueCutoff) - def newCardsDoneToday(self): - return (self._dailyStats.newEase0 + - self._dailyStats.newEase1 + - self._dailyStats.newEase2 + - self._dailyStats.newEase3 + - self._dailyStats.newEase4) - def spacedCardCount(self): "Number of spaced cards." return self.s.scalar(""" @@ -1251,22 +1244,9 @@ combinedDue > :now and due < :now""", now=time.time()) # Stats ########################################################################## - def getStats(self, short=False): - "Return some commonly needed stats." - stats = anki.stats.getStats(self.s, self._globalStats, self._dailyStats) - # add scheduling related stats - stats['new'] = self.newCountToday - stats['failed'] = self.failedSoonCount - stats['rev'] = self.revCount - if stats['dAverageTime']: - stats['timeLeft'] = anki.utils.fmtTimeSpan( - self.getETA(stats), pad=0, point=1, short=short) - else: - stats['timeLeft'] = _("Unknown") - return stats - def getETA(self, stats): # rev + new cards first, account for failures + import traceback; traceback.print_stack() count = stats['rev'] + stats['new'] count *= 1 + stats['gYoungNo%'] / 100.0 left = count * stats['dAverageTime'] @@ -1377,7 +1357,8 @@ where factId = :fid and cardModelId = :cmid""", fact.created+0.0001*cardModel.ordinal) self.updateCardTags([card.id]) self.cardCount += 1 - self.newCount += 1 + raise Exception("incorrect; not checking selective study") + self.newAvail += 1 ids.append(card.id) if ids: @@ -2794,18 +2775,21 @@ select id from facts where spaceUntil like :_ff_%d escape '\\'""" % c assert '\\' not in n return n - # Session handling + # Timeboxing ########################################################################## - def startSession(self): + def startTimebox(self): self.lastSessionStart = self.sessionStartTime self.sessionStartTime = time.time() - self.sessionStartReps = self.getStats()['dTotal'] + self.sessionStartReps = self.repsToday - def stopSession(self): + def stopTimebox(self): self.sessionStartTime = 0 - def sessionLimitReached(self): + def timeboxStarted(self): + return self.sessionStartTime + + def timeboxReached(self): if not self.sessionStartTime: # not started return False @@ -2813,7 +2797,7 @@ select id from facts where spaceUntil like :_ff_%d escape '\\'""" % c (self.sessionStartTime + self.sessionTimeLimit)): return True if (self.sessionRepLimit and self.sessionRepLimit <= - self.getStats()['dTotal'] - self.sessionStartReps): + self.repsToday - self.sessionStartReps): return True return False @@ -3606,8 +3590,6 @@ class DeckStorage(object): raise e if not rebuild: # minimal startup - deck._globalStats = globalStats(deck) - deck._dailyStats = dailyStats(deck) return deck oldMod = deck.modified # fix a bug with current model being unset diff --git a/anki/exporting.py b/anki/exporting.py index 657d58a0f..51e3ca2f4 100644 --- a/anki/exporting.py +++ b/anki/exporting.py @@ -126,8 +126,6 @@ relativeDelay = 2, combinedDue = created, modified = :now """, now=time.time()) - self.newDeck.s.statement(""" -delete from stats""") # media if self.includeMedia: server.deck.mediaPrefix = "" diff --git a/anki/graphs.py b/anki/graphs.py index 2ad97c8d5..6aa556b71 100644 --- a/anki/graphs.py +++ b/anki/graphs.py @@ -2,12 +2,9 @@ # Copyright: Damien Elmes # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html -import os, sys, time -import anki.stats +import os, sys, time, datetime from anki.lang import _ -import datetime - #colours for graphs dueYoungC = "#ffb380" dueMatureC = "#ff5555" @@ -100,16 +97,17 @@ from cards c where relativeDelay = 1 and type >= 0 and interval > 21""" dayReps = self.getDayReps() - todaydt = self.deck._dailyStats.day + todaydt = datetime.datetime.utcfromtimestamp( + time.time() - self.deck.utcOffset).date() for dest, source in [("dayRepsNew", "combinedNewReps"), ("dayRepsYoung", "combinedYoungReps"), ("dayRepsMature", "matureReps")]: self.stats[dest] = dict( - map(lambda dr: (-(todaydt -datetime.date( + map(lambda dr: (-(todaydt - datetime.date( *(int(x)for x in dr["day"].split("-")))).days, dr[source]), dayReps)) self.stats['dayTimes'] = dict( - map(lambda dr: (-(todaydt -datetime.date( + map(lambda dr: (-(todaydt - datetime.date( *(int(x)for x in dr["day"].split("-")))).days, dr["reviewTime"]/60.0), dayReps)) def getDayReps(self): diff --git a/anki/stats.py b/anki/stats.py index eafc5e763..0e4a1843b 100644 --- a/anki/stats.py +++ b/anki/stats.py @@ -2,246 +2,13 @@ # Copyright: Damien Elmes # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html -# we track statistics over the life of the deck, and per-day -STATS_LIFE = 0 -STATS_DAY = 1 - -import unicodedata, time, sys, os, datetime +import time, sys, os, datetime import anki, anki.utils -from datetime import date from anki.db import * from anki.lang import _, ngettext from anki.utils import canonifyTags, ids2str from anki.hooks import runFilter -# Tracking stats on the DB -########################################################################## - -statsTable = Table( - 'stats', metadata, - Column('id', Integer, primary_key=True), - Column('type', Integer, nullable=False), - Column('day', Date, nullable=False), - Column('reps', Integer, nullable=False, default=0), - Column('averageTime', Float, nullable=False, default=0), - Column('reviewTime', Float, nullable=False, default=0), - # next two columns no longer used - Column('distractedTime', Float, nullable=False, default=0), - Column('distractedReps', Integer, nullable=False, default=0), - Column('newEase0', Integer, nullable=False, default=0), - Column('newEase1', Integer, nullable=False, default=0), - Column('newEase2', Integer, nullable=False, default=0), - Column('newEase3', Integer, nullable=False, default=0), - Column('newEase4', Integer, nullable=False, default=0), - Column('youngEase0', Integer, nullable=False, default=0), - Column('youngEase1', Integer, nullable=False, default=0), - Column('youngEase2', Integer, nullable=False, default=0), - Column('youngEase3', Integer, nullable=False, default=0), - Column('youngEase4', Integer, nullable=False, default=0), - Column('matureEase0', Integer, nullable=False, default=0), - Column('matureEase1', Integer, nullable=False, default=0), - Column('matureEase2', Integer, nullable=False, default=0), - Column('matureEase3', Integer, nullable=False, default=0), - Column('matureEase4', Integer, nullable=False, default=0)) - -class Stats(object): - def __init__(self): - self.day = None - self.reps = 0 - self.averageTime = 0 - self.reviewTime = 0 - self.distractedTime = 0 - self.distractedReps = 0 - self.newEase0 = 0 - self.newEase1 = 0 - self.newEase2 = 0 - self.newEase3 = 0 - self.newEase4 = 0 - self.youngEase0 = 0 - self.youngEase1 = 0 - self.youngEase2 = 0 - self.youngEase3 = 0 - self.youngEase4 = 0 - self.matureEase0 = 0 - self.matureEase1 = 0 - self.matureEase2 = 0 - self.matureEase3 = 0 - self.matureEase4 = 0 - - def fromDB(self, s, id): - r = s.first("select * from stats where id = :id", id=id) - (self.id, - self.type, - self.day, - self.reps, - self.averageTime, - self.reviewTime, - self.distractedTime, - self.distractedReps, - self.newEase0, - self.newEase1, - self.newEase2, - self.newEase3, - self.newEase4, - self.youngEase0, - self.youngEase1, - self.youngEase2, - self.youngEase3, - self.youngEase4, - self.matureEase0, - self.matureEase1, - self.matureEase2, - self.matureEase3, - self.matureEase4) = r - self.day = datetime.date(*[int(i) for i in self.day.split("-")]) - - def create(self, s, type, day): - self.type = type - self.day = day - s.execute("""insert into stats -(type, day, reps, averageTime, reviewTime, distractedTime, distractedReps, -newEase0, newEase1, newEase2, newEase3, newEase4, youngEase0, youngEase1, -youngEase2, youngEase3, youngEase4, matureEase0, matureEase1, matureEase2, -matureEase3, matureEase4) values (:type, :day, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)""", self.__dict__) - self.id = s.scalar( - "select id from stats where type = :type and day = :day", - type=type, day=day) - - def toDB(self, s): - assert self.id - s.execute("""update stats set -type=:type, -day=:day, -reps=:reps, -averageTime=:averageTime, -reviewTime=:reviewTime, -newEase0=:newEase0, -newEase1=:newEase1, -newEase2=:newEase2, -newEase3=:newEase3, -newEase4=:newEase4, -youngEase0=:youngEase0, -youngEase1=:youngEase1, -youngEase2=:youngEase2, -youngEase3=:youngEase3, -youngEase4=:youngEase4, -matureEase0=:matureEase0, -matureEase1=:matureEase1, -matureEase2=:matureEase2, -matureEase3=:matureEase3, -matureEase4=:matureEase4 -where id = :id""", self.__dict__) - -mapper(Stats, statsTable) - -def genToday(deck): - return datetime.datetime.utcfromtimestamp( - time.time() - deck.utcOffset).date() - -def updateAllStats(s, gs, ds, card, ease, oldState): - "Update global and daily statistics." - updateStats(s, gs, card, ease, oldState) - updateStats(s, ds, card, ease, oldState) - -def updateStats(s, stats, card, ease, oldState): - stats.reps += 1 - delay = card.totalTime() - if delay >= 60: - stats.reviewTime += 60 - else: - stats.reviewTime += delay - stats.averageTime = ( - stats.reviewTime / float(stats.reps)) - # update eases - attr = oldState + "Ease%d" % ease - setattr(stats, attr, getattr(stats, attr) + 1) - stats.toDB(s) - -def globalStats(deck): - s = deck.s - type = STATS_LIFE - today = genToday(deck) - id = s.scalar("select id from stats where type = :type", - type=type) - stats = Stats() - if id: - stats.fromDB(s, id) - return stats - else: - stats.create(s, type, today) - stats.type = type - return stats - -def dailyStats(deck): - s = deck.s - type = STATS_DAY - today = genToday(deck) - id = s.scalar("select id from stats where type = :type and day = :day", - type=type, day=today) - stats = Stats() - if id: - stats.fromDB(s, id) - return stats - else: - stats.create(s, type, today) - return stats - -def summarizeStats(stats, pre=""): - "Generate percentages and total counts for STATS. Optionally prefix." - cardTypes = ("new", "young", "mature") - h = {} - # total counts - ############### - for type in cardTypes: - # total yes/no for type, eg. gNewYes - h[pre + type.capitalize() + "No"] = (getattr(stats, type + "Ease0") + - getattr(stats, type + "Ease1")) - h[pre + type.capitalize() + "Yes"] = (getattr(stats, type + "Ease2") + - getattr(stats, type + "Ease3") + - getattr(stats, type + "Ease4")) - # total for type, eg. gNewTotal - h[pre + type.capitalize() + "Total"] = ( - h[pre + type.capitalize() + "No"] + - h[pre + type.capitalize() + "Yes"]) - # total yes/no, eg. gYesTotal - for answer in ("yes", "no"): - num = 0 - for type in cardTypes: - num += h[pre + type.capitalize() + answer.capitalize()] - h[pre + answer.capitalize() + "Total"] = num - # total over all, eg. gTotal - num = 0 - for type in cardTypes: - num += h[pre + type.capitalize() + "Total"] - h[pre + "Total"] = num - # percentages - ############## - for type in cardTypes: - # total yes/no % by type, eg. gNewYes% - for answer in ("yes", "no"): - setPercentage(h, pre + type.capitalize() + answer.capitalize(), - pre + type.capitalize()) - for answer in ("yes", "no"): - # total yes/no, eg. gYesTotal% - setPercentage(h, pre + answer.capitalize() + "Total", pre) - h[pre + 'AverageTime'] = stats.averageTime - h[pre + 'ReviewTime'] = stats.reviewTime - return h - -def setPercentage(h, a, b): - try: - h[a + "%"] = (h[a] / float(h[b + "Total"])) * 100 - except ZeroDivisionError: - h[a + "%"] = 0 - -def getStats(s, gs, ds): - "Return a handy dictionary exposing a number of internal stats." - h = {} - h.update(summarizeStats(gs, "g")) - h.update(summarizeStats(ds, "d")) - return h - # Card stats ########################################################################## @@ -295,7 +62,7 @@ class CardStats(object): s = anki.utils.fmtTimeSpan(time.time() - tm) return _("%s ago") % s -# Deck stats (specific to the 'sched' scheduler) +# Deck stats ########################################################################## class DeckStats(object): @@ -355,7 +122,7 @@ class DeckStats(object): 'partOf' : nYes, 'totalSum' : nAll } + "

") # average pending time - existing = d.cardCount - d.newCountToday + existing = d.cardCount - d.newCount def tr(a, b): return "%s%s" % (a, b) def repsPerDay(reps,days): diff --git a/anki/sync.py b/anki/sync.py index c072d4fc4..43133b541 100644 --- a/anki/sync.py +++ b/anki/sync.py @@ -11,9 +11,7 @@ from anki.errors import * from anki.models import Model, FieldModel, CardModel from anki.facts import Fact, Field from anki.cards import Card -from anki.stats import Stats, globalStats from anki.history import CardHistoryEntry -from anki.stats import globalStats from anki.utils import ids2str, hexifyID, checksum from anki.media import mediaFiles from anki.lang import _ @@ -114,7 +112,6 @@ class SyncTools(object): def genPayload(self, summaries): (lsum, rsum) = summaries - self.preSyncRefresh() payload = {} # first, handle models, facts and cards for key in KEYS: @@ -125,7 +122,6 @@ class SyncTools(object): self.deleteObjsFromKey(diff[3], key) # handle the remainder if self.localTime > self.remoteTime: - payload['stats'] = self.bundleStats() payload['history'] = self.bundleHistory() payload['sources'] = self.bundleSources() # finally, set new lastSync and bundle the deck info @@ -134,7 +130,6 @@ class SyncTools(object): def applyPayload(self, payload): reply = {} - self.preSyncRefresh() # model, facts and cards for key in KEYS: k = 'added-' + key @@ -146,14 +141,12 @@ class SyncTools(object): self.deleteObjsFromKey(payload['deleted-' + key], key) # send back deck-related stuff if it wasn't sent to us if not 'deck' in payload: - reply['stats'] = self.bundleStats() reply['history'] = self.bundleHistory() reply['sources'] = self.bundleSources() # finally, set new lastSync and bundle the deck info reply['deck'] = self.bundleDeck() else: self.updateDeck(payload['deck']) - self.updateStats(payload['stats']) self.updateHistory(payload['history']) if 'sources' in payload: self.updateSources(payload['sources']) @@ -172,7 +165,6 @@ class SyncTools(object): # deck if 'deck' in reply: self.updateDeck(reply['deck']) - self.updateStats(reply['stats']) self.updateHistory(reply['history']) if 'sources' in reply: self.updateSources(reply['sources']) @@ -194,10 +186,6 @@ class SyncTools(object): self.deck.s.refresh(self.deck) self.deck.currentModel - def preSyncRefresh(self): - # ensure global stats are available (queue may not be built) - self.deck._globalStats = globalStats(self.deck) - # Summaries ########################################################################## @@ -553,7 +541,7 @@ values def deleteCards(self, ids): self.deck.deleteCards(ids) - # Deck/stats/history + # Deck/history ########################################################################## def bundleDeck(self): @@ -595,42 +583,6 @@ insert or replace into deckVars del deck['meta'] self.applyDict(self.deck, deck) - def bundleStats(self): - def bundleStat(stat): - s = self.dictFromObj(stat) - s['day'] = s['day'].toordinal() - del s['id'] - return s - lastDay = date.fromtimestamp(max(0, self.deck.lastSync - 60*60*24)) - ids = self.deck.s.column0( - "select id from stats where type = 1 and day >= :day", day=lastDay) - stat = Stats() - def statFromId(id): - stat.fromDB(self.deck.s, id) - return stat - stats = { - 'global': bundleStat(self.deck._globalStats), - 'daily': [bundleStat(statFromId(id)) for id in ids], - } - return stats - - def updateStats(self, stats): - stats['global']['day'] = date.fromordinal(stats['global']['day']) - self.applyDict(self.deck._globalStats, stats['global']) - self.deck._globalStats.toDB(self.deck.s) - for record in stats['daily']: - record['day'] = date.fromordinal(record['day']) - stat = Stats() - id = self.deck.s.scalar("select id from stats where " - "type = :type and day = :day", - type=1, day=record['day']) - if id: - stat.fromDB(self.deck.s, id) - else: - stat.create(self.deck.s, 1, record['day']) - self.applyDict(stat, record) - stat.toDB(self.deck.s) - def bundleHistory(self): return self.realLists(self.deck.s.all(""" select cardId, time, lastInterval, nextInterval, ease, delay, @@ -886,10 +838,6 @@ and cards.id in %s""" % ids2str([c[0] for c in cards]))) ls=self.deck.lastSync) > 1000: return True lastDay = date.fromtimestamp(max(0, self.deck.lastSync - 60*60*24)) - if self.deck.s.scalar( - "select count() from stats where day >= :day", - day=lastDay) > 100: - return True return False def prepareFullSync(self): diff --git a/anki/upgrade.py b/anki/upgrade.py index 0ef88b840..be9ef60dc 100644 --- a/anki/upgrade.py +++ b/anki/upgrade.py @@ -2,7 +2,7 @@ # Copyright: Damien Elmes # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html -DECK_VERSION = 72 +DECK_VERSION = 73 from anki.lang import _ from anki.media import rebuildMediaDir @@ -45,9 +45,6 @@ create index if not exists ix_cards_priority on cards # card spacing deck.s.statement(""" create index if not exists ix_cards_factId on cards (factId)""") - # stats - deck.s.statement(""" -create index if not exists ix_stats_typeDay on stats (type, day)""") # fields deck.s.statement(""" create index if not exists ix_fields_factId on fields (factId)""") @@ -230,10 +227,18 @@ this message. (ERR-0101)""") % { deck.version = 71 deck.s.commit() if deck.version < 72: - # remove the expensive value cache + # this was only used for calculating average factor deck.s.statement("drop index if exists ix_cards_factor") deck.version = 72 deck.s.commit() + if deck.version < 73: + # remove stats, as it's all in the revlog now + deck.s.statement("drop index if exists ix_stats_typeDay") + deck.s.statement("drop table if exists stats") + deck.version = 73 + deck.s.commit() + + # executing a pragma here is very slow on large decks, so we store # our own record if not deck.getInt("pageSize") == 4096: diff --git a/tests/test_sync.py b/tests/test_sync.py index 43271800d..2eaa3519f 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -9,7 +9,6 @@ from anki.db import * from anki.stdmodels import BasicModel from anki.sync import SyncClient, SyncServer, HttpSyncServer, HttpSyncServerProxy from anki.sync import copyLocalMedia -from anki.stats import dailyStats, globalStats from anki.facts import Fact from anki.cards import Card from anki.models import FieldModel @@ -104,8 +103,6 @@ def test_localsync_deck(): c = deck1.getCard() deck1.answerCard(c, 4) client.sync() - assert dailyStats(deck2).reps == 1 - assert globalStats(deck2).reps == 1 assert deck2.s.scalar("select count(*) from reviewHistory") == 1 # make sure meta data is synced deck1.setVar("foo", 1)