Merge pull request #380 from Aquafina-water-bottle/updateNote

Update note
This commit is contained in:
Alexei Yatskov 2023-03-31 19:51:58 -07:00 committed by GitHub
commit 3c98885ae9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 170 additions and 2 deletions

118
README.md
View File

@ -3058,7 +3058,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**:
@ -3105,6 +3105,122 @@ corresponding to when the API was available for use.
``` ```
</details> </details>
#### `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.
<details>
<summary><i>Sample request:</i></summary>
```json
{
"action": "updateNote",
"version": 6,
"params": {
"note": {
"id": 1514547547030,
"fields": {
"Front": "new front content",
"Back": "new back content"
},
"tags": ["new", "tags"]
}
}
}
```
</details>
<details>
<summary><i>Sample result:</i></summary>
```json
{
"result": null,
"error": null
}
```
</details>
#### `updateNoteTags`
* Set a note's tags by note ID. Old tags will be removed.
<details>
<summary><i>Sample request:</i></summary>
```json
{
"action": "updateNoteTags",
"version": 6,
"params": {
"note": 1483959289817,
"tags": ["european-languages"]
}
}
```
</details>
<details>
<summary><i>Sample result:</i></summary>
```json
{
"result": null,
"error": null
}
```
</details>
#### `getNoteTags`
* Get a note's tags by note ID.
<details>
<summary><i>Sample request:</i></summary>
```json
{
"action": "getNoteTags",
"version": 6,
"params": {
"note": 1483959289817
}
}
```
</details>
<details>
<summary><i>Sample result:</i></summary>
```json
{
"result": ["european-languages"],
"error": null
}
```
</details>
#### `addTags` #### `addTags`
* Adds tags to notes by note ID. * Adds tags to notes by note ID.

View File

@ -819,6 +819,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")]