python-kanka 2.0.0__py3-none-any.whl
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.
- kanka/__init__.py +90 -0
- kanka/_version.py +3 -0
- kanka/client.py +540 -0
- kanka/exceptions.py +132 -0
- kanka/managers.py +464 -0
- kanka/models/__init__.py +58 -0
- kanka/models/base.py +91 -0
- kanka/models/common.py +175 -0
- kanka/models/entities.py +349 -0
- kanka/py.typed +2 -0
- kanka/types.py +8 -0
- python_kanka-2.0.0.dist-info/METADATA +377 -0
- python_kanka-2.0.0.dist-info/RECORD +16 -0
- python_kanka-2.0.0.dist-info/WHEEL +5 -0
- python_kanka-2.0.0.dist-info/licenses/LICENSE +21 -0
- python_kanka-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-kanka
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: A modern Python client for the Kanka API with full typing support
|
|
5
|
+
Home-page: https://github.com/ervwalter/python-kanka
|
|
6
|
+
Author: Erv Walter
|
|
7
|
+
Author-email: erv@ewal.net
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Bug Reports, https://github.com/ervwalter/python-kanka/issues
|
|
10
|
+
Project-URL: Source, https://github.com/ervwalter/python-kanka
|
|
11
|
+
Project-URL: Documentation, https://github.com/ervwalter/python-kanka#readme
|
|
12
|
+
Project-URL: Kanka API, https://kanka.io/en-US/docs/1.0
|
|
13
|
+
Keywords: kanka api client worldbuilding rpg campaign tabletop ttrpg dnd pathfinder
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Topic :: Games/Entertainment :: Role-Playing
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Operating System :: OS Independent
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: requests==2.32.3
|
|
30
|
+
Requires-Dist: requests_toolbelt>=0.9.1
|
|
31
|
+
Requires-Dist: pydantic==2.11.5
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
36
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
41
|
+
Dynamic: author
|
|
42
|
+
Dynamic: author-email
|
|
43
|
+
Dynamic: classifier
|
|
44
|
+
Dynamic: description
|
|
45
|
+
Dynamic: description-content-type
|
|
46
|
+
Dynamic: home-page
|
|
47
|
+
Dynamic: keywords
|
|
48
|
+
Dynamic: license
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
Dynamic: project-url
|
|
51
|
+
Dynamic: provides-extra
|
|
52
|
+
Dynamic: requires-dist
|
|
53
|
+
Dynamic: requires-python
|
|
54
|
+
Dynamic: summary
|
|
55
|
+
|
|
56
|
+
# python-kanka
|
|
57
|
+
|
|
58
|
+
A modern Python client for the [Kanka API](https://app.kanka.io/api-docs/1.0), the collaborative worldbuilding and campaign management platform.
|
|
59
|
+
|
|
60
|
+
*Originally inspired by/forked from [Kathrin Weihe's python-kanka](https://github.com/rbtnx/python-kanka). Thank you to Kathrin for the foundation and inspiration.*
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- **Entity Support**: Support for 12 core Kanka entity types:
|
|
65
|
+
- Characters, Locations, Organisations, Families
|
|
66
|
+
- Calendars, Events, Quests, Journals
|
|
67
|
+
- Notes, Tags, Races, Creatures
|
|
68
|
+
- **Type Safety**: Built with Pydantic v2 for data validation and type hints
|
|
69
|
+
- **Python 3.9+**: Full typing support for modern Python versions
|
|
70
|
+
- **Pythonic API**: Consistent interface patterns across all entity types
|
|
71
|
+
- **Error Handling**: Specific exception types for different API errors
|
|
72
|
+
- **Rate Limit Handling**: Automatic retry with exponential backoff
|
|
73
|
+
- **Entity Posts**: Support for entity posts/comments management
|
|
74
|
+
- **Filtering and Search**: Filter entities by various criteria and search across types
|
|
75
|
+
- **Pagination**: Built-in pagination support for large result sets
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
Install from PyPI:
|
|
80
|
+
```bash
|
|
81
|
+
pip install python-kanka
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or install from source:
|
|
85
|
+
```bash
|
|
86
|
+
git clone https://github.com/ervwalter/python-kanka.git
|
|
87
|
+
cd python-kanka
|
|
88
|
+
pip install -e .
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Quick Start
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from kanka import KankaClient
|
|
95
|
+
|
|
96
|
+
# Initialize the client
|
|
97
|
+
client = KankaClient(
|
|
98
|
+
token="your-api-token", # Get from https://app.kanka.io/settings/api
|
|
99
|
+
campaign_id=12345 # Your campaign ID
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Create a character
|
|
103
|
+
gandalf = client.characters.create(
|
|
104
|
+
name="Gandalf the Grey",
|
|
105
|
+
title="Wizard",
|
|
106
|
+
type="Istari",
|
|
107
|
+
age="2000+ years",
|
|
108
|
+
is_private=False
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Update the character
|
|
112
|
+
gandalf = client.characters.update(
|
|
113
|
+
gandalf,
|
|
114
|
+
name="Gandalf the White"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Search across all entities
|
|
118
|
+
results = client.search("Dragon")
|
|
119
|
+
for result in results:
|
|
120
|
+
print(f"{result.name} ({result.type})")
|
|
121
|
+
|
|
122
|
+
# List characters with filters
|
|
123
|
+
wizards = client.characters.list(
|
|
124
|
+
type="Wizard",
|
|
125
|
+
is_private=False
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Delete when done
|
|
129
|
+
client.characters.delete(gandalf)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Common Use Cases
|
|
133
|
+
|
|
134
|
+
### Working with Entity Posts
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# Get a character
|
|
138
|
+
character = client.characters.get(character_id)
|
|
139
|
+
|
|
140
|
+
# Create a new post/note (pass the entity object, not just ID)
|
|
141
|
+
post = client.characters.create_post(
|
|
142
|
+
character, # Pass the full entity object
|
|
143
|
+
name="Background",
|
|
144
|
+
entry="*Born in the ancient times...*",
|
|
145
|
+
is_private=False
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# List all posts for an entity
|
|
149
|
+
posts = client.characters.list_posts(character)
|
|
150
|
+
for post in posts:
|
|
151
|
+
print(f"{post.name}: {post.entry[:50]}...")
|
|
152
|
+
|
|
153
|
+
# Update a post (name field is required even if not changing)
|
|
154
|
+
update = client.characters.update_post(
|
|
155
|
+
character,
|
|
156
|
+
post.id,
|
|
157
|
+
name=post.name, # Required by API
|
|
158
|
+
entry="Updated content..."
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Advanced Filtering
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
# Filter by multiple criteria
|
|
166
|
+
results = client.characters.list(
|
|
167
|
+
name="Gandalf", # Partial name match
|
|
168
|
+
type="Wizard", # Exact type match
|
|
169
|
+
is_private=False, # Only public entities
|
|
170
|
+
tags=[15, 23], # Has specific tags
|
|
171
|
+
page=1, # Pagination
|
|
172
|
+
limit=30 # Results per page
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Access the generic entities endpoint
|
|
176
|
+
entities = client.entities(
|
|
177
|
+
types=["character", "location"], # Multiple entity types
|
|
178
|
+
name="Dragon", # Name filter
|
|
179
|
+
tags=[15, 23], # Tag filter
|
|
180
|
+
is_private=False
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Working with Multiple Entity Types
|
|
185
|
+
|
|
186
|
+
All entity types follow the same pattern:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
# Locations
|
|
190
|
+
rivendell = client.locations.create(
|
|
191
|
+
name="Rivendell",
|
|
192
|
+
type="City",
|
|
193
|
+
parent_location_id=middle_earth.id
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Organizations
|
|
197
|
+
council = client.organisations.create(
|
|
198
|
+
name="The White Council",
|
|
199
|
+
type="Council"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Journals
|
|
203
|
+
journal = client.journals.create(
|
|
204
|
+
name="Campaign Log",
|
|
205
|
+
date="3019-03-25"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Notes (for DM/private notes)
|
|
209
|
+
note = client.notes.create(
|
|
210
|
+
name="DM Notes",
|
|
211
|
+
entry="Remember: Gandalf knows about the ring",
|
|
212
|
+
is_private=True
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Tags
|
|
216
|
+
tag = client.tags.create(
|
|
217
|
+
name="Important NPC",
|
|
218
|
+
colour="#ff0000"
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Error Handling
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from kanka.exceptions import (
|
|
226
|
+
NotFoundError,
|
|
227
|
+
ValidationError,
|
|
228
|
+
RateLimitError,
|
|
229
|
+
AuthenticationError
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
character = client.characters.get(99999)
|
|
234
|
+
except NotFoundError:
|
|
235
|
+
print("Character not found")
|
|
236
|
+
except RateLimitError as e:
|
|
237
|
+
print(f"Rate limited. Retry after {e.retry_after} seconds")
|
|
238
|
+
except ValidationError as e:
|
|
239
|
+
print(f"Invalid data: {e.errors}")
|
|
240
|
+
except AuthenticationError:
|
|
241
|
+
print("Invalid API token")
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Rate Limiting
|
|
245
|
+
|
|
246
|
+
The client automatically handles API rate limits by retrying requests with exponential backoff:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
# Default behavior - automatic retry on rate limits
|
|
250
|
+
client = KankaClient(token, campaign_id)
|
|
251
|
+
|
|
252
|
+
# Disable automatic retry
|
|
253
|
+
client = KankaClient(
|
|
254
|
+
token,
|
|
255
|
+
campaign_id,
|
|
256
|
+
enable_rate_limit_retry=False
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Customize retry behavior
|
|
260
|
+
client = KankaClient(
|
|
261
|
+
token,
|
|
262
|
+
campaign_id,
|
|
263
|
+
max_retries=5, # Try up to 5 times (default: 3)
|
|
264
|
+
retry_delay=2.0, # Initial delay in seconds (default: 1.0)
|
|
265
|
+
max_retry_delay=120.0 # Maximum delay between retries (default: 60.0)
|
|
266
|
+
)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The client parses rate limit headers from the API to determine retry delays and respects the API's rate limits.
|
|
270
|
+
|
|
271
|
+
## Migration Guide
|
|
272
|
+
|
|
273
|
+
### Upgrading from v0.x to v2.0
|
|
274
|
+
|
|
275
|
+
The v2.0 release introduces a new API design with Pydantic models and type safety. Here's how to migrate:
|
|
276
|
+
|
|
277
|
+
#### Old API (v0.x)
|
|
278
|
+
```python
|
|
279
|
+
# Old way - procedural API
|
|
280
|
+
import kanka
|
|
281
|
+
client = kanka.KankaClient(token)
|
|
282
|
+
campaign = client.campaign(campaign_id)
|
|
283
|
+
char = campaign.character(char_id)
|
|
284
|
+
char.name = "New Name"
|
|
285
|
+
char.update()
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
#### New API (v2.0+)
|
|
289
|
+
```python
|
|
290
|
+
# New way - object-oriented with managers
|
|
291
|
+
from kanka import KankaClient
|
|
292
|
+
client = KankaClient(token, campaign_id)
|
|
293
|
+
char = client.characters.get(char_id)
|
|
294
|
+
char = client.characters.update(char, name="New Name")
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Key Differences
|
|
298
|
+
|
|
299
|
+
1. **Single Client**: No more separate campaign object - everything through `KankaClient`
|
|
300
|
+
2. **Entity Managers**: Each entity type has a dedicated manager (`client.characters`, `client.locations`, etc.)
|
|
301
|
+
3. **Immutable Models**: Models are Pydantic objects - use manager methods to update
|
|
302
|
+
4. **Better Types**: Full typing support with IDE autocomplete
|
|
303
|
+
5. **Consistent API**: All entities follow the same CRUD pattern
|
|
304
|
+
|
|
305
|
+
## Development Setup
|
|
306
|
+
|
|
307
|
+
For development, install additional dependencies:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Clone the repository
|
|
311
|
+
git clone https://github.com/ervwalter/python-kanka.git
|
|
312
|
+
cd python-kanka
|
|
313
|
+
|
|
314
|
+
# Install dev dependencies
|
|
315
|
+
pip install -r dev-requirements.txt
|
|
316
|
+
pip install -e . # Install in editable mode
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Development Tools
|
|
320
|
+
|
|
321
|
+
This project uses several tools to maintain code quality:
|
|
322
|
+
|
|
323
|
+
- **black** - Code formatter
|
|
324
|
+
- **isort** - Import sorter
|
|
325
|
+
- **ruff** - Fast Python linter
|
|
326
|
+
- **pytest** - Testing framework
|
|
327
|
+
- **mypy** - Static type checker
|
|
328
|
+
|
|
329
|
+
Use the Makefile for common development tasks:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
make install # Install all dependencies
|
|
333
|
+
make format # Format code with black and isort
|
|
334
|
+
make lint # Run linting checks
|
|
335
|
+
make test # Run all tests
|
|
336
|
+
make coverage # Run tests with coverage report
|
|
337
|
+
make check # Run all checks (lint + test)
|
|
338
|
+
make clean # Clean up temporary files
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Pre-commit Hooks (Optional)
|
|
342
|
+
|
|
343
|
+
To automatically run formatting and linting before each commit:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
pre-commit install
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## API Documentation
|
|
350
|
+
|
|
351
|
+
See the [API Reference](API_REFERENCE.md) for detailed documentation of all classes and methods.
|
|
352
|
+
|
|
353
|
+
## Examples
|
|
354
|
+
|
|
355
|
+
Check out the [examples/](examples/) directory for more detailed examples:
|
|
356
|
+
|
|
357
|
+
- `quickstart.py` - Basic usage tutorial
|
|
358
|
+
- `crud_operations.py` - Full CRUD examples for all entity types
|
|
359
|
+
- `filtering.py` - Advanced filtering and search
|
|
360
|
+
- `posts.py` - Working with entity posts
|
|
361
|
+
- `error_handling.py` - Proper error handling patterns
|
|
362
|
+
- `migration.py` - Migrating from the old API
|
|
363
|
+
|
|
364
|
+
## Contributing
|
|
365
|
+
|
|
366
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
367
|
+
|
|
368
|
+
## License
|
|
369
|
+
|
|
370
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
371
|
+
|
|
372
|
+
## Links
|
|
373
|
+
|
|
374
|
+
- [Kanka.io](https://kanka.io) - The Kanka platform
|
|
375
|
+
- [Kanka API Documentation](https://app.kanka.io/api-docs/1.0) - Official API docs
|
|
376
|
+
- [GitHub Repository](https://github.com/ervwalter/python-kanka) - Source code
|
|
377
|
+
- [Issue Tracker](https://github.com/ervwalter/python-kanka/issues) - Report bugs or request features
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
kanka/__init__.py,sha256=CRXZ6Ksdwjdnj0X9Mi0PfKjfuqkbuBubIgOFIi5VEiQ,1940
|
|
2
|
+
kanka/_version.py,sha256=qUKW4fU8V0MfR7QcDOqx466k09zsltHgH0ZEr4DbSK0,67
|
|
3
|
+
kanka/client.py,sha256=8RREBqRIKqrks6TW2yyWX2xYRhIzcw0a05Hg4OIGnVE,18511
|
|
4
|
+
kanka/exceptions.py,sha256=A4P7T7VO_Rp5IYldvFaecE3z4N7WYDgCDojYPu_yDzk,3871
|
|
5
|
+
kanka/managers.py,sha256=mqYs511xa94JnWHNU28pAR8CKkSSsBCXoFyEGPQHGwU,15772
|
|
6
|
+
kanka/py.typed,sha256=mZgbv7X8oKfyMzYIbodUlHrC15905jgP-ZbYviEIL18,86
|
|
7
|
+
kanka/types.py,sha256=ZcIZJZBkivmjoPlm2PXiAQQY_sZqdtZ-dSvF6jVfYIE,293
|
|
8
|
+
kanka/models/__init__.py,sha256=dpIL22R4olQuq26J8B1oB9tDcvmn6_7qdRvIhP1WZjs,1154
|
|
9
|
+
kanka/models/base.py,sha256=1KpfxXPil2z7BO9mFU5G8C1un_hYcqPETFfEWI_EZ7Q,2938
|
|
10
|
+
kanka/models/common.py,sha256=GgvcVrADDc9xcUM-nG2svKKK0pAyhdN0axnwmP7Xrec,4803
|
|
11
|
+
kanka/models/entities.py,sha256=iJCsmd0-QzST8lXFvy25EYEIysIbbrtkieAiocFI-eo,10532
|
|
12
|
+
python_kanka-2.0.0.dist-info/licenses/LICENSE,sha256=37op2CRJ_dU2qGU-iJjczcP-ljtJjpKAgBdKFaiF2GY,1067
|
|
13
|
+
python_kanka-2.0.0.dist-info/METADATA,sha256=JM6c11Uk18sYSbaL9tdqBjLNv4g4UaYTr_X24cNQUMU,10638
|
|
14
|
+
python_kanka-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
python_kanka-2.0.0.dist-info/top_level.txt,sha256=vSc0572bt8Jo1ie_cadWOh97JC-TNaYOvnsd80qMohs,6
|
|
16
|
+
python_kanka-2.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Erv Walter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kanka
|