mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 22:42:25 -04:00
support 4am rollover
This commit is contained in:
parent
23379cd600
commit
e25feed0af
4 changed files with 46 additions and 20 deletions
38
anki/deck.py
38
anki/deck.py
|
@ -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())
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue