Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
To be released
--------------

* Added ``client.studies.update_chapter_tags`` to update PGN tags of a study chapter.

* Deprecate Python 3.9 support - minimum required version is now Python 3.10+. This does not mean the library will not work with Python 3.9, but it will not be tested against it anymore.

Expand Down
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ Most of the API is available:
client.studies.export
client.studies.export_by_username
client.studies.import_pgn
client.studies.update_chapter_tags

client.tablebase.look_up
client.tablebase.standard
Expand Down
18 changes: 16 additions & 2 deletions berserk/clients/studies.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

from typing import cast, List, Iterator, Dict, Any
from typing import Any, Dict, Iterator, List, cast

from ..formats import PGN, NDJSON
from ..formats import NDJSON, PGN
from ..types.common import Color, VariantKey
from ..types import ChapterIdName
from .base import BaseClient
Expand Down Expand Up @@ -133,6 +133,20 @@ def import_pgn(
List[ChapterIdName], self._r.post(path, data=payload).get("chapters", [])
)

def update_chapter_tags(self, study_id: str, chapter_id: str, pgn: str) -> None:
"""Update PGN tags of a study chapter.

Add, update, or delete PGN tags by providing PGN text (only the tag
lines are used; moves are ignored). Omitted tags are left unchanged.

:param study_id: study id (8 characters)
:param chapter_id: chapter id (8 characters)
:param pgn: PGN text containing the tags to set
:return: None (API returns 204 No Content on success)
"""
path = f"/api/study/{study_id}/{chapter_id}/tags"
self._r.post(path, data={"pgn": pgn})

def get_by_user(self, username: str) -> Iterator[Dict[str, Any]]:
"""
Get metadata (name and dates) of all studies of a user.
Expand Down
3 changes: 3 additions & 0 deletions berserk/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ def request(
if not response.ok:
raise exceptions.ResponseError(response)

if response.status_code == 204:
return None

return fmt.handle(response, is_stream=stream, converter=converter)

@overload
Expand Down
27 changes: 27 additions & 0 deletions tests/clients/test_studies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import requests_mock

from berserk import Client


class TestUpdateChapterTags:
def test_request_path_and_form_body(self):
"""Request hits correct path and sends form-encoded pgn body (204)."""
pgn = '[Event "Test"]\n[Site "Lichess"]'
with requests_mock.Mocker() as m:
m.post(
"https://lichess.org/api/study/abc123/chapter456/tags",
status_code=204,
)
res = Client().studies.update_chapter_tags("abc123", "chapter456", pgn=pgn)
assert res is None
assert m.called
assert m.call_count == 1
assert m.last_request.method == "POST"
assert (
m.last_request.headers["Content-Type"]
== "application/x-www-form-urlencoded"
)
assert (
m.last_request.body
== "pgn=%5BEvent+%22Test%22%5D%0A%5BSite+%22Lichess%22%5D"
)