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)
Exemple #2
0
 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()
Exemple #4
0
 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)
Exemple #9
0
    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
Exemple #10
0
 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)
Exemple #12
0
 def _tmpNote(self):
     nt = self.current_notetype()
     return Note(self.col, nt)
Exemple #13
0
 def bury_note(self, note: Note) -> None:
     self.bury_cards(note.card_ids())
Exemple #14
0
 def addNote(self, note: Note) -> int:
     self.add_note(note, note.model()["did"])
     return len(note.cards())
Exemple #15
0
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
Exemple #16
0
    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)
Exemple #17
0
 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)
Exemple #18
0
 def newNote(self, forDeck: bool = True) -> Note:
     "Return a new note with the current model."
     return Note(self, self.models.current(forDeck))
Exemple #19
0
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
Exemple #20
0
 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
Exemple #21
0
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"])
Exemple #22
0
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
Exemple #23
0
def getRelationsFromNids(nids):
    notes = [Note(mw.col, id=nid) for nid in nids]
    return getRelationsFromNotes(notes)
Exemple #24
0
 def add_note(self, note: Note, deck_id: int) -> None:
     note.id = self.backend.add_note(note.to_backend_note(), deck_id)
Exemple #25
0
    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()
Exemple #26
0
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))
Exemple #27
0
 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)
Exemple #28
0
    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)
Exemple #29
0
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
Exemple #30
0
        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())