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

View file

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

View file

@ -30,7 +30,7 @@ from anki.facts import Fact, Field
from anki.cards import Card from anki.cards import Card
from anki.stats import Stats, globalStats from anki.stats import Stats, globalStats
from anki.history import CardHistoryEntry from anki.history import CardHistoryEntry
from anki.stats import dailyStats, globalStats from anki.stats import globalStats
from anki.media import checksum from anki.media import checksum
from anki.utils import ids2str from anki.utils import ids2str
from anki.lang import _ from anki.lang import _
@ -152,7 +152,7 @@ class SyncTools(object):
def preSyncRefresh(self): def preSyncRefresh(self):
# ensure global stats are available (queue may not be built) # 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): def payloadChanges(self, payload):
h = { h = {
@ -538,7 +538,7 @@ values
s['day'] = s['day'].toordinal() s['day'] = s['day'].toordinal()
del s['id'] del s['id']
return s return s
lastDay = date.fromtimestamp(self.deck.lastSync) lastDay = date.fromtimestamp(self.deck.lastSync - 60*60*24)
ids = self.deck.s.column0( ids = self.deck.s.column0(
"select id from stats where type = 1 and day >= :day", day=lastDay) "select id from stats where type = 1 and day >= :day", day=lastDay)
stat = Stats() stat = Stats()

View file

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