def downloadRemote(deck):
    mdir = deck.mediaDir(create=True)
    refs = {}
    deck.startProgress()
    for (question, answer) in deck.s.all(
        "select question, answer from cards"):
        for txt in (question, answer):
            for f in mediaFiles(txt, remote=True):
                refs[f] = True

    tmpdir = tempfile.mkdtemp(prefix="oldanki")
    failed = []
    passed = []
    for c, link in enumerate(refs.keys()):
        try:
            path = os.path.join(tmpdir, os.path.basename(link))
            url = urllib.request.urlopen(link)
            open(path, "wb").write(url.read())
            newpath = copyToMedia(deck, path)
            passed.append([link, newpath])
        except:
            failed.append(link)
        deck.updateProgress(label=_("Download %d...") % c)
    for (url, name) in passed:
        deck.s.statement(
            "update fields set value = replace(value, :url, :name)",
            url=url, name=name)
        deck.updateProgress(label=_("Updating references..."))
    deck.updateProgress(label=_("Updating cards..."))
    # rebuild entire q/a cache
    for m in deck.models:
        deck.updateCardsFromModel(m, dirty=True)
    deck.finishProgress()
    deck.flushMod()
    return (passed, failed)
Exemple #2
0
def downloadRemote(deck):
    mdir = deck.mediaDir(create=True)
    refs = {}
    deck.startProgress()
    for (question, answer) in deck.s.all(
        "select question, answer from cards"):
        for txt in (question, answer):
            for f in mediaFiles(txt, remote=True):
                refs[f] = True

    tmpdir = tempfile.mkdtemp(prefix="oldanki")
    failed = []
    passed = []
    for c, link in enumerate(refs.keys()):
        try:
            path = os.path.join(tmpdir, os.path.basename(link))
            url = urllib2.urlopen(link)
            open(path, "wb").write(url.read())
            newpath = copyToMedia(deck, path)
            passed.append([link, newpath])
        except:
            failed.append(link)
        deck.updateProgress(label=_("Download %d...") % c)
    for (url, name) in passed:
        deck.s.statement(
            "update fields set value = replace(value, :url, :name)",
            url=url, name=name)
        deck.updateProgress(label=_("Updating references..."))
    deck.updateProgress(label=_("Updating cards..."))
    # rebuild entire q/a cache
    for m in deck.models:
        deck.updateCardsFromModel(m, dirty=True)
    deck.finishProgress()
    deck.flushMod()
    return (passed, failed)
Exemple #3
0
 def errmsg(type):
     msg = _("Error executing %s.\n") % type
     try:
         log = open(os.path.join(tmpdir, "latex_log.txt")).read()
         msg += "<small><pre>" + cgi.escape(log) + "</pre></small>"
     except:
         msg += _("Have you installed latex and dvipng?")
         pass
     return msg
Exemple #4
0
 def errmsg(type):
     msg = _("Error executing %s.\n") % type
     try:
         log = open(os.path.join(tmpdir, "latex_log.txt")).read()
         msg += "<small><pre>" + cgi.escape(log) + "</pre></small>"
     except:
         msg += _("Have you installed latex and dvipng?")
         pass
     return msg
Exemple #5
0
 def payloadChanges(self, payload):
     h = {
         "lf": len(payload["added-facts"]["facts"]),
         "rf": len(payload["missing-facts"]),
         "lc": len(payload["added-cards"]),
         "rc": len(payload["missing-cards"]),
         "lm": len(payload["added-models"]),
         "rm": len(payload["missing-models"]),
     }
     if self.localTime > self.remoteTime:
         h["ls"] = _("all")
         h["rs"] = 0
     else:
         h["ls"] = 0
         h["rs"] = _("all")
     return h
Exemple #6
0
    def doExport(self, file):
        ids = self.cardIds()
        strids = ids2str(ids)
        self.deck.startProgress((len(ids) + 1) / 50)
        self.deck.updateProgress(_("Exporting..."))
        cards = self.deck.s.all("""
select cards.question, cards.answer, cards.id from cards
where cards.id in %s
order by cards.created""" % strids)
        self.deck.updateProgress()
        if self.includeTags:
            self.cardTags = dict(self.deck.s.all("""
select cards.id, facts.tags from cards, facts
where cards.factId = facts.id
and cards.id in %s
order by cards.created""" % strids))
        out = u"\n".join(["%s\t%s%s" % (
            self.escapeText(c[0], removeFields=True),
            self.escapeText(c[1], removeFields=True),
            self.tags(c[2]))
                          for c in cards])
        if out:
            out += "\n"
        file.write(out.encode("utf-8"))
        self.deck.finishProgress()
Exemple #7
0
def downloadMissing(deck):
    urlbase = deck.getVar("mediaURL")
    if not urlbase:
        return None
    mdir = deck.mediaDir(create=True)
    deck.startProgress()
    missing = 0
    grabbed = 0
    for c, (f, sum) in enumerate(deck.s.all(
        "select filename, originalPath from media")):
        path = os.path.join(mdir, f)
        if not os.path.exists(path):
            try:
                rpath = urlbase + f
                url = urllib2.urlopen(rpath)
                open(f, "wb").write(url.read())
                grabbed += 1
            except:
                if sum:
                    # the file is supposed to exist
                    deck.finishProgress()
                    return (False, rpath)
                else:
                    # ignore and keep going
                    missing += 1
        deck.updateProgress(label=_("File %d...") % (grabbed+missing))
    deck.finishProgress()
    return (True, grabbed, missing)
Exemple #8
0
 def payloadChanges(self, payload):
     h = {
         'lf': len(payload['added-facts']['facts']),
         'rf': len(payload['missing-facts']),
         'lc': len(payload['added-cards']),
         'rc': len(payload['missing-cards']),
         'lm': len(payload['added-models']),
         'rm': len(payload['missing-models']),
     }
     if self.localTime > self.remoteTime:
         h['ls'] = _('all')
         h['rs'] = 0
     else:
         h['ls'] = 0
         h['rs'] = _('all')
     return h
def downloadMissing(deck):
    urlbase = deck.getVar("mediaURL")
    if not urlbase:
        return None
    mdir = deck.mediaDir(create=True)
    deck.startProgress()
    missing = 0
    grabbed = 0
    for c, (f, sum) in enumerate(deck.s.all(
        "select filename, originalPath from media")):
        path = os.path.join(mdir, f)
        if not os.path.exists(path):
            try:
                rpath = urlbase + f
                url = urllib.request.urlopen(rpath)
                open(f, "wb").write(url.read())
                grabbed += 1
            except:
                if sum:
                    # the file is supposed to exist
                    deck.finishProgress()
                    return (False, rpath)
                else:
                    # ignore and keep going
                    missing += 1
        deck.updateProgress(label=_("File %d...") % (grabbed+missing))
    deck.finishProgress()
    return (True, grabbed, missing)
Exemple #10
0
    def doExport(self, file):
        ids = self.cardIds()
        strids = ids2str(ids)
        self.deck.startProgress((len(ids) + 1) / 50)
        self.deck.updateProgress(_("Exporting..."))
        cards = self.deck.s.all("""
select cards.question, cards.answer, cards.id from cards
where cards.id in %s
order by cards.created""" % strids)
        self.deck.updateProgress()
        if self.includeTags:
            self.cardTags = dict(
                self.deck.s.all("""
select cards.id, facts.tags from cards, facts
where cards.factId = facts.id
and cards.id in %s
order by cards.created""" % strids))
        out = u"\n".join([
            "%s\t%s%s" %
            (self.escapeText(c[0], removeFields=True),
             self.escapeText(c[1], removeFields=True), self.tags(c[2]))
            for c in cards
        ])
        if out:
            out += "\n"
        file.write(out.encode("utf-8"))
        self.deck.finishProgress()
Exemple #11
0
    def doExport(self, file):
        cardIds = self.cardIds()
        self.deck.startProgress()
        self.deck.updateProgress(_("Exporting..."))
        facts = self.deck.s.all("""
select factId, value, facts.created from facts, fields
where
facts.id in
(select distinct factId from cards
where cards.id in %s)
and facts.id = fields.factId
order by factId, ordinal""" % ids2str(cardIds))
        txt = ""
        self.deck.updateProgress()
        if self.includeTags:
            self.factTags = dict(
                self.deck.s.all("select id, tags from facts where id in %s" %
                                ids2str([fact[0] for fact in facts])))
        groups = itertools.groupby(facts, itemgetter(0))
        groups = [[x for x in y[1]] for y in groups]
        groups = [(group[0][2],
                   "\t".join([self.escapeText(x[1])
                              for x in group]) + self.tags(group[0][0]))
                  for group in groups]
        self.deck.updateProgress()
        groups.sort(key=itemgetter(0))
        out = [ret[1] for ret in groups]
        self.count = len(out)
        out = "\n".join(out)
        file.write(out.encode("utf-8"))
        self.deck.finishProgress()
Exemple #12
0
    def doExport(self, file):
        cardIds = self.cardIds()
        self.deck.startProgress()
        self.deck.updateProgress(_("Exporting..."))
        facts = self.deck.s.all("""
select factId, value, facts.created from facts, fields
where
facts.id in
(select distinct factId from cards
where cards.id in %s)
and facts.id = fields.factId
order by factId, ordinal""" % ids2str(cardIds))
        txt = ""
        self.deck.updateProgress()
        if self.includeTags:
            self.factTags = dict(self.deck.s.all(
                "select id, tags from facts where id in %s" %
                ids2str([fact[0] for fact in facts])))
        groups = itertools.groupby(facts, itemgetter(0))
        groups = [[x for x in y[1]] for y in groups]
        groups = [(group[0][2],
                   "\t".join([self.escapeText(x[1]) for x in group]) +
                   self.tags(group[0][0]))
                  for group in groups]
        self.deck.updateProgress()
        groups.sort(key=itemgetter(0))
        out = [ret[1] for ret in groups]
        self.count = len(out)
        out = "\n".join(out)
        file.write(out.encode("utf-8"))
        self.deck.finishProgress()
Exemple #13
0
def RecoveryModel():
    m = Model(_('Recovery'))
    m.addFieldModel(FieldModel(u'Question', False, False))
    m.addFieldModel(FieldModel(u'Answer', False, False))
    m.addCardModel(CardModel(u'Single', u'{{{Question}}}', u'{{{Answer}}}'))
    m.tags = u"Recovery"
    return m
def BasicModel():
    m = Model(_('Basic'))
    m.addFieldModel(FieldModel('Front', True, True))
    m.addFieldModel(FieldModel('Back', False, False))
    m.addCardModel(CardModel('Forward', '%(Front)s', '%(Back)s'))
    m.addCardModel(CardModel('Reverse', '%(Back)s', '%(Front)s', active=False))
    m.tags = "Basic"
    return m
Exemple #15
0
def BasicModel():
    m = Model(_('Basic'))
    m.addFieldModel(FieldModel(u'Front', True, True))
    m.addFieldModel(FieldModel(u'Back', False, False))
    m.addCardModel(CardModel(u'Forward', u'%(Front)s', u'%(Back)s'))
    m.addCardModel(CardModel(u'Reverse', u'%(Back)s', u'%(Front)s',
                             active=False))
    m.tags = u"Basic"
    return m
Exemple #16
0
    def payloadChangeReport(self, payload):
        p = self.payloadChanges(payload)
        return _("""\
<table>
<tr><td><b>Added/Changed&nbsp;&nbsp;&nbsp;</b></td>
<td><b>Here&nbsp;&nbsp;&nbsp;</b></td><td><b>Server</b></td></tr>
<tr><td>Cards</td><td>%(lc)d</td><td>%(rc)d</td></tr>
<tr><td>Facts</td><td>%(lf)d</td><td>%(rf)d</td></tr>
<tr><td>Models</td><td>%(lm)d</td><td>%(rm)d</td></tr>
<tr><td>Stats</td><td>%(ls)s</td><td>%(rs)s</td></tr>
</table>""") % p
Exemple #17
0
    def payloadChangeReport(self, payload):
        p = self.payloadChanges(payload)
        return (
            _(
                """\
<table>
<tr><td><b>Added/Changed&nbsp;&nbsp;&nbsp;</b></td>
<td><b>Here&nbsp;&nbsp;&nbsp;</b></td><td><b>Server</b></td></tr>
<tr><td>Cards</td><td>%(lc)d</td><td>%(rc)d</td></tr>
<tr><td>Facts</td><td>%(lf)d</td><td>%(rf)d</td></tr>
<tr><td>Models</td><td>%(lm)d</td><td>%(rm)d</td></tr>
<tr><td>Stats</td><td>%(ls)s</td><td>%(rs)s</td></tr>
</table>"""
            )
            % p
        )
Exemple #18
0
class TextCardExporter(Exporter):

    key = _("Text files (*.txt)")
    ext = ".txt"

    def __init__(self, deck):
        Exporter.__init__(self, deck)
        self.includeTags = False

    def doExport(self, file):
        ids = self.cardIds()
        strids = ids2str(ids)
        self.deck.startProgress((len(ids) + 1) / 50)
        self.deck.updateProgress(_("Exporting..."))
        cards = self.deck.s.all("""
select cards.question, cards.answer, cards.id from cards
where cards.id in %s
order by cards.created""" % strids)
        self.deck.updateProgress()
        if self.includeTags:
            self.cardTags = dict(
                self.deck.s.all("""
select cards.id, facts.tags from cards, facts
where cards.factId = facts.id
and cards.id in %s
order by cards.created""" % strids))
        out = u"\n".join([
            "%s\t%s%s" %
            (self.escapeText(c[0], removeFields=True),
             self.escapeText(c[1], removeFields=True), self.tags(c[2]))
            for c in cards
        ])
        if out:
            out += "\n"
        file.write(out.encode("utf-8"))
        self.deck.finishProgress()

    def tags(self, id):
        if self.includeTags:
            return "\t" + ", ".join(parseTags(self.cardTags[id]))
        return ""
Exemple #19
0
def alignmentLabels():
    return {
        0: _("Center"),
        1: _("Left"),
        2: _("Right"),
        }
Exemple #20
0
def exporters():
    return (
        (_("Anki Deck (*.oldanki)"), AnkiExporter),
        (_("Cards in tab-separated text file (*.txt)"), TextCardExporter),
        (_("Facts in tab-separated text file (*.txt)"), TextFactExporter))
    def report(self):
        "Return an HTML string with a report."
        fmtPerc = oldanki.utils.fmtPercentage
        fmtFloat = oldanki.utils.fmtFloat
        if self.deck.isEmpty():
            return _("Please add some cards first.") + "<p/>"
        d = self.deck
        html = "<h1>" + _("Deck Statistics") + "</h1>"
        html += _("Deck created: <b>%s</b> ago<br>") % self.createdTimeStr()
        total = d.cardCount
        new = d.newCountAll()
        young = d.youngCardCount()
        old = d.matureCardCount()
        newP = new / float(total) * 100
        youngP = young / float(total) * 100
        oldP = old / float(total) * 100
        stats = d.getStats()
        (stats["new"], stats["newP"]) = (new, newP)
        (stats["old"], stats["oldP"]) = (old, oldP)
        (stats["young"], stats["youngP"]) = (young, youngP)
        html += _("Total number of cards:") + " <b>%d</b><br>" % total
        html += _(
            "Total number of facts:") + " <b>%d</b><br><br>" % d.factCount

        html += "<b>" + _("Card Maturity") + "</b><br>"
        html += _("Mature cards: <!--card count-->"
                  ) + " <b>%(old)d</b> (%(oldP)s)<br>" % {
                      'old': stats['old'],
                      'oldP': fmtPerc(stats['oldP'])
                  }
        html += _("Young cards: <!--card count-->"
                  ) + " <b>%(young)d</b> (%(youngP)s)<br>" % {
                      'young': stats['young'],
                      'youngP': fmtPerc(stats['youngP'])
                  }
        html += _("Unseen cards:") + " <b>%(new)d</b> (%(newP)s)<br>" % {
            'new': stats['new'],
            'newP': fmtPerc(stats['newP'])
        }
        avgInt = self.getAverageInterval()
        if avgInt:
            html += _("Average interval: ") + (
                "<b>%s</b> ") % fmtFloat(avgInt) + _("days")
            html += "<br>"
        html += "<br>"
        html += "<b>" + _("Correct Answers") + "</b><br>"
        html += _("Mature cards: <!--correct answers-->") + " <b>" + fmtPerc(
            stats['gMatureYes%']) + ("</b> " +
                                     _("(%(partOf)d of %(totalSum)d)") % {
                                         'partOf': stats['gMatureYes'],
                                         'totalSum': stats['gMatureTotal']
                                     } + "<br>")
        html += _("Young cards: <!--correct answers-->") + " <b>" + fmtPerc(
            stats['gYoungYes%']) + ("</b> " +
                                    _("(%(partOf)d of %(totalSum)d)") % {
                                        'partOf': stats['gYoungYes'],
                                        'totalSum': stats['gYoungTotal']
                                    } + "<br>")
        html += _("First-seen cards:") + " <b>" + fmtPerc(
            stats['gNewYes%']) + ("</b> " +
                                  _("(%(partOf)d of %(totalSum)d)") % {
                                      'partOf': stats['gNewYes'],
                                      'totalSum': stats['gNewTotal']
                                  } + "<br><br>")

        # average pending time
        existing = d.cardCount - d.newCountToday

        def tr(a, b):
            return "<tr><td>%s</td><td align=right>%s</td></tr>" % (a, b)

        def repsPerDay(reps, days):
            retval = ("<b>%d</b> " % reps) + ngettext("rep", "reps", reps)
            retval += ("/<b>%d</b> " % days) + ngettext("day", "days", days)
            return retval

        if existing and avgInt:
            html += "<b>" + _("Recent Work") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            html += tr(
                _("In last week"),
                repsPerDay(self.getRepsDone(-7, 0),
                           self.getDaysReviewed(-7, 0)))
            html += tr(
                _("In last month"),
                repsPerDay(self.getRepsDone(-30, 0),
                           self.getDaysReviewed(-30, 0)))
            html += tr(
                _("In last 3 months"),
                repsPerDay(self.getRepsDone(-92, 0),
                           self.getDaysReviewed(-92, 0)))
            html += tr(
                _("In last 6 months"),
                repsPerDay(self.getRepsDone(-182, 0),
                           self.getDaysReviewed(-182, 0)))
            html += tr(
                _("In last year"),
                repsPerDay(self.getRepsDone(-365, 0),
                           self.getDaysReviewed(-365, 0)))
            html += tr(
                _("Deck life"),
                repsPerDay(self.getRepsDone(-13000, 0),
                           self.getDaysReviewed(-13000, 0)))
            html += "</table>"

            html += "<br><br><b>" + _("Average Daily Reviews") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            html += tr(_("Deck life"), ("<b>%s</b> ") %
                       (fmtFloat(self.getSumInverseRoundInterval())) +
                       _("cards/day"))
            html += tr(_("In next week"), ("<b>%s</b> ") %
                       (fmtFloat(self.getWorkloadPeriod(7))) + _("cards/day"))
            html += tr(_("In next month"), ("<b>%s</b> ") %
                       (fmtFloat(self.getWorkloadPeriod(30))) + _("cards/day"))
            html += tr(_("In last week"), ("<b>%s</b> ") %
                       (fmtFloat(self.getPastWorkloadPeriod(7))) +
                       _("cards/day"))
            html += tr(_("In last month"), ("<b>%s</b> ") %
                       (fmtFloat(self.getPastWorkloadPeriod(30))) +
                       _("cards/day"))
            html += tr(_("In last 3 months"), ("<b>%s</b> ") %
                       (fmtFloat(self.getPastWorkloadPeriod(92))) +
                       _("cards/day"))
            html += tr(_("In last 6 months"), ("<b>%s</b> ") %
                       (fmtFloat(self.getPastWorkloadPeriod(182))) +
                       _("cards/day"))
            html += tr(_("In last year"), ("<b>%s</b> ") %
                       (fmtFloat(self.getPastWorkloadPeriod(365))) +
                       _("cards/day"))
            html += "</table>"

            html += "<br><br><b>" + _("Average Added") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            html += tr(
                _("Deck life"),
                _("<b>%(a)s</b>/day, <b>%(b)s</b>/mon") % {
                    'a': fmtFloat(self.newAverage()),
                    'b': fmtFloat(self.newAverage() * 30)
                })
            np = self.getNewPeriod(7)
            html += tr(
                _("In last week"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(7))
                }))
            np = self.getNewPeriod(30)
            html += tr(
                _("In last month"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(30))
                }))
            np = self.getNewPeriod(92)
            html += tr(
                _("In last 3 months"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(92))
                }))
            np = self.getNewPeriod(182)
            html += tr(
                _("In last 6 months"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(182))
                }))
            np = self.getNewPeriod(365)
            html += tr(
                _("In last year"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(365))
                }))
            html += "</table>"

            html += "<br><br><b>" + _("Average New Seen") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            np = self.getFirstPeriod(7)
            html += tr(
                _("In last week"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(7))
                }))
            np = self.getFirstPeriod(30)
            html += tr(
                _("In last month"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(30))
                }))
            np = self.getFirstPeriod(92)
            html += tr(
                _("In last 3 months"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(92))
                }))
            np = self.getFirstPeriod(182)
            html += tr(
                _("In last 6 months"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(182))
                }))
            np = self.getFirstPeriod(365)
            html += tr(
                _("In last year"),
                _("<b>%(a)d</b> (<b>%(b)s</b>/day)") %
                ({
                    'a': np,
                    'b': fmtFloat(np / float(365))
                }))
            html += "</table>"

            html += "<br><br><b>" + _("Card Ease") + "</b><br>"
            html += _("Lowest factor: %.2f") % d.s.scalar(
                "select min(factor) from cards") + "<br>"
            html += _("Average factor: %.2f") % d.s.scalar(
                "select avg(factor) from cards") + "<br>"
            html += _("Highest factor: %.2f") % d.s.scalar(
                "select max(factor) from cards") + "<br>"

            html = runFilter("deckStats", html)
        return html
 def strTime(self, tm):
     s = oldanki.utils.fmtTimeSpan(time.time() - tm)
     return _("%s ago") % s
 def report(self):
     c = self.card
     fmt = oldanki.utils.fmtTimeSpan
     fmtFloat = oldanki.utils.fmtFloat
     self.txt = "<table>"
     self.addLine(_("Added"), self.strTime(c.created))
     if c.firstAnswered:
         self.addLine(_("First Review"), self.strTime(c.firstAnswered))
     self.addLine(_("Changed"), self.strTime(c.modified))
     if c.reps:
         next = time.time() - c.combinedDue
         if next > 0:
             next = _("%s ago") % fmt(next)
         else:
             next = _("in %s") % fmt(abs(next))
         self.addLine(_("Due"), next)
     self.addLine(_("Interval"), fmt(c.interval * 86400))
     self.addLine(_("Ease"), fmtFloat(c.factor, point=2))
     if c.lastDue:
         last = _("%s ago") % fmt(time.time() - c.lastDue)
         self.addLine(_("Last Due"), last)
     if c.interval != c.lastInterval:
         # don't show the last interval if it hasn't been updated yet
         self.addLine(_("Last Interval"), fmt(c.lastInterval * 86400))
     self.addLine(_("Last Ease"), fmtFloat(c.lastFactor, point=2))
     if c.reps:
         self.addLine(_("Reviews"),
                      "%d/%d (s=%d)" % (c.yesCount, c.reps, c.successive))
     avg = fmt(c.averageTime, point=2)
     self.addLine(_("Average Time"), avg)
     total = fmt(c.reviewTime, point=2)
     self.addLine(_("Total Time"), total)
     self.addLine(_("Model Tags"), c.fact.model.tags)
     self.addLine(_("Card Template") + "&nbsp;" * 5, c.cardModel.name)
     self.txt += "</table>"
     return self.txt
Exemple #24
0
    "hours": lambda n: ngettext("%s hour", "%s hours", n),
    "minutes": lambda n: ngettext("%s minute", "%s minutes", n),
    "seconds": lambda n: ngettext("%s second", "%s seconds", n),
}

afterTimeTable = {
    "years": lambda n: ngettext("%s year<!--after-->", "%s years<!--after-->", n),
    "months": lambda n: ngettext("%s month<!--after-->", "%s months<!--after-->", n),
    "days": lambda n: ngettext("%s day<!--after-->", "%s days<!--after-->", n),
    "hours": lambda n: ngettext("%s hour<!--after-->", "%s hours<!--after-->", n),
    "minutes": lambda n: ngettext("%s minute<!--after-->", "%s minutes<!--after-->", n),
    "seconds": lambda n: ngettext("%s second<!--after-->", "%s seconds<!--after-->", n),
}

shortTimeTable = {
    "years": _("%sy"),
    "months": _("%sm"),
    "days": _("%sd"),
    "hours": _("%sh"),
    "minutes": _("%sm"),
    "seconds": _("%ss"),
}


def fmtTimeSpan(time, pad=0, point=0, short=False, after=False):
    "Return a string representing a time span (eg '2 days')."
    (type, point) = optimalPeriod(time, point)
    time = convertSecondsTo(time, type)
    if not point:
        time = math.floor(time)
    if short:
def rebuildMediaDir(deck, delete=False, dirty=True):
    mdir = deck.mediaDir()
    if not mdir:
        return (0, 0)
    deck.startProgress(title=_("Check Media DB"))
    # set all ref counts to 0
    deck.s.statement("update media set size = 0")
    # look through cards for media references
    refs = {}
    normrefs = {}
    def norm(s):
        if isinstance(s, str):
            return unicodedata.normalize('NFD', s)
        return s
    for (question, answer) in deck.s.all(
        "select question, answer from cards"):
        for txt in (question, answer):
            for f in mediaFiles(txt):
                if f in refs:
                    refs[f] += 1
                else:
                    refs[f] = 1
                    normrefs[norm(f)] = True
    # update ref counts
    for (file, count) in list(refs.items()):
        updateMediaCount(deck, file, count)
    # find unused media
    unused = []
    for file in os.listdir(mdir):
        path = os.path.join(mdir, file)
        if not os.path.isfile(path):
            # ignore directories
            continue
        nfile = norm(file)
        if nfile not in normrefs:
            unused.append(file)
    # optionally delete
    if delete:
        for f in unused:
            path = os.path.join(mdir, f)
            os.unlink(path)
    # remove entries in db for unused media
    removeUnusedMedia(deck)
    # check md5s are up to date
    update = []
    for (file, created, md5) in deck.s.all(
        "select filename, created, originalPath from media"):
        path = os.path.join(mdir, file)
        if not os.path.exists(path):
            if md5:
                update.append({'f':file, 'sum':"", 'c':time.time()})
        else:
            sum = str(
                checksum(open(os.path.join(mdir, file), "rb").read()))
            if md5 != sum:
                update.append({'f':file, 'sum':sum, 'c':time.time()})
    if update:
        deck.s.statements("""
update media set originalPath = :sum, created = :c where filename = :f""",
                          update)
    # update deck and get return info
    if dirty:
        deck.flushMod()
    nohave = deck.s.column0("select filename from media where originalPath = ''")
    deck.finishProgress()
    return (nohave, unused)
Exemple #26
0
 def report(self):
     c = self.card
     fmt = oldanki.utils.fmtTimeSpan
     fmtFloat = oldanki.utils.fmtFloat
     self.txt = "<table>"
     self.addLine(_("Added"), self.strTime(c.created))
     if c.firstAnswered:
         self.addLine(_("First Review"), self.strTime(c.firstAnswered))
     self.addLine(_("Changed"), self.strTime(c.modified))
     if c.reps:
         next = time.time() - c.combinedDue
         if next > 0:
             next = _("%s ago") % fmt(next)
         else:
             next = _("in %s") % fmt(abs(next))
         self.addLine(_("Due"), next)
     self.addLine(_("Interval"), fmt(c.interval * 86400))
     self.addLine(_("Ease"), fmtFloat(c.factor, point=2))
     if c.lastDue:
         last = _("%s ago") % fmt(time.time() - c.lastDue)
         self.addLine(_("Last Due"), last)
     if c.interval != c.lastInterval:
         # don't show the last interval if it hasn't been updated yet
         self.addLine(_("Last Interval"), fmt(c.lastInterval * 86400))
     self.addLine(_("Last Ease"), fmtFloat(c.lastFactor, point=2))
     if c.reps:
         self.addLine(_("Reviews"), "%d/%d (s=%d)" % (
             c.yesCount, c.reps, c.successive))
     avg = fmt(c.averageTime, point=2)
     self.addLine(_("Average Time"),avg)
     total = fmt(c.reviewTime, point=2)
     self.addLine(_("Total Time"), total)
     self.addLine(_("Model Tags"), c.fact.model.tags)
     self.addLine(_("Card Template") + "&nbsp;"*5, c.cardModel.name)
     self.txt += "</table>"
     return self.txt
Exemple #27
0
class AnkiExporter(Exporter):

    key = _("Anki Deck (*.oldanki)")
    ext = ".oldanki"

    def __init__(self, deck):
        Exporter.__init__(self, deck)
        self.includeSchedulingInfo = False
        self.includeMedia = True

    def exportInto(self, path):
        n = 3
        if not self.includeSchedulingInfo:
            n += 1
        self.deck.startProgress(n)
        self.deck.updateProgress(_("Exporting..."))
        try:
            os.unlink(path)
        except (IOError, OSError):
            pass
        self.newDeck = DeckStorage.Deck(path)
        client = SyncClient(self.deck)
        server = SyncServer(self.newDeck)
        client.setServer(server)
        client.localTime = self.deck.modified
        client.remoteTime = 0
        self.deck.s.flush()
        # set up a custom change list and sync
        lsum = self.localSummary()
        rsum = server.summary(0)
        self.deck.updateProgress()
        payload = client.genPayload((lsum, rsum))
        self.deck.updateProgress()
        res = server.applyPayload(payload)
        if not self.includeSchedulingInfo:
            self.deck.updateProgress()
            self.newDeck.s.statement("""
delete from reviewHistory""")
            self.newDeck.s.statement("""
update cards set
interval = 0,
lastInterval = 0,
due = created,
lastDue = 0,
factor = 2.5,
firstAnswered = 0,
reps = 0,
successive = 0,
averageTime = 0,
reviewTime = 0,
youngEase0 = 0,
youngEase1 = 0,
youngEase2 = 0,
youngEase3 = 0,
youngEase4 = 0,
matureEase0 = 0,
matureEase1 = 0,
matureEase2 = 0,
matureEase3 = 0,
matureEase4 = 0,
yesCount = 0,
noCount = 0,
spaceUntil = 0,
type = 2,
relativeDelay = 2,
combinedDue = created,
modified = :now
""",
                                     now=time.time())
            self.newDeck.s.statement("""
delete from stats""")
        # media
        if self.includeMedia:
            server.deck.mediaPrefix = ""
            copyLocalMedia(client.deck, server.deck)
        # need to save manually
        self.newDeck.rebuildCounts()
        self.newDeck.updateAllPriorities()
        self.exportedCards = self.newDeck.cardCount
        self.newDeck.utcOffset = -1
        self.newDeck.s.commit()
        self.newDeck.close()
        self.deck.finishProgress()

    def localSummary(self):
        cardIds = self.cardIds()
        cStrIds = ids2str(cardIds)
        cards = self.deck.s.all("""
select id, modified from cards
where id in %s""" % cStrIds)
        facts = self.deck.s.all("""
select facts.id, facts.modified from cards, facts where
facts.id = cards.factId and
cards.id in %s""" % cStrIds)
        models = self.deck.s.all("""
select models.id, models.modified from models, facts where
facts.modelId = models.id and
facts.id in %s""" % ids2str([f[0] for f in facts]))
        media = self.deck.s.all("""
select id, created from media""")
        return {
            # cards
            "cards": cards,
            "delcards": [],
            # facts
            "facts": facts,
            "delfacts": [],
            # models
            "models": models,
            "delmodels": [],
            # media
            "media": media,
            "delmedia": [],
        }
Exemple #28
0
    "hours": lambda n: ngettext("%s hour", "%s hours", n),
    "minutes": lambda n: ngettext("%s minute", "%s minutes", n),
    "seconds": lambda n: ngettext("%s second", "%s seconds", n),
    }

afterTimeTable = {
    "years": lambda n: ngettext("%s year<!--after-->", "%s years<!--after-->", n),
    "months": lambda n: ngettext("%s month<!--after-->", "%s months<!--after-->", n),
    "days": lambda n: ngettext("%s day<!--after-->", "%s days<!--after-->", n),
    "hours": lambda n: ngettext("%s hour<!--after-->", "%s hours<!--after-->", n),
    "minutes": lambda n: ngettext("%s minute<!--after-->", "%s minutes<!--after-->", n),
    "seconds": lambda n: ngettext("%s second<!--after-->", "%s seconds<!--after-->", n),
    }

shortTimeTable = {
    "years": _("%sy"),
    "months": _("%sm"),
    "days": _("%sd"),
    "hours": _("%sh"),
    "minutes": _("%sm"),
    "seconds": _("%ss"),
    }

def fmtTimeSpan(time, pad=0, point=0, short=False, after=False):
    "Return a string representing a time span (eg '2 days')."
    (type, point) = optimalPeriod(time, point)
    time = convertSecondsTo(time, type)
    if not point:
        time = math.floor(time)
    if short:
        fmt = shortTimeTable[type]
Exemple #29
0
 def strTime(self, tm):
     s = oldanki.utils.fmtTimeSpan(time.time() - tm)
     return _("%s ago") % s
Exemple #30
0
    def exportInto(self, path):
        n = 3
        if not self.includeSchedulingInfo:
            n += 1
        self.deck.startProgress(n)
        self.deck.updateProgress(_("Exporting..."))
        try:
            os.unlink(path)
        except (IOError, OSError):
            pass
        self.newDeck = DeckStorage.Deck(path)
        client = SyncClient(self.deck)
        server = SyncServer(self.newDeck)
        client.setServer(server)
        client.localTime = self.deck.modified
        client.remoteTime = 0
        self.deck.s.flush()
        # set up a custom change list and sync
        lsum = self.localSummary()
        rsum = server.summary(0)
        self.deck.updateProgress()
        payload = client.genPayload((lsum, rsum))
        self.deck.updateProgress()
        res = server.applyPayload(payload)
        if not self.includeSchedulingInfo:
            self.deck.updateProgress()
            self.newDeck.s.statement("""
delete from reviewHistory""")
            self.newDeck.s.statement("""
update cards set
interval = 0,
lastInterval = 0,
due = created,
lastDue = 0,
factor = 2.5,
firstAnswered = 0,
reps = 0,
successive = 0,
averageTime = 0,
reviewTime = 0,
youngEase0 = 0,
youngEase1 = 0,
youngEase2 = 0,
youngEase3 = 0,
youngEase4 = 0,
matureEase0 = 0,
matureEase1 = 0,
matureEase2 = 0,
matureEase3 = 0,
matureEase4 = 0,
yesCount = 0,
noCount = 0,
spaceUntil = 0,
type = 2,
relativeDelay = 2,
combinedDue = created,
modified = :now
""", now=time.time())
            self.newDeck.s.statement("""
delete from stats""")
        # media
        if self.includeMedia:
            server.deck.mediaPrefix = ""
            copyLocalMedia(client.deck, server.deck)
        # need to save manually
        self.newDeck.rebuildCounts()
        self.newDeck.updateAllPriorities()
        self.exportedCards = self.newDeck.cardCount
        self.newDeck.utcOffset = -1
        self.newDeck.s.commit()
        self.newDeck.close()
        self.deck.finishProgress()
Exemple #31
0
def exporters():
    return ((_("Anki Deck (*.oldanki)"), AnkiExporter),
            (_("Cards in tab-separated text file (*.txt)"), TextCardExporter),
            (_("Facts in tab-separated text file (*.txt)"), TextFactExporter))
Exemple #32
0
    def report(self):
        "Return an HTML string with a report."
        fmtPerc = oldanki.utils.fmtPercentage
        fmtFloat = oldanki.utils.fmtFloat
        if self.deck.isEmpty():
            return _("Please add some cards first.") + "<p/>"
        d = self.deck
        html="<h1>" + _("Deck Statistics") + "</h1>"
        html += _("Deck created: <b>%s</b> ago<br>") % self.createdTimeStr()
        total = d.cardCount
        new = d.newCountAll()
        young = d.youngCardCount()
        old = d.matureCardCount()
        newP = new / float(total) * 100
        youngP = young / float(total) * 100
        oldP = old / float(total) * 100
        stats = d.getStats()
        (stats["new"], stats["newP"]) = (new, newP)
        (stats["old"], stats["oldP"]) = (old, oldP)
        (stats["young"], stats["youngP"]) = (young, youngP)
        html += _("Total number of cards:") + " <b>%d</b><br>" % total
        html += _("Total number of facts:") + " <b>%d</b><br><br>" % d.factCount

        html += "<b>" + _("Card Maturity") + "</b><br>"
        html += _("Mature cards: <!--card count-->") + " <b>%(old)d</b> (%(oldP)s)<br>" % {
                'old': stats['old'], 'oldP' : fmtPerc(stats['oldP'])}
        html += _("Young cards: <!--card count-->") + " <b>%(young)d</b> (%(youngP)s)<br>" % {
                'young': stats['young'], 'youngP' : fmtPerc(stats['youngP'])}
        html += _("Unseen cards:") + " <b>%(new)d</b> (%(newP)s)<br>" % {
                'new': stats['new'], 'newP' : fmtPerc(stats['newP'])}
        avgInt = self.getAverageInterval()
        if avgInt:
            html += _("Average interval: ") + ("<b>%s</b> ") % fmtFloat(avgInt) + _("days")
            html += "<br>"
        html += "<br>"
        html += "<b>" + _("Correct Answers") + "</b><br>"
        html += _("Mature cards: <!--correct answers-->") + " <b>" + fmtPerc(stats['gMatureYes%']) + (
                "</b> " + _("(%(partOf)d of %(totalSum)d)") % {
                'partOf' : stats['gMatureYes'],
                'totalSum' : stats['gMatureTotal'] } + "<br>")
        html += _("Young cards: <!--correct answers-->")  + " <b>" + fmtPerc(stats['gYoungYes%']) + (
                "</b> " + _("(%(partOf)d of %(totalSum)d)") % {
                'partOf' : stats['gYoungYes'],
                'totalSum' : stats['gYoungTotal'] } + "<br>")
        html += _("First-seen cards:") + " <b>" + fmtPerc(stats['gNewYes%']) + (
                "</b> " + _("(%(partOf)d of %(totalSum)d)") % {
                'partOf' : stats['gNewYes'],
                'totalSum' : stats['gNewTotal'] } + "<br><br>")

        # average pending time
        existing = d.cardCount - d.newCountToday
        def tr(a, b):
            return "<tr><td>%s</td><td align=right>%s</td></tr>" % (a, b)
        def repsPerDay(reps,days):
            retval =  ("<b>%d</b> " % reps)  + ngettext("rep", "reps", reps)
            retval += ("/<b>%d</b> " % days) + ngettext("day", "days", days)
            return retval
        if existing and avgInt:
            html += "<b>" + _("Recent Work") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            html += tr(_("In last week"), repsPerDay(
                self.getRepsDone(-7, 0),
                self.getDaysReviewed(-7, 0)))
            html += tr(_("In last month"), repsPerDay(
                self.getRepsDone(-30, 0),
                self.getDaysReviewed(-30, 0)))
            html += tr(_("In last 3 months"), repsPerDay(
                self.getRepsDone(-92, 0),
                self.getDaysReviewed(-92, 0)))
            html += tr(_("In last 6 months"), repsPerDay(
                self.getRepsDone(-182, 0),
                self.getDaysReviewed(-182, 0)))
            html += tr(_("In last year"), repsPerDay(
                self.getRepsDone(-365, 0),
                self.getDaysReviewed(-365, 0)))
            html += tr(_("Deck life"), repsPerDay(
                self.getRepsDone(-13000, 0),
                self.getDaysReviewed(-13000, 0)))
            html += "</table>"

            html += "<br><br><b>" + _("Average Daily Reviews") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            html += tr(_("Deck life"), ("<b>%s</b> ") % (
                fmtFloat(self.getSumInverseRoundInterval())) + _("cards/day"))
            html += tr(_("In next week"), ("<b>%s</b> ") % (
                fmtFloat(self.getWorkloadPeriod(7))) + _("cards/day"))
            html += tr(_("In next month"), ("<b>%s</b> ") % (
                fmtFloat(self.getWorkloadPeriod(30))) + _("cards/day"))
            html += tr(_("In last week"), ("<b>%s</b> ") % (
                fmtFloat(self.getPastWorkloadPeriod(7))) + _("cards/day"))
            html += tr(_("In last month"), ("<b>%s</b> ") % (
                fmtFloat(self.getPastWorkloadPeriod(30))) + _("cards/day"))
            html += tr(_("In last 3 months"), ("<b>%s</b> ") % (
                fmtFloat(self.getPastWorkloadPeriod(92))) + _("cards/day"))
            html += tr(_("In last 6 months"), ("<b>%s</b> ") % (
                fmtFloat(self.getPastWorkloadPeriod(182))) + _("cards/day"))
            html += tr(_("In last year"), ("<b>%s</b> ") % (
                fmtFloat(self.getPastWorkloadPeriod(365))) + _("cards/day"))
            html += "</table>"

            html += "<br><br><b>" + _("Average Added") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            html += tr(_("Deck life"), _("<b>%(a)s</b>/day, <b>%(b)s</b>/mon") % {
                'a': fmtFloat(self.newAverage()), 'b': fmtFloat(self.newAverage()*30)})
            np = self.getNewPeriod(7)
            html += tr(_("In last week"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(7))}))
            np = self.getNewPeriod(30)
            html += tr(_("In last month"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(30))}))
            np = self.getNewPeriod(92)
            html += tr(_("In last 3 months"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(92))}))
            np = self.getNewPeriod(182)
            html += tr(_("In last 6 months"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(182))}))
            np = self.getNewPeriod(365)
            html += tr(_("In last year"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(365))}))
            html += "</table>"

            html += "<br><br><b>" + _("Average New Seen") + "</b>"
            if sys.platform.startswith("darwin"):
                html += "<table width=250>"
            else:
                html += "<table width=200>"
            np = self.getFirstPeriod(7)
            html += tr(_("In last week"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(7))}))
            np = self.getFirstPeriod(30)
            html += tr(_("In last month"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(30))}))
            np = self.getFirstPeriod(92)
            html += tr(_("In last 3 months"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(92))}))
            np = self.getFirstPeriod(182)
            html += tr(_("In last 6 months"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(182))}))
            np = self.getFirstPeriod(365)
            html += tr(_("In last year"), _("<b>%(a)d</b> (<b>%(b)s</b>/day)") % (
                {'a': np, 'b': fmtFloat(np / float(365))}))
            html += "</table>"

            html += "<br><br><b>" + _("Card Ease") + "</b><br>"
            html += _("Lowest factor: %.2f") % d.s.scalar(
                "select min(factor) from cards") + "<br>"
            html += _("Average factor: %.2f") % d.s.scalar(
                "select avg(factor) from cards") + "<br>"
            html += _("Highest factor: %.2f") % d.s.scalar(
                "select max(factor) from cards") + "<br>"

            html = runFilter("deckStats", html)
        return html
Exemple #33
0
    def exportInto(self, path):
        n = 3
        if not self.includeSchedulingInfo:
            n += 1
        self.deck.startProgress(n)
        self.deck.updateProgress(_("Exporting..."))
        try:
            os.unlink(path)
        except (IOError, OSError):
            pass
        self.newDeck = DeckStorage.Deck(path)
        client = SyncClient(self.deck)
        server = SyncServer(self.newDeck)
        client.setServer(server)
        client.localTime = self.deck.modified
        client.remoteTime = 0
        self.deck.s.flush()
        # set up a custom change list and sync
        lsum = self.localSummary()
        rsum = server.summary(0)
        self.deck.updateProgress()
        payload = client.genPayload((lsum, rsum))
        self.deck.updateProgress()
        res = server.applyPayload(payload)
        if not self.includeSchedulingInfo:
            self.deck.updateProgress()
            self.newDeck.s.statement("""
delete from reviewHistory""")
            self.newDeck.s.statement("""
update cards set
interval = 0,
lastInterval = 0,
due = created,
lastDue = 0,
factor = 2.5,
firstAnswered = 0,
reps = 0,
successive = 0,
averageTime = 0,
reviewTime = 0,
youngEase0 = 0,
youngEase1 = 0,
youngEase2 = 0,
youngEase3 = 0,
youngEase4 = 0,
matureEase0 = 0,
matureEase1 = 0,
matureEase2 = 0,
matureEase3 = 0,
matureEase4 = 0,
yesCount = 0,
noCount = 0,
spaceUntil = 0,
type = 2,
relativeDelay = 2,
combinedDue = created,
modified = :now
""",
                                     now=time.time())
            self.newDeck.s.statement("""
delete from stats""")
        # media
        if self.includeMedia:
            server.deck.mediaPrefix = ""
            copyLocalMedia(client.deck, server.deck)
        # need to save manually
        self.newDeck.rebuildCounts()
        self.newDeck.updateAllPriorities()
        self.exportedCards = self.newDeck.cardCount
        self.newDeck.utcOffset = -1
        self.newDeck.s.commit()
        self.newDeck.close()
        self.deck.finishProgress()
Exemple #34
0
def rebuildMediaDir(deck, delete=False, dirty=True):
    mdir = deck.mediaDir()
    if not mdir:
        return (0, 0)
    deck.startProgress(title=_("Check Media DB"))
    # set all ref counts to 0
    deck.s.statement("update media set size = 0")
    # look through cards for media references
    refs = {}
    normrefs = {}
    def norm(s):
        if isinstance(s, unicode):
            return unicodedata.normalize('NFD', s)
        return s
    for (question, answer) in deck.s.all(
        "select question, answer from cards"):
        for txt in (question, answer):
            for f in mediaFiles(txt):
                if f in refs:
                    refs[f] += 1
                else:
                    refs[f] = 1
                    normrefs[norm(f)] = True
    # update ref counts
    for (file, count) in refs.items():
        updateMediaCount(deck, file, count)
    # find unused media
    unused = []
    for file in os.listdir(mdir):
        path = os.path.join(mdir, file)
        if not os.path.isfile(path):
            # ignore directories
            continue
        nfile = norm(file)
        if nfile not in normrefs:
            unused.append(file)
    # optionally delete
    if delete:
        for f in unused:
            path = os.path.join(mdir, f)
            os.unlink(path)
    # remove entries in db for unused media
    removeUnusedMedia(deck)
    # check md5s are up to date
    update = []
    for (file, created, md5) in deck.s.all(
        "select filename, created, originalPath from media"):
        path = os.path.join(mdir, file)
        if not os.path.exists(path):
            if md5:
                update.append({'f':file, 'sum':u"", 'c':time.time()})
        else:
            sum = unicode(
                checksum(open(os.path.join(mdir, file), "rb").read()))
            if md5 != sum:
                update.append({'f':file, 'sum':sum, 'c':time.time()})
    if update:
        deck.s.statements("""
update media set originalPath = :sum, created = :c where filename = :f""",
                          update)
    # update deck and get return info
    if dirty:
        deck.flushMod()
    nohave = deck.s.column0("select filename from media where originalPath = ''")
    deck.finishProgress()
    return (nohave, unused)