def _save_mask_and_add_note(self, note_number, mask): mask_path = self._save_mask(mask, note_number) editor_note = self.current_editor.note #see anki.collection._Collection#_newCard model = mw.col.models.byName('PicOcc') #Have to do this as not corrent if first card # editor_note.model()['did'] did = self.current_editor.parentWindow.deckChooser.selectedId() model['did'] = did def field_content(field_name): return editor_note[field_name] if field_name in editor_note else u'' new_note = Note(mw.col, model) def fname2img(path): return '<img src="%s" />' % os.path.split(path)[1] #This is all highly specific to my own workflow/note models etc #but it should not break extra_field = field_content(u'Extra') if self.kbd_modifiers["ctrl"] else u'' intro_field = editor_note.fields[0] if self.kbd_modifiers["shift"] else u'' new_note.fields = [('%s' % new_note.id), fname2img(self.image_path), extra_field, field_content(u'Context'), intro_field, field_content(u'RefLink'), fname2img(mask_path)] new_note.tags = copy.copy(editor_note.tags) mw.col.addNote(new_note)
def newNote(seq, contexts, line): n = Note(self.mw.col, self.mw.col.models.byName(lpcg_models.NAME)) n.model()['did'] = self.deckChooser.selectedId() n.tags = tags n['Title'] = title n['Sequence'] = str(seq) n['Context'] = ''.join("<p>%s</p>" % i for i in contexts) n['Line'] = line self.mw.col.addNote(n)
def duplicate_selected_note(browser): assert(len(browser.selectedNotes()) == 1) base_note = browser.card.note() base_did = browser.card.did new_note = Note(mw.col, base_note.model()) #self.id = timestampID(col.db, "notes") t = base_note.id + 1 while mw.col.db.scalar("select id from %s where id = ?" % "notes", t): t += 1 new_note.id = t #No change needed: self.guid = guid64() #No change needed: self._model = model #No change needed: self.mid = model['id'] new_note.tags = copy.copy(base_note.tags) #self.tags = [] new_note.fields = copy.copy(base_note.fields) #self.fields = [""] * len(self._model['flds']) new_note.flags = base_note.flags #self.flags = 0 new_note.data = base_note.data #self.data = "" #No change needed: self._fmap = self.col.models.fieldMap(self._model) #No change needed: self.scm = self.col.scm new_note.addTag(u'Copied') browser.model.beginReset() #The cards will be added to the deck set into the current template. #Changing the template's deck would be unexpected, # so change the decks manually cards = mw.col.addNote(new_note) for card in new_note.cards(): if card.did != base_did: card.did = base_did card.flush() browser.model.endReset()
def newNote(seq, context1, context2, line): n = Note(self.mw.col, self.mw.col.models.byName(lpcg_models.NAME)) n.model()['did'] = self.deckChooser.selectedId() n.tags = tags n['Title'] = title n['Sequence'] = unicode(seq) if context2: n['Context'] = "<p>%s</p><p>%s</p>" % (context1, context2) else: n['Context'] = "<p>%s</p>" % context1 n['Line'] = line self.mw.col.addNote(n)
def add_note(self, data): from anki.notes import Note model = self.collection.models.byName(data['model']) note = Note(self.collection, model) for name, value in data['fields'].items(): note[name] = value if data.has_key('tags'): note.setTagsFromStr(data['tags']) self.collection.addNote(note)
def add_note(self, col, req): from anki.notes import Note # TODO: I think this would be better with 'model' for the name # and 'mid' for the model id. if type(req.data['model']) in (str, unicode): model = col.models.byName(req.data['model']) else: model = col.models.get(req.data['model']) note = Note(col, model) for name, value in req.data['fields'].items(): note[name] = value if req.data.has_key('tags'): note.setTagsFromStr(req.data['tags']) col.addNote(note)
def __init__(self, collection, note_id_list, old_model=None, parent=None): QDialog.__init__(self, parent) self.collection = collection self.note_id_list = note_id_list self.old_model = old_model if self.old_model is None: first_note = Note(collection, id=note_id_list[0]) self.old_model = first_note.model() # todo consider extracting UI file self.form = aqt.forms.changemodel.Ui_Dialog() self.form.setupUi(self) self.setWindowModality(Qt.WindowModal) self.setup() self.pauseUpdate = False self.model_changed(self.collection.models.current()) aqt.utils.restoreGeom(self, "changeModel") anki.hooks.addHook("reset", self.on_reset) anki.hooks.addHook("currentModelChanged", self.on_reset)
def _saveMaskAndReturnNote(self, omask_path, qmask, amask, img, note_id, nid=None): """Write actual note for given qmask and amask""" fields = self.fields model = self.model mflds = self.mflds fields[self.ioflds['im']] = img if omask_path: # Occlusions updated qmask_path = self._saveMask(qmask, note_id, "Q") amask_path = self._saveMask(amask, note_id, "A") fields[self.ioflds['qm']] = fname2img(qmask_path) fields[self.ioflds['am']] = fname2img(amask_path) fields[self.ioflds['om']] = fname2img(omask_path) fields[self.ioflds['id']] = note_id self.model['did'] = self.did if nid: note = mw.col.getNote(nid) else: note = Note(mw.col, model) # add fields to note note.tags = self.tags for i in mflds: fname = i["name"] if fname in fields: # only update fields that have been modified note[fname] = fields[fname] if nid: note.flush() logging.debug("!noteflush %s", note) else: mw.col.addNote(note) logging.debug("!notecreate %s", note)
def dupNote(self, note, model, deckId): ''' Parameters should be objects (not IDs) Returns the new note, or None on failure ''' mw.col.models.setCurrent(model) # TODO: To really copy the whole thing, not just the data fields, we could # deepcopy the whole note, then get unique IDs by creating a temp Note just to steal its id and guid, # then delete the temp note. (I tried that and couldn't get it to work, so far.) ''' n2tmp = Note(mw.col, model) n2 = copy.deepcopy(note) n2.id = n2tmp.id n2.guid = n2tmp.guid del n2tmp # just being explicit # overwrite model info n2._model = model n2.mid = model['id'] ''' n2 = Note(mw.col, model) assert( len(n2.model()['flds']) >= len(note.fields) ) # copy data n2.fields = copy.deepcopy(note.fields) n2.tags = copy.deepcopy(note.tags) # anything else?? n2.flush(intTime()) #save #n2.load() # Copying its cards technically shouldn't be necessary since they should auto-generate. (Make sure they do? Orphaned notes are inaccessible in Anki's UI.) # It would be nice to copy c.queue , at least if it's -1 (suspended) # But we do need actual cards in order to specify a target deck. cardCount = mw.col.addNote(n2) if cardCount: q = "select distinct cards.id from cards where cards.nid = %s" % (n2.id) cards = mw.col.db.list(q) for cid in cards: c = mw.col.getCard(cid) c.did = deckId # Move card into the target deck c.flush() self.cardsCopied += 1 return n2 self.errors.add('Copy Note failed.') self.errorCount += 1 print "Copy failed for: %s" % note return None
def getNote(self, id: int) -> Note: return Note(self, id=id)
def create(self, text, settings): currentCard = mw.reviewer.card currentNote = currentCard.note() model = mw.col.models.byName(settings['modelName']) newNote = Note(mw.col, model) newNote.tags = currentNote.tags setField(newNote, settings['textField'], fixImages(text)) if settings['extractDeck']: deck = mw.col.decks.byName(settings['extractDeck']) if not deck: showWarning('Destination deck no longer exists. ' 'Please update your settings.') return did = deck['id'] else: did = currentCard.did if settings['isQuickKey']: newNote.tags += settings['tags'] if settings['sourceField']: setField(newNote, settings['sourceField'], getField(currentNote, self.settings['sourceField'])) if settings['editExtract']: highlight = self._editExtract(newNote, did, settings) else: highlight = True newNote.model()['did'] = did mw.col.addNote(newNote) else: if settings['copyTitle']: title = getField(currentNote, settings['titleField']) else: title = '' setField(newNote, settings['sourceField'], getField(currentNote, settings['sourceField'])) if settings['prioEnabled']: setField(newNote, settings['priorityField'], getField(currentNote, settings['priorityField'])) if settings['editExtract']: setField(newNote, settings['titleField'], title) highlight = self._editExtract(newNote, did, settings) else: highlight = self._getTitle(newNote, did, title, settings) if settings['scheduleExtract'] and not settings['prioEnabled']: cards = newNote.cards() if cards: mw.readingManager.scheduler.answer(cards[0], SCHEDULE_EXTRACT) if highlight: self.highlight(settings['extractBgColor'], settings['extractTextColor']) if settings['editSource']: EditCurrent(mw)
def _tmpNote(self): nt = self.current_notetype() return Note(self.col, nt)
def bury_note(self, note: Note) -> None: self.bury_cards(note.card_ids())
def addNote(self, note: Note) -> int: self.add_note(note, note.model()["did"]) return len(note.cards())
def getSelectedNotes(browser): nids = browser.selectedNotes() debug(f"Selected nids are {nids}") notes = {Note(mw.col, id=nid) for nid in nids} debug(f"Selected notes are {notes}") return notes
def save(self, remoteDeckID): col = mw.col model = col.models.get(getLocalModelID(self.get('model'))) if not self.get('localID') and not getLocalNoteID(self.get('creationNote')): # Wurde nicht gerade eben gepusht und ist nicht in der Lokalen Datenbank also neu note = Note(col, model) fields = self.getFields() for key in fields: note.__setitem__(str(key), fields.get(key)) note.flush() col.addNote(note) col.save() localID = note.id elif self.get('localID') and not getLocalNoteID(self.get('creationNote')): # Hat ne lokale Id wurde also eben gepusht aber ist nicht in der Lokalen Datenbank also update # Hier muss noch die remoteID in die datenbank geschrieben werden localID = self.get('localID') print("DO i really need this") else: # Hat nichts von allem also existiert schon also update # get remote id for local localID = getLocalNoteID(self.get('creationNote')) oldNote = Note(col, None, localID) fields = self.getFields() print('test1') for key in fields: print('test2') if key not in oldNote.keys(): print("we changed quite a bit") else: print("test3") print(fields.get(key)) print(oldNote.__getitem__(key)) if not fields.get(key) == oldNote.__getitem__(key): oldNote.__setitem__(key, fields.get(key)) oldNote.flush() col.save() print("we changed it") # if fields.get(key) == #print(oldNote.values()) #print(oldNote.cards()) col.genCards([localID]) col.save() mw.col.db.execute("INSERT OR REPLACE INTO NoteIDs (RemoteID, RemoteDeckID, LocalID) VALUES (?,?,?)", self.get('creationNote'), remoteDeckID, localID)
def manage_template(self, item: SidebarItem) -> None: note = Note(self.col, self.col.models.get(item._parent_item.id)) CardLayout(self.mw, note, ord=item.id, parent=self, fill_empty=True)
def newNote(self, forDeck: bool = True) -> Note: "Return a new note with the current model." return Note(self, self.models.current(forDeck))
class Note(JsonSerializableAnkiObject): export_filter_set = JsonSerializableAnkiObject.export_filter_set | \ {"col", # Don't need collection "_fmap", # Generated data "_model", # Card model. Would be handled by deck. "mid", # -> uuid "scm", # todo: clarify "config", "newlyAdded" } def __init__(self, anki_note=None, config: ConfigSettings = None): super(Note, self).__init__(anki_note) self.note_model_uuid = None self.config = config or ConfigSettings.get_instance() @staticmethod def get_notes_from_collection(collection, deck_id, note_models): note_ids = collection.decks.get_note_ids(deck_id, include_from_dynamic=True) return [ Note.from_collection(collection, note_id, note_models) for note_id in note_ids ] @classmethod def from_collection(cls, collection, note_id, note_models): anki_note = AnkiNote(collection, id=note_id) note = Note(anki_note=anki_note) note_model = NoteModel.from_collection(collection, note.anki_object.mid) note_models.setdefault(note_model.get_uuid(), note_model) note.note_model_uuid = note_model.get_uuid() return note @classmethod def from_json(cls, json_dict): note = Note() note.anki_object_dict = json_dict note.note_model_uuid = json_dict["note_model_uuid"] return note def get_uuid(self): return self.anki_object.guid if self.anki_object else self.anki_object_dict.get( "guid") def handle_model_update(self, collection, model_map_cache): """ Update note's cards if note's model has changed """ old_model_uuid = self.anki_object.model().get(UUID_FIELD_NAME) if self.note_model_uuid == old_model_uuid: return # todo if models semantically identical - create map without calling dialog uuid_fetcher = UuidFetcher(collection) new_model = NoteModel.from_json( uuid_fetcher.get_model(self.note_model_uuid)) mapping = model_map_cache[old_model_uuid].get(self.note_model_uuid) if mapping: collection.models.change(self.anki_object.model(), [self.anki_object.id], new_model.anki_dict, mapping.field_map, mapping.template_map) else: new_model.make_current(collection) # todo signals instead of direct dialog creation? dialog = ChangeModelDialog(collection, [self.anki_object.id], self.anki_object.model()) def on_accepted(): model_map_cache[old_model_uuid][self.note_model_uuid] = \ NoteModel.ModelMap(dialog.get_field_map(), dialog.get_template_map()) dialog.accepted.connect(on_accepted) dialog.exec_() # todo process cancel # To get an updated note to work with self.anki_object = uuid_fetcher.get_note(self.get_uuid()) def move_cards_to_deck(self, deck_id, move_from_dynamic_decks=False): """ Move all cards for note with given id to specified deck. :param deck_id: :param move_from_dynamic_decks: :return: """ # Todo: consider move only when majority of cards are in a different deck. for card in self.anki_object.cards(): card.move_to_deck(deck_id, move_from_dynamic_decks) card.flush() def save_to_collection(self, collection, deck, model_map_cache, import_config): # Todo uuid match on existing notes note_model = deck.metadata.models[self.note_model_uuid] self.anki_object = UuidFetcher(collection).get_note(self.get_uuid()) new_note = self.anki_object is None if new_note: self.anki_object = AnkiNote(collection, note_model.anki_dict) else: self.handle_model_update(collection, model_map_cache) self.handle_import_config_changes(import_config, note_model) self.anki_object.__dict__.update(self.anki_object_dict) self.anki_object.mid = note_model.anki_dict["id"] self.anki_object.mod = anki.utils.intTime() if new_note: collection.add_note(self.anki_object, deck.anki_dict["id"]) else: self.anki_object.flush() if not import_config.ignore_deck_movement: self.move_cards_to_deck(deck.anki_dict["id"]) def handle_import_config_changes(self, import_config, note_model): # Personal Fields for num in range(len(self.anki_object_dict["fields"])): if import_config.is_personal_field( note_model.anki_dict['name'], note_model.anki_dict['flds'][num]['name']): self.anki_object_dict["fields"][num] = self.anki_object.fields[ num] # Tag Cards on Import self.anki_object_dict["tags"] += import_config.add_tag_to_cards
def _note(self, anki_note: AnkiNote) -> Note: note = Note(self.collection, self.model) note.fields[self.content_field_index] = anki_note.content if self.source_field_index is not None: note.fields[self.source_field_index] = anki_note.source return note
class Note(JsonSerializableAnkiObject): export_filter_set = JsonSerializableAnkiObject.export_filter_set | \ {"col", # Don't need collection "_fmap", # Generated data "_model", # Card model. Would be handled by deck. "mid", # -> uuid "scm" # todo: clarify } def __init__(self, anki_note=None): super(Note, self).__init__(anki_note) self.note_model_uuid = None @staticmethod def get_notes_from_collection(collection, deck_id, note_models): note_ids = collection.decks.get_note_ids(deck_id, include_from_dynamic=True) return [ Note.from_collection(collection, note_id, note_models) for note_id in note_ids ] @classmethod def from_collection(cls, collection, note_id, note_models): anki_note = AnkiNote(collection, id=note_id) note = Note(anki_note) note_model = NoteModel.from_collection(collection, note.anki_object.mid) note_models.setdefault(note_model.get_uuid(), note_model) note.note_model_uuid = note_model.get_uuid() return note @classmethod def from_json(cls, json_dict): note = Note() note.anki_object_dict = json_dict note.note_model_uuid = json_dict["note_model_uuid"] return note def get_uuid(self): return self.anki_object.guid if self.anki_object else self.anki_object_dict.get( "guid") def handle_model_update(self, collection, model_map_cache): """ Update note's cards if note's model has changed """ old_model_uuid = self.anki_object.model().get(UUID_FIELD_NAME) if self.note_model_uuid == old_model_uuid: return # todo if models semantically identical - create map without calling dialog new_model = NoteModel.from_json( collection.models.get_by_uuid(self.note_model_uuid)) mapping = model_map_cache[old_model_uuid].get(self.note_model_uuid) if mapping: collection.models.change(self.anki_object.model(), [self.anki_object.id], new_model.anki_dict, mapping.field_map, mapping.template_map) else: new_model.make_current(collection) # todo signals instead of direct dialog creation? dialog = ChangeModelDialog(collection, [self.anki_object.id], self.anki_object.model()) def on_accepted(): model_map_cache[old_model_uuid][self.note_model_uuid] = \ NoteModel.ModelMap(dialog.get_field_map(), dialog.get_template_map()) dialog.accepted.connect(on_accepted) dialog.exec_() # todo process cancel # To get an updated note to work with self.anki_object = AnkiNote.get_by_uuid(collection, self.get_uuid()) def move_cards_to_deck(self, deck_id, move_from_dynamic_decks=False): """ Move all cards for note with given id to specified deck. :param deck_id: :param move_from_dynamic_decks: :return: """ # Todo: consider move only when majority of cards are in a different deck. for card in self.anki_object.cards(): card.move_to_deck(deck_id, move_from_dynamic_decks) card.flush() def save_to_collection(self, collection, deck, model_map_cache): # Todo uuid match on existing notes note_model = deck.metadata.models[self.note_model_uuid] # You may ask WTF? Well, it seems anki has 2 ways to identify deck where to place card: # 1) Looking for existing cards of this note # 2) Looking at model->did and to add new cards to correct deck we need to modify the did each time # ;( note_model.anki_dict["did"] = deck.anki_dict["id"] self.anki_object = AnkiNote.get_by_uuid(collection, self.get_uuid()) new_note = self.anki_object is None if new_note: self.anki_object = AnkiNote(collection, note_model.anki_dict) else: self.handle_model_update(collection, model_map_cache) self.anki_object.__dict__.update(self.anki_object_dict) self.anki_object.mid = note_model.anki_dict["id"] self.anki_object.mod = anki.utils.intTime() self.anki_object.flush() if new_note: collection.addNote(self.anki_object) else: self.move_cards_to_deck(deck.anki_dict["id"])
def mergeNotes(note1, note2): """Merge note1 and note2 following the configuration rules. If they have distinct note type, fails.""" mw.checkpoint("Merge Notes") model1 = note1.model() model2 = note2.model() if model1 != model2: showWarning(_("Please select notes of the same type to merge them")) return merged_note = Note(mw.col, note1.model()) new_nid = merge_id(note1, note2) weak = maybeGetWeakNote(note1, note2) overwrite_patterns = [ re.compile(p) for p in (getUserOption("Overwrite patterns", []) or []) ] for i in range(len(merged_note.fields)): if maybeOverwriteField(overwrite_patterns, i, note1, note2): continue elif note1 == weak: merged_note.fields[i] = note2.fields[ i] if note2.fields[i] != "" else note1.fields[i] elif note2 == weak: merged_note.fields[i] = note1.fields[ i] if note1.fields[i] != "" else note2.fields[i] elif note1.fields[i] != note2.fields[i] or not getUserOption( "When identical keep a single field", True): merged_note.fields[i] = note1.fields[i] + note2.fields[i] else: # identical merged_note.fields[i] = note1.fields[i] cards = dict() # tags merged_note.addTag(note1.stringTags()) merged_note.addTag(note2.stringTags()) merged_note.addTag(f"merged merged_{note1.id} merged_{note2.id}") # Choosing which card to keep cards_to_delete = [] for card1 in note1.cards(): cards[card1.ord] = card1 for card2 in note2.cards(): ord = card2.ord card1 = cards.get(ord) if card1 is None or card1.type == CARD_NEW or card1.ivl < card2.ivl or ( card1.ivl == card2.ivl and card1.factor < card2.factor): cards[ord] = card2 cards_to_delete.append(card1) else: cards_to_delete.append(card2) if getUserOption("Delete original cards", False): mw.col._remNotes([note1.id]) mw.col._remNotes([note2.id]) mw.col.remCards( [card.id for card in cards_to_delete if card is not None], notes=False) for card in cards.values(): if card is None: continue if not getUserOption("Delete original cards", False): tmp_card = Card(mw.col) card.id = tmp_card.id card.nid = new_nid card.flush() mw.col.add_note(merged_note, 1) tmp_nid = merged_note.id mw.col.db.execute(""" update notes set id=? where id=? """, new_nid, merged_note.id) mw.col.db.execute(""" delete from cards where nid=? """, tmp_nid) merged_note.id = new_nid tooltip(_("Notes merged")) return merged_note
def getRelationsFromNids(nids): notes = [Note(mw.col, id=nid) for nid in nids] return getRelationsFromNotes(notes)
def add_note(self, note: Note, deck_id: int) -> None: note.id = self.backend.add_note(note.to_backend_note(), deck_id)
def doImport(self): logDebug('beginning import') currentProfileDeckId = self.currentProfileDeckCombo.itemData( self.currentProfileDeckCombo.currentIndex()) logDebug('current profile deck id %d' % currentProfileDeckId) # get the note ids of all selected notes noteIds = [ self.noteListModel.itemFromIndex(idx).data() for idx in self.noteListView.selectedIndexes() ] # clear the selection self.noteListView.clearSelection() logDebug('importing %d notes' % len(noteIds)) statSuccess = 0 statNoMatchingModel = 0 statDupe = 0 for nid in noteIds: # load the note logDebug('import note id %d' % nid) otherNote = self.otherProfileCollection.getNote(nid) # find the model name of the note modelName = otherNote._model['name'] logDebug('model name %r' % modelName) # find a model in current profile that matches the name of model from other profile matchingModel = mw.col.models.byName(modelName) if matchingModel: # TODO: ensure that field map is same between two models (or same length?), otherwise skip or error? logDebug('matching model found, id %s' % matchingModel['id']) else: logDebug('no matching model, copying') copiedModel = deepcopy( otherNote._model ) # do deep copy just to be safe. model is a dict, but might be nested copiedModel['id'] = None mw.col.models.add(copiedModel) matchingModel = copiedModel # create a new note object newNote = Note(mw.col, matchingModel) logDebug('new note %s %s' % (newNote.id, newNote.mid)) # set the deck that the note will generate cards into newNote.model()['did'] = currentProfileDeckId # copy field values into new note object newNote.fields = otherNote.fields[:] # list of strings, so clone it # check if note is dupe of existing one if newNote.dupeOrEmpty(): logDebug('dupe') statDupe += 1 continue # check if there are any media files referenced by the note mediaFiles = self.otherProfileCollection.media.filesInStr( otherNote.mid, otherNote.joinedFields()) logDebug('mediaFiles %s' % (mediaFiles, )) for fn in mediaFiles: fullfn = os.path.join(self.otherProfileCollection.media.dir(), fn) # referenced media might not exist, in which case we skip it if not os.path.exists(fullfn): continue logDebug('copying from %s' % fullfn) addedFn = mw.col.media.addFile(fullfn) # NOTE: addedFn may differ from fn (name conflict, different contents), in which case we need to update the note. if addedFn != fn: logDebug('name conflict') newNote.fields = [ f.replace(fn, addedFn) for f in newNote.fields ] addedCardCount = mw.col.addNote(newNote) statSuccess += 1 if statSuccess: mw.requireReset() if statSuccess: self.statSuccessLabel.setText('%d notes successfully imported' % statSuccess) self.statSuccessLabel.show() else: self.statSuccessLabel.hide() if statNoMatchingModel: self.statNoMatchingModelLabel.setText( '%d notes failed to import because there is no matching Note Type in the current profile' % statNoMatchingModel) self.statNoMatchingModelLabel.show() else: self.statNoMatchingModelLabel.hide() if statDupe: self.statDupeLabel.setText( '%d notes were duplicates, and skipped' % statDupe) self.statDupeLabel.show() else: self.statDupeLabel.hide()
def add_notes(words): if not mw.col.conf.get(u'searchedict_hasopensettings'): showInfo(u'Please check the settings first') SettingsWindow.open() return # select deck deck_name = mw.col.conf.get('searchedict_deck') if deck_name is None: deck_id = mw.col.decks.selected() else: deck_id = mw.col.decks.id(deck_name) deck = mw.col.decks.get(deck_id) if deck is None: showInfo('Deck not found') return # select model try: model_name = mw.col.conf['searchedict_model'] except KeyError: showInfo('Note type is not set') return model = mw.col.models.by_name(model_name) if model is None: showInfo('Note type not found') return model['did'] = deck['id'] # update model's default deck # check fields if not check_field(model, 'searchedict_kanjiField'): return if not check_field(model, 'searchedict_kanaField'): return if not check_field(model, 'searchedict_furiganaField'): return if not check_field(model, 'searchedict_definitionField'): return if not check_field(model, 'searchedict_idField'): return n_newcards = 0 for word in words: # create new note note = Note(mw.col, model) # fill new note note_set_field(note, 'searchedict_kanjiField', word.kanji) note_set_field(note, 'searchedict_kanaField', word.kana) note_set_field(note, 'searchedict_furiganaField', word.get_furigana()) note_set_field(note, 'searchedict_definitionField', word.get_meanings_html()) note_set_field(note, 'searchedict_idField', word.get_sequence_number()) # check for duplicates if id field is set idfield = mw.col.conf.get('searchedict_idField') if idfield is not None and mw.col.find_notes( f'{idfield}:{word.get_sequence_number()}'): continue # add card n_newcards += mw.col.addNote(note) mw.reset() tooltip( ngettext('{} card added.', '{} cards added.', n_newcards).format(n_newcards))
def findTemplates(self, note: Note) -> List: "Return (active), non-empty templates." model = note.model() avail = self.models.availOrds(model, joinFields(note.fields)) return self._tmplsFromOrds(model, avail)
def check(self, local=None): "Return (missingFiles, unusedFiles)." mdir = self.dir() # gather all media references in NFC form allRefs = set() refsToNid = dict() # this dic is new for nid, mid, flds in self.col.db.execute( "select id, mid, flds from notes"): noteRefs = self.filesInStr(mid, flds) # check the refs are in NFC for f in noteRefs: # if they're not, we'll need to fix them first if f != unicodedata.normalize("NFC", f): self._normalizeNoteRefs(nid) noteRefs = self.filesInStr(mid, flds) break # new. update refsToNid for f in noteRefs: if f not in refsToNid: refsToNid[f] = set() refsToNid[f].add(nid) # end new allRefs.update(noteRefs) # loop through media folder unused = [] if local is None: files = os.listdir(mdir) else: files = local renamedFiles = False dirFound = False warnings = [] for file in files: if not local: if not os.path.isfile(file): if self.processSubdir: dir = file path = [dir + '/' + f for f in os.listdir(dir)] files.extend(path) else: # ignore directories dirFound = True continue if file.startswith("_"): # leading _ says to ignore file continue if self.hasIllegal(file): name = file.encode(sys.getfilesystemencoding(), errors="replace") name = str(name, sys.getfilesystemencoding()) warnings.append( _("Invalid file name, please rename: %s") % name) continue nfcFile = unicodedata.normalize("NFC", file) # we enforce NFC fs encoding on non-macs if not isMac and not local: if file != nfcFile: # delete if we already have the NFC form, otherwise rename if os.path.exists(nfcFile): os.unlink(file) renamedFiles = True else: os.rename(file, nfcFile) renamedFiles = True file = nfcFile # compare if nfcFile not in allRefs: unused.append(file) else: allRefs.discard(nfcFile) # if we renamed any files to nfc format, we must rerun the check # to make sure the renamed files are not marked as unused if renamedFiles: return self.check(local=local) #This line was removed in the addon, but was causing problems for #_ files, so it's added it back in for now. nohave = [x for x in allRefs if not x.startswith("_") and not isURL(x)] # NEW: A line here removed because it was a bug # New finder = Finder(self.col) alreadyMissingNids = finder.findNotes("tag:MissingMedia") nidsOfMissingRefs = set() for ref in nohave: nidsOfMissingRefs.update(refsToNid[ref]) #print(f"nidsOfMissingRefs is now {nidsOfMissingRefs}") for nid in nidsOfMissingRefs: if nid not in alreadyMissingNids: # print(f"missing nid {nid}") note = Note(self.col, id=nid) note.addTag("MissingMedia") note.flush() for nid in alreadyMissingNids: if nid not in nidsOfMissingRefs: # print(f"not missing anymore nid {nid}") note = Note(self.col, id=nid) note.delTag("MissingMedia") note.flush() # end new # make sure the media DB is valid try: self.findChanges() except DBError: self._deleteDB() if not self.processSubdir and dirFound: warnings.append( _("Anki does not support files in subfolders of the collection.media folder." )) return (nohave, unused, warnings)
def getNotesFromRelations(relations): notes = {Note(mw.col, id=nid) for nid in getNidsFromRelations(relations)} debug(f"from relations {relations} we get notes {notes}") return notes
my_sol_raw = open(my_sol_fn, encoding="utf-8").read() my_sol_html = "自己答案<br>" + my_sol_raw answer_list = [my_sol_html] + official_answer_list else: is_my_sol = False answer_list = official_answer_list answer = md_util.md_convert("\r\n<hr>答案分隔线<hr>\r\n".join(answer_list)) # 答案可能会变化,但文件名和问题会相对固定,所以以这两者取 guid guid = util.java_string_hashcode(deck_name + "|" + problem_slug) item_id = util.get_item_id(col, guid) fields0 = question fields1 = answer if item_id > 0: note = Note(col, id=item_id) old_tags = deepcopy(note.tags) if is_my_sol: note.addTag("mysol") for tag in tags: note.addTag(tag) if old_tags != note.tags: note.flush() if not (note.fields[0] == fields0 and note.fields[1] == fields1): note.guid = guid note.model()['did'] = deck['id'] note.fields[0] = fields0 note.fields[1] = fields1 note.flush(int(time.time())) # 如果没有此 item 则建立新的 item else:
def keys_to_update(self, note: Note) -> Iterable[str]: if not self.selected_fields: return note.keys() else: return filter(lambda field: field in self.selected_fields, note.keys())