diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a95bc88..6b0bb1d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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. diff --git a/README.rst b/README.rst index c627f5e..62bff6a 100644 --- a/README.rst +++ b/README.rst @@ -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 diff --git a/berserk/clients/studies.py b/berserk/clients/studies.py index 42bd36e..3c43b07 100644 --- a/berserk/clients/studies.py +++ b/berserk/clients/studies.py @@ -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 @@ -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. diff --git a/berserk/session.py b/berserk/session.py index b8a5b5d..cc8fb83 100644 --- a/berserk/session.py +++ b/berserk/session.py @@ -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 diff --git a/tests/clients/test_studies.py b/tests/clients/test_studies.py new file mode 100644 index 0000000..b439579 --- /dev/null +++ b/tests/clients/test_studies.py @@ -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" + )