def sync(): cs = CollectionStats(mw.col) cs.wholeCollection = True lims = [] num = 365 chunk = 1 if num is not None: lims.append("id > %d" % ( (cs.col.sched.dayCutoff-(num*chunk*86400))*1000)) lim = cs._revlogLimit() if lim: lims.append(lim) if lims: lim = "where " + " and ".join(lims) else: lim = "" if cs.type == 0: tf = 60.0 # minutes else: tf = 3600.0 # hours revlog = cs.col.db.all(""" select (cast((id/1000.0 - :cut) / 86400.0 as int))/:chunk as day, sum(time)/1000, sum(case when type = 0 then 1 else 0 end), -- lrn count sum(case when type = 1 and lastIvl < 21 then 1 else 0 end), -- yng count sum(case when type = 1 and lastIvl >= 21 then 1 else 0 end), -- mtr count sum(case when type = 2 then 1 else 0 end), -- lapse count sum(case when type = 3 then 1 else 0 end), -- cram count sum(case when type = 0 then time/1000.0 else 0 end)/:tf, -- lrn time -- yng + mtr time sum(case when type = 1 and lastIvl < 21 then time/1000.0 else 0 end)/:tf, sum(case when type = 1 and lastIvl >= 21 then time/1000.0 else 0 end)/:tf, sum(case when type = 2 then time/1000.0 else 0 end)/:tf, -- lapse time sum(case when type = 3 then time/1000.0 else 0 end)/:tf -- cram time from revlog %s group by day order by day""" % lim, cut=cs.col.sched.dayCutoff, tf=tf, chunk=chunk) values = {'data': revlog, 'email': mw.pm.profile.get('syncUser')} html = http_post(values) showInfo(html)
def _create_revision_count_graph(self): stats = CollectionStats(self.col) categories_labels = list(range(-30, 1, 1)) data = stats.revision_count_stats() data = self._transform_first_column_to_key(data) data = self._add_default_value_for_missing_keys( data, [0, 0, 0, 0, 0], categories_labels) series = QStackedBarSeries(self) setLearning = QBarSet("Learning", self) [setLearning.append(row[0]) for row in data] setYoung = QBarSet("Young", self) [setYoung.append(row[1]) for row in data] setMature = QBarSet("Mature", self) [setMature.append(row[2]) for row in data] setLapse = QBarSet("Lapse", self) [setLapse.append(row[3]) for row in data] setEarly = QBarSet("Early", self) [setEarly.append(row[4]) for row in data] categories = QBarCategoryAxis() categories.setCategories([str(-i) for i in categories_labels]) series.append(setLearning) series.append(setYoung) series.append(setMature) series.append(setLapse) series.append(setEarly) chart = QChart() chart.addSeries(series) chart.createDefaultAxes() chart.setAxisX(categories, series) chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTitle("Number of cards reviewed recently") chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartview = QChartView(chart) chartview.setRenderHint(QPainter.Antialiasing) self.layout.addWidget(chartview)
def _create_reviews_due_chart(self): ## TODO: later replace by date selector start, end, chunk = 0, 31, 1 stats = CollectionStats(self.col) ## TODO: refactor stats._due categories_labels = list(range(0, 31, 1)) data = stats.due(start, end, chunk) data = self._transform_first_column_to_key(data) data = self._add_default_value_for_missing_keys( data, [0, 0, 0, 0], categories_labels) series = QStackedBarSeries(self) setYoung = QBarSet("Young", self) [setYoung.append(row[0]) for row in data] setAdolescent = QBarSet("Adolescent", self) [setAdolescent.append(row[1]) for row in data] setMature = QBarSet("Mature", self) [setMature.append(row[2]) for row in data] setOld = QBarSet("Old", self) [setOld.append(row[3]) for row in data] categories = QBarCategoryAxis() categories.setCategories([str(i) for i in categories_labels]) series.append(setOld) series.append(setMature) series.append(setAdolescent) series.append(setYoung) chart = QChart() chart.addSeries(series) chart.createDefaultAxes() chart.setAxisX(categories, series) chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTitle("Forecast: the number of reviews due in the future") chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartview = QChartView(chart) chartview.setRenderHint(QPainter.Antialiasing) self.layout.addWidget(chartview)
def test_days_studied_works_with_old_logs(): # ARRANGE col = getEmptyCol() ivl = 1 card = create_learning_card(col, ivl) hard_answer = 2 factor = 2500 time_taken = 10 # seconds for day in range(1, 31): timestamp = datetime(2020, 4, day, 12, 0, 0, 0).timestamp() # days, due were added, so previous logs have 0s col.db.execute("insert into revlog values (?,?,?,?,?,?,?,?,?,?,?)", int(timestamp * 1000), card.id, col.usn(), hard_answer, ivl, ivl, factor, time_taken, 1, 0, 0) # ACT collection_stats = CollectionStats(col) collection_stats.type = 2 return_value = collection_stats._daysStudied() # ASSERT # number of days should be 30 assert return_value[0] == 30
def stats(self) -> "anki.stats.CollectionStats": from anki.stats import CollectionStats return CollectionStats(self)
def stats(self): from anki.stats import CollectionStats return CollectionStats(self)
def _update(self): if not self.dock or not self.dock.isVisible(): return if freezeMode and self.type>10: return card = mw.reviewer.card txt = _("No Current Card") if self.type<-10: txt = _("No Last Card") card = mw.reviewer.lastCard() cs = CollectionStats(mw.col) cs.wholeCollection=True if self.type<0 else False if self.type==0: #review count txt = "<center>%s</center>" % mw.reviewer._remaining() if mw.state=="review" else '? + ? + ?' elif abs(self.type)==1: #todayStats txt = "<center>%s</center>" % cs.todayStats() elif abs(self.type)==2: #forecast chart #no filtered decks did=mw.col.decks.selected() dyn=mw.col.decks.get(did) if dyn['dyn'] and self.type>0: txt="No data for filtered decks" elif ANKI21: p=mw.mediaServer.getPort() txt = """ <script src="http://localhost:%d/_anki/jquery.js"></script> <script src="http://localhost:%d/_anki/plot.js"></script> <center>%s</center> """ % (p,p,self.dueGraph(cs)) else: txt = "<script>%s\n</script><center>%s</center>" % ( anki.js.jquery+anki.js.plot, self.dueGraph(cs)) elif abs(self.type)>=90: txt = self.customViews(card) elif mw.state!='review' and self.type>10: txt = _("No Review Card") elif card: #left or right col cs = CardStats(mw.col, card) if abs(self.type)==12: #card rev log txt = self._revlogData(card, cs) else: #card stats txt = self.deckOptionsInfo(card) txt += _("<h3>Card Stats:</h3>") txt += self.mini_card_stats(card) txt += cs.report() did = card.odid if card.odid else card.did fdid = card.did if card.odid else 0 deckName = mw.col.decks.get(did)['name'] #in case of filtered decks sis=[i for i in mw.col.db.list("select id from cards where nid=?", card.nid)] tags=mw.col.getNote(card.nid).stringTags() txt += """<table width="100%%"><tr> <td><b>Deck ID:</b></td> <td>%d</td></tr><tr> <td><b>Fil DID:</b></td> <td>%d</td></tr><tr> <td><b>oDeck:</b></td> <td>%s</td></tr><tr> <td><b>SiblingCnt:</b></td> <td>%d</td></tr><tr> <td><b>Siblings:</b></td> <td>%s</td></tr><tr> <td><b>Tags:</b></td> <td>%s</td> </tr></table>""" % (did, fdid, deckName, len(sis), str(sis), tags) style = self._style() self.web.setHtml(""" <!doctype html><html><head><style>%s</style></head> <body class="night_mode">%s</body></html>""" % (style, txt))
def test_filter_by_quantiles_all_elements_are_the_same(): data = CollectionStats.filter_by_quantiles(10, 90, [50, 50, 50]) assert data == (50, 50, [(50, 3)])
def report(self, type=0): self.type = type self.data = {} try: title = self.col.decks.get(self.col.decks.active()[0])[u'name'] ws = upload(title) if ws is not None: lim = self._revlogLimit() if lim: lim = " and " + lim a,b,c,d,e,f,g = self.col.db.first(""" select count(), sum(time)/1000, sum(case when ease = 1 then 1 else 0 end), /* failed */ sum(case when type = 0 then 1 else 0 end), /* learning */ sum(case when type = 1 then 1 else 0 end), /* review */ sum(case when type = 2 then 1 else 0 end), /* relearn */ sum(case when type = 3 then 1 else 0 end) /* filter */ from revlog where id > ? """+lim, (self.col.sched.dayCutoff-86400)*1000) self.data["totalCardsStudied"] = a or 0 self.data["timeSpentStudying"] = b or 0 self.data["againCount"] = c or 0 self.data["learn"] = d or 0 self.data["review"] = e or 0 self.data["relearn"] = f or 0 self.data["filtered"] = g or 0 if self.data["totalCardsStudied"] > 0: self.data["percentCorrectAllCards"] = "{0:.2f}".format((1-self.data["againCount"]/float(self.data["totalCardsStudied"]))*100) else: self.data["percentCorrectAllCards"] = 0 mcnt, msum = self.col.db.first(""" select count(), sum(case when ease = 1 then 0 else 1 end) from revlog where lastIvl >= 21 and id > ?"""+lim, (self.col.sched.dayCutoff-86400)*1000) self.data["matureCardsStudied"] = msum or 0 if mcnt > 0: self.data["percentCorrectMatureCards"] = "{0:.2f}".format(self.data["matureCardsStudied"] / float(mcnt) * 100) else: self.data["percentCorrectMatureCards"] = 0 a,b,c,d = self.col.db.first(""" select sum(case when queue=2 and ivl >= 21 then 1 else 0 end), -- mtr sum(case when queue in (1,3) or (queue=2 and ivl < 21) then 1 else 0 end), -- yng/lrn sum(case when queue=0 then 1 else 0 end), -- new sum(case when queue<0 then 1 else 0 end) -- susp from cards where did in %s""" % self._limit()) self.data["totalMatureCards"] = a or 0 self.data["totalYoungLearnCards"] = b or 0 self.data["totalUnseenCards"] = c or 0 self.data["totalSuspendedCards"] = d or 0 for deck in self.col.sched.deckDueList(): if deck[0] == title: self.data["totalDueCards"] = deck[2] break s = '' for key,value in self.data.items(): s += '{0}:{1}\n'.format(key,value) from aqt import mw import datetime currentDay = (datetime.datetime.today() - datetime.datetime.fromtimestamp(mw.col.crt)).days from PyQt4 import QtGui QtGui.QMessageBox.information(mw,'Statistics Upload', s) col = startColumn if not dayInFirstRow: row = ws.row_count + 1 else: col = col + 1 if ws.cell(startRow,startColumn).value == "": row = startRow else: row = currentDay - int(ws.cell(startRow,startColumn).value) + 1 ws.update_cell(row,startColumn,str(currentDay)) if row > ws.row_count: ws.resize(rows = row) cell_list = [] for field in order: cell = self.cell(row, col) cell.value = self.data[field] cell_list.append(cell) col = col + 1 ws.update_cells(cell_list) except: pass return CollectionStats.report(self,type)
def __init__(self, col): CollectionStats.__init__(self,col)