def getNextScheduledCard(msg): """Fetch next scheduled card""" typeCheck(msg, { 'deckName': str, }) with Col() as col: deckName = msg['deckName'] deck = col.decks.byName(deckName) col.decks.select(deck['id']) card = col.sched.getCard() if card is None: return emit.emitResult(None) answerButtonCount = col.sched.answerButtons(card) cardDict[card.id] = card return emit.emitResult({ 'cardId': card.id, 'noteId': card.nid, 'front': card.q(), 'back': card.a(), 'ansButtonCount': answerButtonCount, })
def deleteCardBatch(msg): typeCheck(msg, { 'cardIds': list, }) with Col() as col: col.remCards(msg['cardIds']) return emit.emitResult(True)
def listDeckDue(msg): with Col() as col: dueTree = col.sched.deckDueTree() def traverseDueTree(tree, prefix=''): result = [] for name, deckId, rev, lrn, new, subTree in tree: deck = col.decks.get(deckId) result.append({ 'name': name, 'fullname': prefix + name, 'newCount': new, 'lrnCount': lrn, 'revCount': rev, 'subDecks': traverseDueTree(subTree, prefix + name + '::'), 'collapsed': deck['collapsed'] }) return result return emit.emitResult(traverseDueTree(dueTree))
def changeDues(msg): typeCheck(msg, {'cardIds': list, 'minDue': int, 'maxDue': int}) cids = msg['cardIds'] with Col() as col: checkpoint(col, 'Change card dues') minDue = (msg['minDue'] - col.crt) // 86400 maxDue = (msg['maxDue'] - col.crt) // 86400 for cid in cids: card = col.getCard(cid) oldIvl, oldDue = card.ivl, card.due if card.queue == 0 or card.type == 0: # Ignore for new cards continue # TODO: Properly calculate the next interval using exponential learning curve oldDue = card.due newDue = random.randint(minDue, maxDue) print(oldDue, newDue) card.type = 2 card.queue = 2 card.due = newDue card.ivl += newDue - oldDue card.flush() col.reset() return emit.emitResult(True)
def resetScheduling(msg): typeCheck(msg, {'cardIds': list}) cids = msg['cardIds'] with Col() as col: checkpoint(col, "Reset scheduling and learning on selected cards") col.sched.resetCards(cids) col.sched.removeLrn(cids) return emit.emitResult(True)
def queryTagSuggestions(msg): typeCheck(msg, { 'query': str, }) query = msg['query'] with Col() as col: tagList = col.tags.all() tagList = [tag for tag in tagList if tag.startswith(query)] return emit.emitResult(tagList)
def updateCardsModel(msg): typeCheck(msg, { 'model': str, 'cardIds': list, }) with Col() as col: model = col.models.byName(msg['model']) nidSet = getNidSet(col, msg['cardIds']) modelChanger.changeNotesModel(col, nidSet, model) return emit.emitResult(True)
def getModelList(msg): typeCheck(msg, { 'filename': str, 'datab64': str, }) filename = msg['filename'] data = base64.b64decode(msg['datab64']) with Col() as col: mediaFilename = col.media.writeData(filename, data) print('UPLOAD', filename, len(data), mediaFilename) return emit.emitResult(mediaFilename)
def collapseDeck(msg): typeCheck(msg, { 'deckName': str, 'collapse': bool, }) with Col() as col: deckName = msg['deckName'] newCollapse = msg['collapse'] deck = col.decks.byName(deckName) did = deck['id'] if deck['collapsed'] != newCollapse: col.decks.collapse(did) return emit.emitResult(deck['collapsed'])
def updateNote(msg): typeCheck(msg, {'noteId': int, 'fields': list, 'tags': list}) with Col() as col: noteId = msg['noteId'] fields = msg['fields'] tags = msg['tags'] note = col.getNote(noteId) assert len(fields) == len(note.fields) note.fields[:] = fields note.tags = tags note.flush() return emit.emitResult(True)
def updateCardsDeck(msg): typeCheck(msg, { 'deck': str, 'cardIds': list, }) with Col() as col: newDeck = col.decks.byName(msg['deck']) for cardId in msg['cardIds']: card = col.getCard(cardId) card.did = newDeck['id'] card.flush() col.reset() return emit.emitResult(True)
def answerCard(msg): typeCheck(msg, { 'cardId': int, 'ease': int, }) cardId, ease = int(msg['cardId']), int(msg['ease']) cardId = msg['cardId'] card = cardDict[cardId] ease = int(msg['ease']) with Col() as col: col.sched.answerCard(card, ease) logging.info("Cid[%d] Reviewed with ease %d" % (cardId, ease)) return emit.emitResult(True)
def deleteCardTags(msg): typeCheck(msg, { 'tags': list, 'cardIds': list, }) with Col() as col: tags = msg['tags'] nidSet = getNidSet(col, msg['cardIds']) for nid in nidSet: note = col.getNote(nid) for tag in tags: note.delTag(tag) note.flush() return emit.emitResult(True)
def getDeckInfo(msg): typeCheck(msg, { 'deckName': str, }) with Col() as col: deckNameReq = msg['deckName'] for deckName, did, rev, lrn, new in col.sched.deckDueList(): if deckName == deckNameReq: # SQL Code from More Overview Stats 2 addon col.decks.select(did) total, mature, young, unseen, suspended, due = col.db.first( '''select -- total count(id), -- mature sum(case when queue = 2 and ivl >= 21 then 1 else 0 end), -- young / learning sum(case when queue in (1, 3) or (queue = 2 and ivl < 21) then 1 else 0 end), -- unseen sum(case when queue = 0 then 1 else 0 end), -- suspended sum(case when queue < 0 then 1 else 0 end), -- due sum(case when queue = 1 and due <= ? then 1 else 0 end) from cards where did in %s ''' % col.sched._deckLimit(), round(time.time())) # If there are no cards in current selected deck, if total == 0: mature = young = unseen = suspended = due = 0 return emit.emitResult({ 'name': deckName, 'due': { 'newCount': new, 'lrnCount': lrn, 'revCount': rev, }, 'stat': { 'mature': mature, 'young': young, 'total': total, 'unseen': unseen, 'suspended': suspended, 'due': due } })
def getCardsBatch(msg): typeCheck(msg, {'cardIds': list}) with Col() as col: noteDict = {} cards = [col.getCard(cid) for cid in msg['cardIds']] ret = [] for card in cards: try: note = noteDict[card.nid] except KeyError: note = noteDict[card.nid] = card.note() model = card.model() # Code from aqt/browser.py if card.odid: # Special case: filtered decks due = '(filtered)' elif card.queue == 1: # Learning card due = card.due elif card.queue == 0 or card.type == 0: # New cards due = '(new card)' elif card.queue in (2, 3) or (card.type == 2 and card.queue < 0): due = col.crt + 86400 * card.due else: due = '' ret.append({ 'id': card.id, 'deck': col.decks.get(card.did)['name'], 'noteId': note.id, 'ord': card.ord, 'model': model['name'], 'preview': htmlToTextLine(card.q(browser=True)), 'tags': note.tags, 'createdAt': card.id // 1000, 'updatedAt': card.mod, 'due': due, 'type': card.type, 'queue': card.queue, }) return emit.emitResult(ret)
def getCard(msg): typeCheck(msg, { 'cardId': int, }) with Col() as col: cardId = msg['cardId'] card = col.getCard(cardId) note = card.note() model = card.model() return emit.emitResult({ 'id': card.id, 'deck': col.decks.get(card.did)['name'], 'noteId': note.id, 'model': model['name'], 'fieldFormats': [{ 'name': fFormat['name'], 'sticky': fFormat['sticky'], } for fFormat in model['flds']], 'fields': note.fields, 'tags': note.tags, })
def getNote(msg): typeCheck(msg, { 'noteId': int, }) with Col() as col: noteId = msg['noteId'] note = col.getNote(noteId) fieldTemplateList = note.model()['flds'] return emit.emitResult({ 'id': note.id, 'model': note.model()['name'], 'fieldFormats': [{ 'name': fFormat['name'], 'sticky': fFormat['sticky'], } for fFormat in fieldTemplateList], 'fields': note.fields, 'tags': note.tags, })
def updateCard(msg): typeCheck(msg, { 'cardId': int, 'deck': str, 'fields': list, 'tags': list }) with Col() as col: card = col.getCard(msg['cardId']) note = col.getNote(card.nid) deck = col.decks.byName(msg['deck']) fields = msg['fields'] tags = msg['tags'] assert len(fields) == len(note.fields) note.fields[:] = fields note.tags = tags note.flush() card.did = deck['id'] card.flush() return emit.emitResult(True)
def getModel(msg): typeCheck(msg, { 'modelName': str, }) with Col() as col: modelName = msg['modelName'] model = col.models.byName(modelName) return emit.emitResult({ 'type': 'basic' if model['type'] == 0 else 'cloze', 'name': model['name'], 'templates': [{ 'name': template['name'], 'front': template['qfmt'], 'back': template['afmt'], } for template in model['tmpls']], 'css': model['css'], 'fieldFormats': [{ 'name': fFormat['name'], 'sticky': fFormat['sticky'], } for fFormat in model['flds']] })
def addNote(msg): typeCheck(msg, {'deck': str, 'model': str, 'fields': list, 'tags': list}) with Col() as col: model = col.models.byName(msg['model']) did = col.decks.id(msg['deck'], create=True) # cf) Create deck if not exists fields = msg['fields'] tags = msg['tags'] if not htmlToTextLine(fields[0]): return emit.emitError('First field should not be empty') if isClozeNote(fields) and model['type'] == 0: if msg['model'] in ('Basic', _('Basic')): # Automatic Basic → Cloze model = col.models.byName('Cloze') or col.models.byName( _('Cloze')) else: model = None if not model: return emit.emitError( 'You need a cloze note type to make cloze notes') if not isClozeNote(fields) and model['type'] == 1: return emit.emitError('You need at least one cloze deletion.') note = Note(col, model) if len(note.fields) != len(fields): raise RuntimeError('Field number mismatch') note.fields[:] = fields note.tags[:] = tags model['did'] = did cardNum = col.addNote(note) return emit.emitResult({'noteId': note.id, 'cardNum': cardNum})
def listDeck(msg): typeCheck(msg, { 'query': str, }) query = msg['query'] with Col() as col: sortBy = msg.get('sortBy', 'createdAt') sortOrder = msg.get('sortOrder', 'desc') orderBy = { 'id': 'c.id', 'deck': 'n.did', 'noteId': 'n.id', 'model': 'n.mid', 'preview': 'n.sfld collate nocase, c.ord', 'createdAt': 'n.id, c.ord', 'updatedAt': 'c.mod', 'due': 'c.type, c.due', }[sortBy] cIds = col.findCards(query, orderBy) if sortOrder == 'desc': cIds.reverse() return emit.emitResult(cIds)
def getCidFromNid(msg): typeCheck(msg, {'noteId': int}) with Col() as col: return emit.emitResult(col.findCards('nid:%d' % msg['noteId']))
def listDeck(msg): with Col() as col: deckNames = [d['name'] for d in col.decks.all()] deckNames.sort() return emit.emitResult(deckNames)
def getNidFromCid(msg): typeCheck(msg, {'cardId': int}) with Col() as col: cid = msg['cardId'] return emit.emitResult(col.getCard(cid).nid)
def getModelList(msg): with Col() as col: return emit.emitResult(col.models.allNames())