How to change the type of a note?

What is the proper way to change the note type of a card from within an add-on? Is there an example for that?

See how it’s done by Anki here:

1 Like

Thanks! It pushed me in the right direction!

I still can’t figure it out. This is my current code:

change_notetype_info = mw.col.models.change_notetype_info(
	old_notetype_id=old_notetype_id,
	new_notetype_id=new_notetype_id,
)

note_ids: Sequence[NoteId] = []
# Fill the list ...

req = ChangeNotetypeRequest()
req.ParseFromString(change_notetype_info.SerializeToString())
req.note_ids.extend(note_ids)

mw.col.models.change_notetype_of_notes(req)

But the last line triggers an error: anki.errors.BackendError: ProtoError { info: “failed to decode Protobuf message: ChangeNotetypeRequest.old_notetype_id: invalid wire type: LengthDelimited (expected Varint)” }

Any ideas?

By the way, the docstring for change_notetype_request() does not mention that you have to serialize the argument to ParseFromString().

Looks like this is my bad. The note_ids I pass look suspicious. I’ll come back, when I’ve found the reason.

The note_ids are okay. The “suspicious” ones were were added by the ParseFromString() call. I have checked both notetype ids. They both belong to existing models/note types. I have checked every note in note_ids. They exist and are using the model with old_notetype_id.

Why does this fail then?

It appears that the example given in the docstring no longer works after some changes to the protobuf structure. This example works for me:

def new_names_to_indices(new_names: list[str], old_names: list[str]) -> list[int]:
    indices = []
    for name in new_names:
        try:
            indices.append(old_names.index(name))
        except ValueError:
            indices.append(-1)
    return indices

basic = mw.col.models.by_name("Basic")
basic_reversed = mw.col.models.by_name("Basic (and reversed card)")
info = mw.col.models.change_notetype_info(
    old_notetype_id=basic["id"], new_notetype_id=basic_reversed["id"]
)
req = ChangeNotetypeRequest()
req.new_fields.extend(
    new_names_to_indices(list(info.new_field_names), list(info.old_field_names))
)
req.new_templates.extend(
    new_names_to_indices(
        list(info.new_template_names), list(info.old_template_names)
    )
)
req.old_notetype_id = basic["id"]
req.new_notetype_id = basic_reversed["id"]
req.current_schema = mw.col.db.scalar("select scm from col")
req.old_notetype_name = basic["name"]
req.is_cloze = basic["type"] == 1 or basic_reversed["type"] == 1
req.note_ids.extend([1720992551032])
mw.col.models.change_notetype_of_notes(req)

I’ll look into sending a PR to fix the docs and maybe add a helper function to do the conversion.

1 Like

Thank you! That is very much appreciated.

Was that maybe this commit? Merging Notetypes on Import (#2612) · ankitects/anki@14de845 · GitHub

I guess the minimum required version of my add-on will be the release containing the change.

My guess is that it’s this commit: Change Notetype UI Rework (#1499) · ankitects/anki@6809208 · GitHub

It adds an additional field to ChangeNotetypeInfo.

2 Likes

My example above can be simplified a lot:

basic = mw.col.models.by_name("Basic")
basic_reversed = mw.col.models.by_name("Basic (and reversed card)")
info = mw.col.models.change_notetype_info(
    old_notetype_id=basic["id"], new_notetype_id=basic_reversed["id"]
)
req = info.input
req.note_ids.extend([1720992551032])
mw.col.models.change_notetype_of_notes(req)
2 Likes

I can confirm that the complicated version works like a charm. But I will try the simplification, too, and then mark one of your replies as the solution. Thanks a lot again!

1 Like

The simplified version is also slightly better than the long version because it also fills the “Sort Field”.