mirror of
https://github.com/ankitects/anki.git
synced 2025-11-07 21:27:14 -05:00
We did away with the stats table because it's impossible to merge it, so the revlog is canonical now. But we also want a cheap way to display to the user how much time or how many cards they've done over the day, even if their study is split into multiple sessions. We were already storing the new cards of a day in the top level groups, so we just expand that out to log the other info too. In the event of a user studying in two places on the same day without syncing, the counts will not be accurate as they can't be merged without consulting the revlog, which we want to avoid for performance reasons. But the graphs and stats do not use the groups for reporting, so the inaccurate counts are only temporary. Might need to mention this in an FAQ. Also, since groups are cheap to fetch now, cards now automatically limit timeTaken() to the group limit, instead of relying on the calling code to do so.
146 lines
4.1 KiB
Python
146 lines
4.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
import time
|
|
from anki.utils import intTime, hexifyID, timestampID
|
|
|
|
# Cards
|
|
##########################################################################
|
|
|
|
# Type: 0=new, 1=learning, 2=due
|
|
# Queue: same as above, and:
|
|
# -1=suspended, -2=user buried, -3=sched buried
|
|
# Due is used differently for different queues.
|
|
# - new queue: fact id or random int
|
|
# - rev queue: integer day
|
|
# - lrn queue: integer timestamp
|
|
|
|
class Card(object):
|
|
|
|
def __init__(self, deck, id=None):
|
|
self.deck = deck
|
|
self.timerStarted = None
|
|
self._qa = None
|
|
self._rd = None
|
|
if id:
|
|
self.id = id
|
|
self.load()
|
|
else:
|
|
# to flush, set fid, ord, and due
|
|
self.id = timestampID(deck.db, "cards")
|
|
self.gid = 1
|
|
self.crt = intTime()
|
|
self.type = 0
|
|
self.queue = 0
|
|
self.ivl = 0
|
|
self.factor = 0
|
|
self.reps = 0
|
|
self.lapses = 0
|
|
self.grade = 0
|
|
self.cycles = 0
|
|
self.edue = 0
|
|
self.data = ""
|
|
|
|
def load(self):
|
|
(self.id,
|
|
self.fid,
|
|
self.gid,
|
|
self.ord,
|
|
self.mod,
|
|
self.type,
|
|
self.queue,
|
|
self.due,
|
|
self.ivl,
|
|
self.factor,
|
|
self.reps,
|
|
self.lapses,
|
|
self.grade,
|
|
self.cycles,
|
|
self.edue,
|
|
self.data) = self.deck.db.first(
|
|
"select * from cards where id = ?", self.id)
|
|
self._qa = None
|
|
self._rd = None
|
|
|
|
def flush(self):
|
|
self.mod = intTime()
|
|
self.deck.db.execute(
|
|
"""
|
|
insert or replace into cards values
|
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
self.id,
|
|
self.fid,
|
|
self.gid,
|
|
self.ord,
|
|
self.mod,
|
|
self.type,
|
|
self.queue,
|
|
self.due,
|
|
self.ivl,
|
|
self.factor,
|
|
self.reps,
|
|
self.lapses,
|
|
self.grade,
|
|
self.cycles,
|
|
self.edue,
|
|
self.data)
|
|
|
|
def flushSched(self):
|
|
self.mod = intTime()
|
|
self.deck.db.execute(
|
|
"""update cards set
|
|
mod=?, type=?, queue=?, due=?, ivl=?, factor=?, reps=?,
|
|
lapses=?, grade=?, cycles=?, edue=? where id = ?""",
|
|
self.mod, self.type, self.queue, self.due, self.ivl,
|
|
self.factor, self.reps, self.lapses,
|
|
self.grade, self.cycles, self.edue, self.id)
|
|
|
|
def q(self, classes="q", reload=False):
|
|
return self._withClass(self._getQA(reload)['q'], classes)
|
|
|
|
def a(self, classes="a"):
|
|
return self._withClass(self._getQA()['a'], classes)
|
|
|
|
def _getQA(self, reload=False):
|
|
if not self._qa or reload:
|
|
f = self.fact(); m = self.model()
|
|
data = [self.id, f.id, m['id'], self.gid, self.ord, f.stringTags(),
|
|
f.joinedFields()]
|
|
self._qa = self.deck._renderQA(data)
|
|
return self._qa
|
|
|
|
def _withClass(self, txt, extra):
|
|
return '<div class="%s %s">%s</div>' % (self.cssClass(), extra, txt)
|
|
|
|
def _reviewData(self, reload=False):
|
|
"Fetch the model and fact."
|
|
if not self._rd or reload:
|
|
f = self.deck.getFact(self.fid)
|
|
m = self.deck.models.get(f.mid)
|
|
self._rd = [f, m]
|
|
return self._rd
|
|
|
|
def fact(self):
|
|
return self._reviewData()[0]
|
|
|
|
def model(self, reload=False):
|
|
return self._reviewData()[1]
|
|
|
|
def groupConf(self):
|
|
return self.deck.groups.conf(self.gid)
|
|
|
|
def template(self):
|
|
return self._reviewData()[1]['tmpls'][self.ord]
|
|
|
|
def cssClass(self):
|
|
return "cm%s-%s" % (hexifyID(self.model()['id']),
|
|
hexifyID(self.template()['ord']))
|
|
|
|
def startTimer(self):
|
|
self.timerStarted = time.time()
|
|
|
|
def timeTaken(self):
|
|
"Time taken to answer card, in integer MS."
|
|
total = int((time.time() - self.timerStarted)*1000)
|
|
return min(total, self.groupConf()['maxTaken']*1000)
|