support 4am rollover

This commit is contained in:
Damien Elmes 2008-10-12 04:02:33 +09:00
parent 23379cd600
commit e25feed0af
4 changed files with 46 additions and 20 deletions

View file

@ -9,7 +9,6 @@ The Deck
__docformat__ = 'restructuredtext'
import tempfile, time, os, random, sys, re, stat, shutil, types
from heapq import heapify, heappush, heappop
from anki.db import *
from anki.lang import _
@ -18,7 +17,7 @@ from anki.stdmodels import BasicModel
from anki.utils import parseTags, tidyHTML, genID, ids2str
from anki.history import CardHistoryEntry
from anki.models import Model, CardModel
from anki.stats import dailyStats, globalStats
from anki.stats import dailyStats, globalStats, genToday
# ensure all the metadata in other files is loaded before proceeding
import anki.models, anki.facts, anki.cards, anki.stats
@ -42,7 +41,7 @@ decksTable = Table(
Column('created', Float, nullable=False, default=time.time),
Column('modified', Float, nullable=False, default=time.time),
Column('description', UnicodeText, nullable=False, default=u""),
Column('version', Integer, nullable=False, default=10),
Column('version', Integer, nullable=False, default=11),
Column('currentModelId', Integer, ForeignKey("models.id")),
# syncing
Column('syncName', UnicodeText),
@ -77,7 +76,9 @@ decksTable = Table(
Column('newCardsPerDay', Integer, nullable=False, default=20),
# currently unused
Column('sessionRepLimit', Integer, nullable=False, default=100),
Column('sessionTimeLimit', Integer, nullable=False, default=1800))
Column('sessionTimeLimit', Integer, nullable=False, default=1800),
# stats offset
Column('utcOffset', Float, nullable=False, default=0))
class Deck(object):
"Top-level object. Manages facts, cards and scheduling information."
@ -220,6 +221,7 @@ Caller is responsible for ensuring cards are not spaced."""
##########################################################################
def answerCard(self, card, ease):
self.checkDailyStats()
now = time.time()
oldState = self.cardState(card)
lastDelay = max(0, (time.time() - card.due) / 86400.0)
@ -303,8 +305,8 @@ where isDue = 1""")
self.updateRelativeDelays()
self.markExpiredCardsDue()
# cache global/daily stats
self._globalStats = globalStats(self.s)
self._dailyStats = dailyStats(self.s)
self._globalStats = globalStats(self)
self._dailyStats = dailyStats(self)
# invalid card count
self._countsDirty = True
# determine new card distribution
@ -322,6 +324,11 @@ where isDue = 1""")
"select avg(factor) from cards where reps > 0")
or Deck.initialFactor)
def checkDailyStats(self):
# check if the day has rolled over
if genToday(self) != self._dailyStats.day:
self._dailyStats = dailyStats(self)
# Interval management
##########################################################################
@ -1487,15 +1494,19 @@ class DeckStorage(object):
if create:
deck = DeckStorage._init(s)
else:
if s.scalar("select version from decks limit 1") < 5:
# add missing deck field
ver = s.scalar("select version from decks limit 1")
if ver < 5:
# add missing deck fields
s.execute("""
alter table decks add column newCardsPerDay integer not null default 20""")
if s.scalar("select version from decks limit 1") < 6:
if ver < 6:
s.execute("""
alter table decks add column sessionRepLimit integer not null default 100""")
s.execute("""
alter table decks add column sessionTimeLimit integer not null default 1800""")
if ver < 11:
s.execute("""
alter table decks add column utcOffset numeric(10, 2) not null default 0""")
deck = s.query(Deck).get(1)
# attach db vars
deck.path = path
@ -1793,9 +1804,18 @@ insert into media values (
alter table models add column source integer not null default 0""")
deck.version = 10
deck.s.commit()
if deck.version < 11:
DeckStorage._setUTCOffset(deck)
deck.version = 11
deck.s.commit()
return deck
_upgradeDeck = staticmethod(_upgradeDeck)
def _setUTCOffset(deck):
# 4am
deck.utcOffset = time.timezone + 60*60*4
_setUTCOffset = staticmethod(_setUTCOffset)
def backup(modified, path):
# need a non-unicode path
path = path.encode(sys.getfilesystemencoding())

View file

@ -25,7 +25,7 @@ statsTable = Table(
'stats', metadata,
Column('id', Integer, primary_key=True),
Column('type', Integer, nullable=False),
Column('day', Date, nullable=False, default=date.today),
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),
@ -50,7 +50,7 @@ statsTable = Table(
class Stats(object):
def __init__(self):
self.day = date.today()
self.day = None
self.reps = 0
self.averageTime = 0
self.reviewTime = 0
@ -139,6 +139,10 @@ 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)
@ -159,9 +163,10 @@ def updateStats(s, stats, card, ease, oldState):
setattr(stats, attr, getattr(stats, attr) + 1)
stats.toDB(s)
def globalStats(s):
def globalStats(deck):
s = deck.s
type = STATS_LIFE
today = date.today()
today = genToday(deck)
id = s.scalar("select id from stats where type = :type",
type=type)
stats = Stats()
@ -173,9 +178,10 @@ def globalStats(s):
stats.type = type
return stats
def dailyStats(s):
def dailyStats(deck):
s = deck.s
type = STATS_DAY
today = date.today()
today = genToday(deck)
id = s.scalar("select id from stats where type = :type and day = :day",
type=type, day=today)
stats = Stats()

View file

@ -30,7 +30,7 @@ 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 dailyStats, globalStats
from anki.stats import globalStats
from anki.media import checksum
from anki.utils import ids2str
from anki.lang import _
@ -152,7 +152,7 @@ class SyncTools(object):
def preSyncRefresh(self):
# ensure global stats are available (queue may not be built)
self.deck._globalStats = globalStats(self.deck.s)
self.deck._globalStats = globalStats(self.deck)
def payloadChanges(self, payload):
h = {
@ -538,7 +538,7 @@ values
s['day'] = s['day'].toordinal()
del s['id']
return s
lastDay = date.fromtimestamp(self.deck.lastSync)
lastDay = date.fromtimestamp(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()

View file

@ -101,8 +101,8 @@ def test_localsync_deck():
c = deck1.getCard()
deck1.answerCard(c, 4)
client.sync()
assert dailyStats(deck2.s).reps == 1
assert globalStats(deck2.s).reps == 1
assert dailyStats(deck2).reps == 1
assert globalStats(deck2).reps == 1
assert deck2.s.scalar("select count(id) from reviewHistory") == 1
@nose.with_setup(setup_local, teardown)