Skip to content
This repository has been archived by the owner on Dec 31, 2023. It is now read-only.

Update note #380

Merged
merged 2 commits into from
Apr 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3058,7 +3058,7 @@ corresponding to when the API was available for use.

#### `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.

> **Warning**:
Expand Down Expand Up @@ -3105,6 +3105,122 @@ corresponding to when the API was available for use.
```
</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`

* Adds tags to notes by note ID.
Expand Down
31 changes: 31 additions & 0 deletions plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,37 @@ def updateNoteFields(self, note):
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()
def addTags(self, notes, tags, add=True):
self.startEditing()
Expand Down
23 changes: 22 additions & 1 deletion tests/test_notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ def test_clearUnusedTags(self, setup):
ac.clearUnusedTags()
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:
def test_updateNoteFields(self, setup):
Expand All @@ -107,12 +113,27 @@ def test_updateNoteFields(self, setup):
notes_info = ac.notesInfo(notes=[setup.note1_id])
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"]}
with pytest.raises(NotFoundError):
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:
foo_bar_notes = [make_note(front="foo"), make_note(front="bar")]

Expand Down