Skip to content
This repository was archived by the owner on Nov 4, 2025. It is now read-only.

Commit 3c98885

Browse files
authored
Merge pull request #380 from Aquafina-water-bottle/updateNote
Update note
2 parents bf5c73b + ed508ba commit 3c98885

3 files changed

Lines changed: 170 additions & 2 deletions

File tree

README.md

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3058,7 +3058,7 @@ corresponding to when the API was available for use.
30583058
30593059
#### `updateNoteFields`
30603060
3061-
* 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
3061+
* 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
30623062
optional `audio`, `video`, or `picture` property. Please see the documentation for `addNote` for an explanation of objects in the `audio`, `video`, or `picture` array.
30633063
30643064
> **Warning**:
@@ -3105,6 +3105,122 @@ corresponding to when the API was available for use.
31053105
```
31063106
</details>
31073107
3108+
#### `updateNote`
3109+
3110+
* Modify the fields and/or tags of an existing note.
3111+
In other words, combines `updateNoteFields` and `updateNoteTags`.
3112+
Please see their documentation for an explanation of all properties.
3113+
3114+
Either `fields` or `tags` property can be omitted without affecting the other.
3115+
Thus valid requests to `updateNoteFields` also work with `updateNote`.
3116+
The note must have the `fields` property in order to update the optional audio, video, or picture objects.
3117+
3118+
If neither `fields` nor `tags` are provided, the method will fail.
3119+
Fields are updated first and are not rolled back if updating tags fails.
3120+
Tags are not updated if updating fields fails.
3121+
3122+
> **Warning**
3123+
> You must not be viewing the note that you are updating on your Anki browser, otherwise
3124+
> the fields will not update. See [this issue](https://github.com/FooSoft/anki-connect/issues/82)
3125+
> for further details.
3126+
3127+
<details>
3128+
<summary><i>Sample request:</i></summary>
3129+
3130+
```json
3131+
{
3132+
"action": "updateNote",
3133+
"version": 6,
3134+
"params": {
3135+
"note": {
3136+
"id": 1514547547030,
3137+
"fields": {
3138+
"Front": "new front content",
3139+
"Back": "new back content"
3140+
},
3141+
"tags": ["new", "tags"]
3142+
}
3143+
}
3144+
}
3145+
```
3146+
3147+
</details>
3148+
3149+
<details>
3150+
<summary><i>Sample result:</i></summary>
3151+
3152+
```json
3153+
{
3154+
"result": null,
3155+
"error": null
3156+
}
3157+
```
3158+
3159+
</details>
3160+
3161+
#### `updateNoteTags`
3162+
3163+
* Set a note's tags by note ID. Old tags will be removed.
3164+
3165+
<details>
3166+
<summary><i>Sample request:</i></summary>
3167+
3168+
```json
3169+
{
3170+
"action": "updateNoteTags",
3171+
"version": 6,
3172+
"params": {
3173+
"note": 1483959289817,
3174+
"tags": ["european-languages"]
3175+
}
3176+
}
3177+
```
3178+
3179+
</details>
3180+
3181+
<details>
3182+
<summary><i>Sample result:</i></summary>
3183+
3184+
```json
3185+
{
3186+
"result": null,
3187+
"error": null
3188+
}
3189+
```
3190+
3191+
</details>
3192+
3193+
#### `getNoteTags`
3194+
3195+
* Get a note's tags by note ID.
3196+
3197+
<details>
3198+
<summary><i>Sample request:</i></summary>
3199+
3200+
```json
3201+
{
3202+
"action": "getNoteTags",
3203+
"version": 6,
3204+
"params": {
3205+
"note": 1483959289817
3206+
}
3207+
}
3208+
```
3209+
3210+
</details>
3211+
3212+
<details>
3213+
<summary><i>Sample result:</i></summary>
3214+
3215+
```json
3216+
{
3217+
"result": ["european-languages"],
3218+
"error": null
3219+
}
3220+
```
3221+
3222+
</details>
3223+
31083224
#### `addTags`
31093225
31103226
* Adds tags to notes by note ID.

plugin/__init__.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,37 @@ def updateNoteFields(self, note):
819819
self.stopEditing()
820820

821821

822+
@util.api()
823+
def updateNote(self, note):
824+
updated = False
825+
if 'fields' in note.keys():
826+
self.updateNoteFields(note)
827+
updated = True
828+
if 'tags' in note.keys():
829+
self.updateNoteTags(note['id'], note['tags'])
830+
updated = True
831+
if not updated:
832+
raise Exception('Must provide a "fields" or "tags" property.')
833+
834+
835+
@util.api()
836+
def updateNoteTags(self, note, tags):
837+
if type(tags) == str:
838+
tags = [tags]
839+
if type(tags) != list or not all([type(t) == str for t in tags]):
840+
raise Exception('Must provide tags as a list of strings')
841+
842+
for old_tag in self.getNoteTags(note):
843+
self.removeTags([note], old_tag)
844+
for new_tag in tags:
845+
self.addTags([note], new_tag)
846+
847+
848+
@util.api()
849+
def getNoteTags(self, note):
850+
return self.getNote(note).tags
851+
852+
822853
@util.api()
823854
def addTags(self, notes, tags, add=True):
824855
self.startEditing()

tests/test_notes.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ def test_clearUnusedTags(self, setup):
9898
ac.clearUnusedTags()
9999
assert ac.getTags() == ["tag1"]
100100

101+
def test_updateNoteTags_and_getNoteTags(self, setup):
102+
ac.updateNoteTags(note=setup.note1_id, tags="footag")
103+
assert ac.getNoteTags(note=setup.note1_id) == ["footag"]
104+
ac.updateNoteTags(note=setup.note1_id, tags=["foo", "bar", "baz"])
105+
assert len(ac.getNoteTags(note=setup.note1_id)) == 3
106+
101107

102108
class TestUpdateNoteFields:
103109
def test_updateNoteFields(self, setup):
@@ -107,12 +113,27 @@ def test_updateNoteFields(self, setup):
107113
notes_info = ac.notesInfo(notes=[setup.note1_id])
108114
assert notes_info[0]["fields"]["field2"]["value"] == "bar"
109115

110-
def test_updateNoteFields_will_note_update_invalid_notes(self, setup):
116+
def test_updateNoteFields_will_not_update_invalid_notes(self, setup):
111117
bad_note = {"id": 123, "fields": make_note()["fields"]}
112118
with pytest.raises(NotFoundError):
113119
ac.updateNoteFields(note=bad_note)
114120

115121

122+
class TestUpdateNote:
123+
def test_updateNote(self, setup):
124+
new_fields = {"field1": "frontbar", "field2": "backbar"}
125+
new_tags = ["foobar"]
126+
good_note = {"id": setup.note1_id, "fields": new_fields, "tags": new_tags}
127+
ac.updateNote(note=good_note)
128+
notes_info = ac.notesInfo(notes=[setup.note1_id])
129+
assert notes_info[0]["fields"]["field2"]["value"] == "backbar"
130+
assert notes_info[0]["tags"] == ["foobar"]
131+
132+
def test_updateNote_requires_either_fields_or_tags(self, setup):
133+
with pytest.raises(Exception, match="ust provide"):
134+
ac.updateNote(note={"id": setup.note1_id})
135+
136+
116137
class TestCanAddNotes:
117138
foo_bar_notes = [make_note(front="foo"), make_note(front="bar")]
118139

0 commit comments

Comments
 (0)