python-kanka 2.4.1__tar.gz → 2.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. {python_kanka-2.4.1 → python_kanka-2.5.0}/.bumpversion.cfg +1 -1
  2. {python_kanka-2.4.1 → python_kanka-2.5.0}/PKG-INFO +1 -1
  3. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/api-reference.md +14 -2
  4. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/pagination.md +8 -0
  5. {python_kanka-2.4.1 → python_kanka-2.5.0}/pyproject.toml +1 -1
  6. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/_version.py +1 -1
  7. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/client.py +38 -2
  8. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/test_client.py +77 -1
  9. {python_kanka-2.4.1 → python_kanka-2.5.0}/uv.lock +1 -1
  10. {python_kanka-2.4.1 → python_kanka-2.5.0}/.github/renovate.json +0 -0
  11. {python_kanka-2.4.1 → python_kanka-2.5.0}/.github/workflows/ci.yml +0 -0
  12. {python_kanka-2.4.1 → python_kanka-2.5.0}/.github/workflows/publish.yml +0 -0
  13. {python_kanka-2.4.1 → python_kanka-2.5.0}/.gitignore +0 -0
  14. {python_kanka-2.4.1 → python_kanka-2.5.0}/.pre-commit-config.yaml +0 -0
  15. {python_kanka-2.4.1 → python_kanka-2.5.0}/CHANGELOG.md +0 -0
  16. {python_kanka-2.4.1 → python_kanka-2.5.0}/CLAUDE.md +0 -0
  17. {python_kanka-2.4.1 → python_kanka-2.5.0}/LICENSE +0 -0
  18. {python_kanka-2.4.1 → python_kanka-2.5.0}/MANIFEST.in +0 -0
  19. {python_kanka-2.4.1 → python_kanka-2.5.0}/Makefile +0 -0
  20. {python_kanka-2.4.1 → python_kanka-2.5.0}/PUBLISHING.md +0 -0
  21. {python_kanka-2.4.1 → python_kanka-2.5.0}/README.md +0 -0
  22. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/README.md +0 -0
  23. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/assets-and-images.md +0 -0
  24. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/core-concepts.md +0 -0
  25. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/debug-mode.md +0 -0
  26. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/entities.md +0 -0
  27. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/entity-types-reference.md +0 -0
  28. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/error-handling.md +0 -0
  29. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/gallery.md +0 -0
  30. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/getting-started.md +0 -0
  31. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/known-limitations.md +0 -0
  32. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/posts.md +0 -0
  33. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/rate-limiting.md +0 -0
  34. {python_kanka-2.4.1 → python_kanka-2.5.0}/docs/search-and-filtering.md +0 -0
  35. {python_kanka-2.4.1 → python_kanka-2.5.0}/pytest.ini +0 -0
  36. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/__init__.py +0 -0
  37. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/exceptions.py +0 -0
  38. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/managers.py +0 -0
  39. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/models/__init__.py +0 -0
  40. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/models/base.py +0 -0
  41. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/models/common.py +0 -0
  42. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/models/entities.py +0 -0
  43. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/py.typed +0 -0
  44. {python_kanka-2.4.1 → python_kanka-2.5.0}/src/kanka/types.py +0 -0
  45. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/__init__.py +0 -0
  46. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/README.md +0 -0
  47. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/__init__.py +0 -0
  48. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/base.py +0 -0
  49. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/run_integration_tests.py +0 -0
  50. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/setup_test_env.py +0 -0
  51. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_calendars_integration.py +0 -0
  52. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_calendars_simple.py +0 -0
  53. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_characters_integration.py +0 -0
  54. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_creatures_integration.py +0 -0
  55. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_entities_api_integration.py +0 -0
  56. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_entity_assets_integration.py +0 -0
  57. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_entity_images_integration.py +0 -0
  58. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_entity_tags_integration.py +0 -0
  59. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_events_integration.py +0 -0
  60. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_families_integration.py +0 -0
  61. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_gallery_integration.py +0 -0
  62. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_journals_integration.py +0 -0
  63. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_locations_integration.py +0 -0
  64. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_mentions_integration.py +0 -0
  65. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_notes_integration.py +0 -0
  66. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_organisations_integration.py +0 -0
  67. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_posts_integration.py +0 -0
  68. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_quests_integration.py +0 -0
  69. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_races_integration.py +0 -0
  70. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_rate_limiting.py +0 -0
  71. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_search_integration.py +0 -0
  72. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_tag_colour.py +0 -0
  73. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/integration/test_tags_integration.py +0 -0
  74. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/mypy.ini +0 -0
  75. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/test_client_advanced.py +0 -0
  76. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/test_entity_models.py +0 -0
  77. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/test_exceptions.py +0 -0
  78. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/test_managers.py +0 -0
  79. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/test_models.py +0 -0
  80. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/test_models_advanced.py +0 -0
  81. {python_kanka-2.4.1 → python_kanka-2.5.0}/tests/utils.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 2.4.0
2
+ current_version = 2.5.0
3
3
  commit = True
4
4
  tag = True
5
5
  tag_name = v{new_version}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-kanka
3
- Version: 2.4.1
3
+ Version: 2.5.0
4
4
  Summary: Python client for the Kanka API
5
5
  Project-URL: Homepage, https://github.com/ervwalter/python-kanka
6
6
  Project-URL: Documentation, https://github.com/ervwalter/python-kanka#readme
@@ -90,12 +90,21 @@ Get a single entity by its universal `entity_id`. Returns a raw dictionary.
90
90
  #### entities
91
91
 
92
92
  ```python
93
- entities(**filters) → list[dict[str, Any]]
93
+ entities(page: int = 1, limit: int = 15, **filters) → list[dict[str, Any]]
94
94
  ```
95
95
 
96
96
  Query the generic `/entities` endpoint. Returns raw dictionaries.
97
97
 
98
- **Filters:** `types` (list[str]), `name` (str), `tags` (list[int]), `is_private` (bool), `created_by` (int), `updated_by` (int)
98
+ | Parameter | Type | Default | Description |
99
+ |-----------|------|---------|-------------|
100
+ | `page` | `int` | `1` | Page number |
101
+ | `limit` | `int` | `15` | Results per page |
102
+ | `types` | `list[str]` | — | Filter by entity types |
103
+ | `name` | `str` | — | Filter by name |
104
+ | `tags` | `list[int]` | — | Filter by tag IDs |
105
+ | `is_private` | `bool` | — | Filter by privacy |
106
+ | `created_by` | `int` | — | Filter by creator ID |
107
+ | `updated_by` | `int` | — | Filter by updater ID |
99
108
 
100
109
  #### gallery
101
110
 
@@ -137,6 +146,9 @@ Delete a gallery image by UUID.
137
146
 
138
147
  | Property | Type | Description |
139
148
  |----------|------|-------------|
149
+ | `last_entities_meta` | `dict` | Pagination metadata from last `entities()` |
150
+ | `last_entities_links` | `dict` | Pagination links from last `entities()` |
151
+ | `entities_has_next_page` | `bool` | Whether next page exists for `entities()` |
140
152
  | `last_search_meta` | `dict` | Pagination metadata from last `search()` |
141
153
  | `last_search_links` | `dict` | Pagination links from last `search()` |
142
154
  | `last_gallery_meta` | `dict` | Pagination metadata from last `gallery()` |
@@ -83,6 +83,14 @@ Each list endpoint stores its own pagination state:
83
83
  | `manager.last_assets_meta` | Metadata from last `list_assets()` |
84
84
  | `manager.last_assets_links` | Links from last `list_assets()` |
85
85
 
86
+ ### Entities
87
+
88
+ | Property | Description |
89
+ |----------|-------------|
90
+ | `client.last_entities_meta` | Metadata from last `entities()` |
91
+ | `client.last_entities_links` | Links from last `entities()` |
92
+ | `client.entities_has_next_page` | `True` if next page exists |
93
+
86
94
  ### Gallery
87
95
 
88
96
  | Property | Description |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "python-kanka"
7
- version = "2.4.1"
7
+ version = "2.5.0"
8
8
  description = "Python client for the Kanka API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -1,3 +1,3 @@
1
1
  """Version information for python-kanka."""
2
2
 
3
- __version__ = "2.4.1"
3
+ __version__ = "2.5.0"
@@ -321,19 +321,29 @@ class KankaClient:
321
321
  response = self._request("GET", f"entities/{entity_id}")
322
322
  return response["data"] # type: ignore[no-any-return]
323
323
 
324
- def entities(self, **filters) -> list[dict[str, Any]]:
324
+ def entities(
325
+ self, page: int = 1, limit: int = 15, **filters
326
+ ) -> list[dict[str, Any]]:
325
327
  """Access the /entities endpoint with filters.
326
328
 
327
329
  This endpoint provides a unified way to query entities across all types
328
330
  with various filtering options.
329
331
 
330
332
  Args:
333
+ page: Page number (default: 1)
334
+ limit: Number of results per page (default: 15)
331
335
  **filters: Filter parameters like types, name, is_private, tags
332
336
 
333
337
  Returns:
334
338
  List of entity data
339
+
340
+ Note:
341
+ Pagination information is available after calling entities():
342
+ - client.last_entities_meta: Contains total count, current page, etc.
343
+ - client.last_entities_links: Contains URLs for first, last, prev, next
344
+ - client.entities_has_next_page: True if more pages are available
335
345
  """
336
- params: dict[str, int | str] = {}
346
+ params: dict[str, int | str] = {"page": page, "limit": limit}
337
347
 
338
348
  # Handle special filters
339
349
  if "types" in filters and isinstance(filters["types"], list):
@@ -355,6 +365,8 @@ class KankaClient:
355
365
  params[key] = filters[key]
356
366
 
357
367
  response = self._request("GET", "entities", params=params)
368
+ self._last_entities_meta = response.get("meta", {})
369
+ self._last_entities_links = response.get("links", {})
358
370
  return response["data"] # type: ignore[no-any-return]
359
371
 
360
372
  def _parse_rate_limit_headers(self, response) -> float | None:
@@ -692,3 +704,27 @@ class KankaClient:
692
704
  Dict[str, Any]: Links for pagination including first, last, prev, next URLs
693
705
  """
694
706
  return getattr(self, "_last_search_links", {})
707
+
708
+ @property
709
+ def last_entities_meta(self) -> dict[str, Any]:
710
+ """Get metadata from the last entities() call.
711
+
712
+ Returns:
713
+ Dict[str, Any]: Pagination metadata including current_page, from, to,
714
+ last_page, per_page, total
715
+ """
716
+ return getattr(self, "_last_entities_meta", {})
717
+
718
+ @property
719
+ def last_entities_links(self) -> dict[str, Any]:
720
+ """Get pagination links from the last entities() call.
721
+
722
+ Returns:
723
+ Dict[str, Any]: Links for pagination including first, last, prev, next URLs
724
+ """
725
+ return getattr(self, "_last_entities_links", {})
726
+
727
+ @property
728
+ def entities_has_next_page(self) -> bool:
729
+ """Check if more pages are available from the last entities() call."""
730
+ return bool(getattr(self, "_last_entities_links", {}).get("next"))
@@ -151,7 +151,7 @@ class TestKankaClient:
151
151
  mock_session.request.assert_called_with(
152
152
  "GET",
153
153
  "https://api.kanka.io/1.0/campaigns/123/entities",
154
- params={"types": "character,location"},
154
+ params={"page": 1, "limit": 15, "types": "character,location"},
155
155
  )
156
156
 
157
157
  assert len(results) == 2
@@ -181,6 +181,8 @@ class TestKankaClient:
181
181
  "GET",
182
182
  "https://api.kanka.io/1.0/campaigns/123/entities",
183
183
  params={
184
+ "page": 1,
185
+ "limit": 15,
184
186
  "types": "character",
185
187
  "tags": "1,2,3",
186
188
  "name": "test",
@@ -189,6 +191,80 @@ class TestKankaClient:
189
191
  },
190
192
  )
191
193
 
194
+ @patch("requests.Session")
195
+ def test_entities_pagination(self, mock_session_class):
196
+ """Test entities endpoint pagination parameters and metadata."""
197
+ mock_session = MagicMock()
198
+ mock_session_class.return_value = mock_session
199
+
200
+ meta = {
201
+ "total": 50,
202
+ "current_page": 2,
203
+ "per_page": 15,
204
+ "last_page": 4,
205
+ "from": 16,
206
+ "to": 30,
207
+ }
208
+ links = {
209
+ "first": "https://api.kanka.io/1.0/campaigns/123/entities?page=1",
210
+ "last": "https://api.kanka.io/1.0/campaigns/123/entities?page=4",
211
+ "prev": "https://api.kanka.io/1.0/campaigns/123/entities?page=1",
212
+ "next": "https://api.kanka.io/1.0/campaigns/123/entities?page=3",
213
+ }
214
+ mock_response = MockResponse(
215
+ create_api_response(
216
+ [{"id": 1, "name": "Test", "type": "character"}],
217
+ meta=meta,
218
+ links=links,
219
+ ),
220
+ status_code=200,
221
+ )
222
+ mock_session.request.return_value = mock_response
223
+
224
+ client = KankaClient(token="test_token", campaign_id=123)
225
+ results = client.entities(page=2, limit=15, types=["character"])
226
+
227
+ mock_session.request.assert_called_with(
228
+ "GET",
229
+ "https://api.kanka.io/1.0/campaigns/123/entities",
230
+ params={"page": 2, "limit": 15, "types": "character"},
231
+ )
232
+
233
+ assert len(results) == 1
234
+ assert client.last_entities_meta["total"] == 50
235
+ assert client.last_entities_meta["current_page"] == 2
236
+ assert client.last_entities_links["next"] is not None
237
+ assert client.entities_has_next_page is True
238
+
239
+ @patch("requests.Session")
240
+ def test_entities_no_next_page(self, mock_session_class):
241
+ """Test entities_has_next_page is False on last page."""
242
+ mock_session = MagicMock()
243
+ mock_session_class.return_value = mock_session
244
+
245
+ mock_response = MockResponse(
246
+ create_api_response([{"id": 1, "name": "Test", "type": "character"}]),
247
+ status_code=200,
248
+ )
249
+ mock_session.request.return_value = mock_response
250
+
251
+ client = KankaClient(token="test_token", campaign_id=123)
252
+ client.entities()
253
+
254
+ assert client.entities_has_next_page is False
255
+
256
+ @patch("requests.Session")
257
+ def test_entities_pagination_defaults(self, mock_session_class):
258
+ """Test entities pagination properties before any call."""
259
+ mock_session = MagicMock()
260
+ mock_session_class.return_value = mock_session
261
+
262
+ client = KankaClient(token="test_token", campaign_id=123)
263
+
264
+ assert client.last_entities_meta == {}
265
+ assert client.last_entities_links == {}
266
+ assert client.entities_has_next_page is False
267
+
192
268
  @patch("requests.Session")
193
269
  def test_request_error_handling(self, mock_session_class):
194
270
  """Test error handling in _request method."""
@@ -853,7 +853,7 @@ wheels = [
853
853
 
854
854
  [[package]]
855
855
  name = "python-kanka"
856
- version = "2.4.0"
856
+ version = "2.5.0"
857
857
  source = { editable = "." }
858
858
  dependencies = [
859
859
  { name = "pydantic" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes