Add getNoteTags, updateNoteTags and updateNote

getNoteTags returns [str ...] of tags for a given note (note: int)

updateNoteTags removes the existing tags and sets new tags

updateNote combines the old updateNoteFields and the new updateNoteTags

Closes #369
This commit is contained in:
introt 2022-12-30 13:13:31 +02:00
parent 7234914d44
commit 2f50c747fb
3 changed files with 146 additions and 2 deletions

View File

@ -2533,7 +2533,7 @@ corresponding to when the API was available for use.
* **updateNoteFields** * **updateNoteFields**
Modify the fields of an exist note. You can also include audio, video, or picture files which will be added to the note with an Modify the fields of an existing note. You can also include audio, video, or picture files which will be added to the note with an
optional `audio`, `video`, or `picture` property. Please see the documentation for `addNote` for an explanation of objects in the `audio`, `video`, or `picture` array. optional `audio`, `video`, or `picture` property. Please see the documentation for `addNote` for an explanation of objects in the `audio`, `video`, or `picture` array.
> **Warning** > **Warning**
@ -2574,6 +2574,98 @@ corresponding to when the API was available for use.
} }
``` ```
* **updateNote**
Modify the fields and/or tags of an existing note.
In other words, combines `updateNoteFields` and `updateNoteTags`.
Please see their documentation for an explanation of all properties.
Either `fields` or `tags` property can be omitted without affecting the other.
Thus valid requests to `updateNoteFields` also work with `updateNote`.
The note must have the `fields` property in order to update the optional audio, video, or picture objects.
If neither `fields` nor `tags` are provided, the method will fail.
Fields are updated first and are not rolled back if updating tags fails.
Tags are not updated if updating fields fails.
> **Warning**
> You must not be viewing the note that you are updating on your Anki browser, otherwise
> the fields will not update. See [this issue](https://github.com/FooSoft/anki-connect/issues/82)
> for further details.
*Sample request*:
```json
{
"action": "updateNote",
"version": 6,
"params": {
"note": {
"id": 1514547547030,
"fields": {
"Front": "new front content",
"Back": "new back content"
},
"tags": ["new", "tags"]
}
}
}
```
*Sample result*:
```json
{
"result": null,
"error": null
}
```
* **updateNoteTags**
Set a note's tags by note ID. Old tags will be removed.
*Sample request*:
```json
{
"action": "updateNoteTags",
"version": 6,
"params": {
"note": 1483959289817,
"tags": ["european-languages"]
}
}
```
*Sample result*:
```json
{
"result": null,
"error": null
}
```
* **getNoteTags**
Get a note's tags by note ID.
*Sample request*:
```json
{
"action": "getNoteTags",
"version": 6,
"params": {
"note": 1483959289817,
}
}
```
*Sample result*:
```json
{
"result": ["european-languages"],
"error": null
}
```
* **addTags** * **addTags**
Adds tags to notes by note ID. Adds tags to notes by note ID.

View File

@ -811,6 +811,37 @@ class AnkiConnect:
self.stopEditing() self.stopEditing()
@util.api()
def updateNote(self, note):
updated = False
if 'fields' in note.keys():
self.updateNoteFields(note)
updated = True
if 'tags' in note.keys():
self.updateNoteTags(note['id'], note['tags'])
updated = True
if not updated:
raise Exception('Must provide a "fields" or "tags" property.')
@util.api()
def updateNoteTags(self, note, tags):
if type(tags) == str:
tags = [tags]
if type(tags) != list or not all([type(t) == str for t in tags]):
raise Exception('Must provide tags as a list of strings')
for old_tag in self.getNoteTags(note):
self.removeTags([note], old_tag)
for new_tag in tags:
self.addTags([note], new_tag)
@util.api()
def getNoteTags(self, note):
return self.getNote(note).tags
@util.api() @util.api()
def addTags(self, notes, tags, add=True): def addTags(self, notes, tags, add=True):
self.startEditing() self.startEditing()

View File

@ -98,6 +98,12 @@ class TestTags:
ac.clearUnusedTags() ac.clearUnusedTags()
assert ac.getTags() == ["tag1"] assert ac.getTags() == ["tag1"]
def test_updateNoteTags_and_getNoteTags(self, setup):
ac.updateNoteTags(note=setup.note1_id, tags="footag")
assert ac.getNoteTags(note=setup.note1_id) == ["footag"]
ac.updateNoteTags(note=setup.note1_id, tags=["foo", "bar", "baz"])
assert len(ac.getNoteTags(note=setup.note1_id)) == 3
class TestUpdateNoteFields: class TestUpdateNoteFields:
def test_updateNoteFields(self, setup): def test_updateNoteFields(self, setup):
@ -107,12 +113,27 @@ class TestUpdateNoteFields:
notes_info = ac.notesInfo(notes=[setup.note1_id]) notes_info = ac.notesInfo(notes=[setup.note1_id])
assert notes_info[0]["fields"]["field2"]["value"] == "bar" assert notes_info[0]["fields"]["field2"]["value"] == "bar"
def test_updateNoteFields_will_note_update_invalid_notes(self, setup): def test_updateNoteFields_will_not_update_invalid_notes(self, setup):
bad_note = {"id": 123, "fields": make_note()["fields"]} bad_note = {"id": 123, "fields": make_note()["fields"]}
with pytest.raises(NotFoundError): with pytest.raises(NotFoundError):
ac.updateNoteFields(note=bad_note) ac.updateNoteFields(note=bad_note)
class TestUpdateNote:
def test_updateNote(self, setup):
new_fields = {"field1": "frontbar", "field2": "backbar"}
new_tags = ["foobar"]
good_note = {"id": setup.note1_id, "fields": new_fields, "tags": new_tags}
ac.updateNote(note=good_note)
notes_info = ac.notesInfo(notes=[setup.note1_id])
assert notes_info[0]["fields"]["field2"]["value"] == "backbar"
assert notes_info[0]["tags"] == ["foobar"]
def test_updateNote_requires_either_fields_or_tags(self, setup):
with pytest.raises(Exception, match="ust provide"):
ac.updateNote(note={"id": setup.note1_id})
class TestCanAddNotes: class TestCanAddNotes:
foo_bar_notes = [make_note(front="foo"), make_note(front="bar")] foo_bar_notes = [make_note(front="foo"), make_note(front="bar")]