Add new notes / update existing notes

Hi,

I wonder how I can add a new Note with an id or update an existing Note (in case the id is taken).

For a new Note I came up with something like:

deckId = 42
noteId = 42

notetype = self.col.models.byName("Basic")
note = self.col.new_note(notetype)
note.guid = str(noteId)
note["Front"] = "Question"
note["Back"] = "Answer
self.col.add_note(note, deckId)

Here I’m not quite sure if “guid” is correct. What is the best way to set an id?

For getting an existing Note I can probably use get_note(self, id: NoteId) -> Note and then update_note(self, note: Note) -> OpChanges.

The first functions fails though, if the note does not exist, therefore I need a way to check that.

So how can I:

  • Give a Note an unique identifier?
  • Know if a Note with some ID already exists?

Thanks in advance!

Anki takes care of giving new notes unique IDs. Could you explain why you need to set it explicitly?

get_note throws the anki.errors.NotFoundError exception if a note with the given ID doesn’t exist.

GUID is different from the note ID by the way. The note ID field is actually note.id.
I guess the GUID is mainly intended for internal use by Anki when importing/exporting, and I’m not sure setting it manually is a good idea.

1 Like

Because I write an extension for importing notes and I want to be able to reimport Flashcards.
Currently I use the txt Importer and set the first field as my id.
I guess that would be possible here to (although I‘m not sure how), but having some internal field would be even better!

I thought about that, but wasn‘t sure if that‘s the way this should be done. But I guess this depends on question 1 anyway (Can I set this id)

I couldn‘t set it though. Anki expected it to be 0? Not quite sure I understood how it works.

Btw. I‘m using the latest master as it seems some useful functions has beend added quite recently.
Just in case that matters.

The import/export code is one of the remaining things that is in need of a refactor. You can set an arbitrary GUID if you need to keep track of a given note for now, but you’ll need to manually query the DB for find a note with a given GUID, and you may find it easier just to store the ID in a field instead.

1 Like

Is this detection for creating a new note or updating it when first field matches built into Note? Or how would I create / update the card with the provided functions in collection?

You can see how addcards.py does it for individual notes

Thanks for the hint!

Based on what I found, I came up with this:

note = self.col.new_note(notetype)
... 
if note.duplicate_or_empty() == DuplicateOrEmptyResult.DUPLICATE:
  note.id = self.col.find_notes(self.col.build_search_string(note["id"], SearchNode(field_name="id")))[0]
  self.col.update_note(note)
else: 
  self.col.add_note(note, deckId)

There is one problem with this though. It seems like the deck information is lost after using update_note(). When I update the notes, they all land in the default deck rather than the deck where they were before. Is this intentional? What can I do against this?

There is also a strange thing I noticed with NoteTypes. When I change the NoteType by adding new fields / replacing fields with those anki/models.py at 42a4d11416d6b04e49cc05d9d2b7db9f8e19b1d8 · ankitects/anki · GitHub, the ordinal number is not set.
This causes duplicate_or_empty() to fail.

My workaround:

    def _addField(self, notetype, name: str):
        field = self.col.models.new_field(name)
        field["ord"] = len(notetype["flds"])
        self.col.models.add_field(notetype, field)

Furthermore I wonder why that matters at all to duplicate_or_empty(). This should just compare the note and not the notetype. The issue is also just with these ordinal numers for fields. Based on my tests, there is no problem with adding new fields (as long as they get an ordinal number as well) and also no problem with templates (which sets ordinal number to None as well).

You should not be setting the ordinals manually, you should save() the notetype and then load it again to get the assigned values if you’re planning to add material immediately afterwards. Manually assigning an id also looks wrong; you should load the existing note instead of assigning its id to a different note object.

This is what I did originally. The problem is that when I change a notetype, it causes duplicate_or_empty to return 0 for existing cards.

A simple example. Say I have a Notetype “MyCustomNoteType”. So I do retrieve it:

notetype = self.col.models.byName("MyCustomNoteType")

Then I try to detect if a Note already exists, so I construct a Note Object with an id as the first field

note = self.col.new_note(notetype)
note["id"] = "This is the first field and a note with such field exists"

I detect a change:

if note.duplicate_or_empty() == DuplicateOrEmptyResult.DUPLICATE:
  print("Note exists")

So far this works. But If I change the Nottype in certain ways, it causes duplicte_or_empty() to return 0, so no duplicate.
What I can do:

notetype["tmpls"] = [] 
self.col.models.add_template(notetype, self.col.models.new_template("Card 1"))
self.col.models.save(notetype)

So remove all templates and add exact the same templates. It works.

What I can’t do

notetype["flds"] = [] 
field = self.col.models.new_field("id")
self.col.models.add_field(notetype, field)
self.col.models.save(notetype)

So removing all fields and adding exact the same fields.

So I try to search what the difference is: The ordinal numbers are None

But in general: I don’t understand what the notetype has to do with duplicate_or_empty for the note. If the note doesn’t change (same fields, same values, same id in first field, same notetype Id) that function should return duplicate. If the Notetype gets a new field and the note gets a new field, it should still be a duplicate, because the first field matches.

I figure I might just wanna search myself for this. So something like:

noteIds = self.col.find_notes(self.col.build_search_string("Here my duplicate id", SearchNode(field_name="id")))
if noteIds == []: 
  print("No Duplicate")
else 
  print("Duplicate")

Edit: Turns out this doesn’t work either when I modify the notetype in any way.

I think you’re right. I thought as I constructed the note Id with all fields before, I could just copy the id over. But obviously there are more fields like the usn. So yes I should load the original note and modify that.
I’ll see if that also fixes my issue.