Habiticalib 0.1.0a1__tar.gz → 0.1.0a3__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 (50) hide show
  1. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.github/labels.yml +5 -5
  2. habiticalib-0.1.0a3/.github/release-drafter.yml +94 -0
  3. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.github/workflows/build.yml +1 -1
  4. habiticalib-0.1.0a3/PKG-INFO +97 -0
  5. habiticalib-0.1.0a3/README.md +79 -0
  6. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/pyproject.toml +4 -4
  7. habiticalib-0.1.0a3/src/habiticalib/__init__.py +71 -0
  8. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/src/habiticalib/const.py +2 -0
  9. habiticalib-0.1.0a3/src/habiticalib/exceptions.py +50 -0
  10. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/src/habiticalib/helpers.py +28 -5
  11. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/src/habiticalib/lib.py +65 -46
  12. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/src/habiticalib/types.py +89 -67
  13. habiticalib-0.1.0a3/tests/__snapshots__/test_avatar.ambr +28 -0
  14. habiticalib-0.1.0a3/tests/__snapshots__/test_user.ambr +4 -0
  15. habiticalib-0.1.0a3/tests/conftest.py +49 -0
  16. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/tests/fixtures/user.json +10 -5
  17. habiticalib-0.1.0a3/tests/fixtures/user_styles.json +57 -0
  18. habiticalib-0.1.0a3/tests/fixtures/user_styles_kickstarter.json +57 -0
  19. habiticalib-0.1.0a3/tests/fixtures/user_styles_seafoam.json +57 -0
  20. habiticalib-0.1.0a3/tests/fixtures/user_styles_shinySeed.json +57 -0
  21. habiticalib-0.1.0a3/tests/fixtures/user_styles_sleeping.json +57 -0
  22. habiticalib-0.1.0a3/tests/fixtures/user_styles_snowball.json +57 -0
  23. habiticalib-0.1.0a3/tests/fixtures/user_styles_spookySparkles.json +57 -0
  24. habiticalib-0.1.0a3/tests/fixtures/user_styles_with_chair.json +57 -0
  25. habiticalib-0.1.0a3/tests/test_avatar.py +100 -0
  26. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/tests/test_user.py +1 -3
  27. habiticalib-0.1.0a1/.github/release-drafter.yml +0 -39
  28. habiticalib-0.1.0a1/PKG-INFO +0 -92
  29. habiticalib-0.1.0a1/README.md +0 -73
  30. habiticalib-0.1.0a1/src/habiticalib/__init__.py +0 -12
  31. habiticalib-0.1.0a1/src/habiticalib/exceptions.py +0 -27
  32. habiticalib-0.1.0a1/tests/__snapshots__/test_user.ambr +0 -4
  33. habiticalib-0.1.0a1/tests/conftest.py +0 -27
  34. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.cruft.json +0 -0
  35. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.editorconfig +0 -0
  36. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.github/FUNDING.yml +0 -0
  37. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.github/dependabot.yml +0 -0
  38. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.github/workflows/documentation.yml +0 -0
  39. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.github/workflows/draft.yml +0 -0
  40. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.github/workflows/labeler.yml +0 -0
  41. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.gitignore +0 -0
  42. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.pre-commit-config.yaml +0 -0
  43. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/.vscode/settings.json +0 -0
  44. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/LICENSE +0 -0
  45. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/docs/index.md +0 -0
  46. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/docs/reference/habiticalib.md +0 -0
  47. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/mkdocs.yml +0 -0
  48. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/src/habiticalib/py.typed +0 -0
  49. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/tests/__init__.py +0 -0
  50. {habiticalib-0.1.0a1 → habiticalib-0.1.0a3}/tests/test_init.py +0 -0
@@ -25,7 +25,7 @@
25
25
  - name: duplicate
26
26
  description: This issue or pull request already exists
27
27
  color: cfd3d7
28
- - name: enhancement
28
+ - name: feature
29
29
  description: New feature or request
30
30
  color: a2eeef
31
31
  - name: github_actions
@@ -49,17 +49,17 @@
49
49
  - name: question
50
50
  description: Further information is requested
51
51
  color: d876e3
52
- - name: refactoring
53
- description: Refactoring
52
+ - name: code quality
53
+ description: Code quality improvements
54
54
  color: ef67c4
55
- - name: removal
55
+ - name: deprecation
56
56
  description: Removals and Deprecations
57
57
  color: 9ae7ea
58
58
  - name: style
59
59
  description: Style
60
60
  color: c120e5
61
61
  - name: testing
62
- description: Testing
62
+ description: Pull request that adds tests
63
63
  color: b1fc6f
64
64
  - name: wontfix
65
65
  description: This will not be worked on
@@ -0,0 +1,94 @@
1
+ name-template: '$RESOLVED_VERSION'
2
+ tag-template: 'v$RESOLVED_VERSION'
3
+
4
+
5
+ categories:
6
+ - title: '⚠️ Breaking changes'
7
+ labels:
8
+ - 'breaking'
9
+ - title: '🚀 Features'
10
+ labels:
11
+ - 'feature'
12
+ - 'enhancement'
13
+ - title: '🐛 Bug Fixes'
14
+ labels:
15
+ - 'bug'
16
+ - title: '⏳ Deprecations'
17
+ labels:
18
+ - 'deprecation'
19
+ - title: '📃 Documentation'
20
+ labels:
21
+ - 'documentation'
22
+ - title: '🧩 Dependency Updates'
23
+ labels:
24
+ - 'dependencies'
25
+ - 'github_actions'
26
+ collapse-after: 5
27
+ - title: '🧰 Maintenance'
28
+ labels:
29
+ - 'perfomance'
30
+ - 'refactor'
31
+ - 'ci'
32
+ - 'build'
33
+ - title: '🔬 Other updates'
34
+ labels:
35
+ - 'style'
36
+ - 'testing'
37
+
38
+ autolabeler:
39
+ - label: 'feature'
40
+ title:
41
+ - '/adds/i'
42
+ - label: 'bug'
43
+ title:
44
+ - '/fix/i'
45
+ - label: 'code quality'
46
+ title:
47
+ - '/Refactor/i'
48
+ - label: 'testing'
49
+ title:
50
+ - '/test:/i'
51
+ files:
52
+ - 'test_*'
53
+ - 'conftest.py'
54
+ - label: 'documentation'
55
+ title:
56
+ - '/docs:/i'
57
+ files:
58
+ - '*.md'
59
+ - 'mkdocs.yml'
60
+ - label: 'ci'
61
+ title:
62
+ - '/ci:/i'
63
+ files:
64
+ - '.github/*'
65
+ - label: 'dependencies'
66
+ title:
67
+ - '/bump/i'
68
+ - label: 'deprecation'
69
+ title:
70
+ - '/Deprecate/i'
71
+
72
+ change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
73
+ change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
74
+ exclude-contributors:
75
+ - 'tr4nt0r'
76
+
77
+ version-resolver:
78
+ major:
79
+ labels:
80
+ - 'major'
81
+ minor:
82
+ labels:
83
+ - 'minor'
84
+ patch:
85
+ labels:
86
+ - 'patch'
87
+ default: patch
88
+
89
+ template: |
90
+ ## Changes
91
+
92
+ $CHANGES
93
+
94
+ Special thanks to: $CONTRIBUTORS
@@ -27,7 +27,7 @@ jobs:
27
27
  - name: Test
28
28
  run: |
29
29
  hatch run test-cov-xml
30
- - uses: codecov/codecov-action@v4
30
+ - uses: codecov/codecov-action@v5
31
31
  with:
32
32
  token: ${{ secrets.CODECOV_TOKEN }}
33
33
  fail_ci_if_error: true
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.3
2
+ Name: Habiticalib
3
+ Version: 0.1.0a3
4
+ Summary: Asynchronous Python client library for the Habitica API
5
+ Project-URL: Documentation, https://tr4nt0r.github.io/habiticalib/
6
+ Project-URL: Source, https://github.com/tr4nt0r/habiticalib
7
+ Author-email: Manfred Dennerlein Rodelo <manfred@dennerlein.name>
8
+ License: MIT License
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Requires-Python: >=3.12
13
+ Requires-Dist: aiohttp~=3.9
14
+ Requires-Dist: mashumaro~=3.13
15
+ Requires-Dist: orjson~=3.10
16
+ Requires-Dist: pillow~=11.0
17
+ Description-Content-Type: text/markdown
18
+
19
+ # Habiticalib
20
+
21
+ <p align="center">
22
+ <em>Modern asynchronous Python client library for the Habitica API</em>
23
+ </p>
24
+
25
+ [![build](https://github.com/tr4nt0r/habiticalib/workflows/Build/badge.svg)](https://github.com/tr4nt0r/habiticalib/actions)
26
+ [![codecov](https://codecov.io/gh/tr4nt0r/habiticalib/graph/badge.svg?token=iEsZ1Ktj7d)](https://codecov.io/gh/tr4nt0r/habiticalib)
27
+ [![PyPI version](https://badge.fury.io/py/habiticalib.svg)](https://badge.fury.io/py/habiticalib)
28
+
29
+ **Habiticalib** is a Python library for interacting with the [Habitica API](https://habitica.com). It provides an organized, typed interface to work with Habitica’s features, including tasks, user data, and avatars. The goal of this library is to simplify integration with Habitica.
30
+
31
+ ## Key features
32
+
33
+ - **Asynchronous**: The library is fully asynchronous, allowing non-blocking API calls.
34
+ - **Fully typed with Dataclasses**: The library is fully typed using Python `dataclasses`. It handles serialization with `mashumaro` and `orjson` for efficient conversion between Habitica API JSON data and Python objects.
35
+ - **Dynamic avatar image generation**: Habiticalib can fetch all necessary assets (like equipped items, pets, and mounts) and combine them into a single avatar image. This image can be saved to disk or returned as a byte buffer for further processing.
36
+ **Fetch user data**: Retrieve and manage user data such as stats, preferences, and items. User data is structured with dataclasses to make it easy to work with.
37
+ - **Task management**: Support for creating, updating, and retrieving Habitica tasks (to-dos, dailies, habits, rewards) is provided.
38
+ - **Task status updates**: The library allows updates for task statuses, habit scoring, and daily completion.
39
+ - **Tags**: Habiticalib supports the creation, updating and deletion of tags.
40
+ - **Stat allocation, class cystem and sleep**: The library offers methods for stat point allocation and switching between Habitica classes. It also provides the ability to disable the class system and pausing damage(resting in the inn)
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install habiticalib
46
+ ```
47
+
48
+ ## Getting started
49
+ Here’s an example to demonstrate basic usage:
50
+
51
+ ```python
52
+ import asyncio
53
+
54
+ from aiohttp import ClientSession
55
+
56
+ from habiticalib import Habitica, TaskType
57
+
58
+
59
+ async def main():
60
+ async with ClientSession() as session:
61
+ habitica = Habitica(session)
62
+
63
+ # Login to Habitica
64
+ habitica.login(username="your_username", password="your_password")
65
+
66
+ # Fetch user data
67
+ user_data = await habitica.user()
68
+ print(f"Your current health: {user_data.stats.hp}")
69
+
70
+ # Fetch all tasks (to-dos, dailies, habits, and rewards)
71
+ tasks = await habitica.get_tasks()
72
+ print("All tasks:")
73
+ for task in tasks:
74
+ print(f"- {task.text} (type: {task.type})")
75
+
76
+ # Fetch only to-dos
77
+ todos = await habitica.get_tasks(task_type=TaskType.TODO)
78
+ print("\nTo-Do tasks:")
79
+ for todo in todos:
80
+ print(f"- {todo.text} (due: {todo.date})")
81
+
82
+ # Fetch only dailies
83
+ dailies = await habitica.tasks(task_type=TaskType.DAILY)
84
+ print("\nDailies:")
85
+ for daily in dailies:
86
+ print(f"- {daily.text}")
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ## Documentation
92
+
93
+ For full documentation and detailed usage examples, please visit the [Habiticalib documentation](https://tr4nt0r.github.io/habiticalib/).
94
+
95
+ ## License
96
+
97
+ This project is licensed under the terms of the MIT license.
@@ -0,0 +1,79 @@
1
+ # Habiticalib
2
+
3
+ <p align="center">
4
+ <em>Modern asynchronous Python client library for the Habitica API</em>
5
+ </p>
6
+
7
+ [![build](https://github.com/tr4nt0r/habiticalib/workflows/Build/badge.svg)](https://github.com/tr4nt0r/habiticalib/actions)
8
+ [![codecov](https://codecov.io/gh/tr4nt0r/habiticalib/graph/badge.svg?token=iEsZ1Ktj7d)](https://codecov.io/gh/tr4nt0r/habiticalib)
9
+ [![PyPI version](https://badge.fury.io/py/habiticalib.svg)](https://badge.fury.io/py/habiticalib)
10
+
11
+ **Habiticalib** is a Python library for interacting with the [Habitica API](https://habitica.com). It provides an organized, typed interface to work with Habitica’s features, including tasks, user data, and avatars. The goal of this library is to simplify integration with Habitica.
12
+
13
+ ## Key features
14
+
15
+ - **Asynchronous**: The library is fully asynchronous, allowing non-blocking API calls.
16
+ - **Fully typed with Dataclasses**: The library is fully typed using Python `dataclasses`. It handles serialization with `mashumaro` and `orjson` for efficient conversion between Habitica API JSON data and Python objects.
17
+ - **Dynamic avatar image generation**: Habiticalib can fetch all necessary assets (like equipped items, pets, and mounts) and combine them into a single avatar image. This image can be saved to disk or returned as a byte buffer for further processing.
18
+ **Fetch user data**: Retrieve and manage user data such as stats, preferences, and items. User data is structured with dataclasses to make it easy to work with.
19
+ - **Task management**: Support for creating, updating, and retrieving Habitica tasks (to-dos, dailies, habits, rewards) is provided.
20
+ - **Task status updates**: The library allows updates for task statuses, habit scoring, and daily completion.
21
+ - **Tags**: Habiticalib supports the creation, updating and deletion of tags.
22
+ - **Stat allocation, class cystem and sleep**: The library offers methods for stat point allocation and switching between Habitica classes. It also provides the ability to disable the class system and pausing damage(resting in the inn)
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install habiticalib
28
+ ```
29
+
30
+ ## Getting started
31
+ Here’s an example to demonstrate basic usage:
32
+
33
+ ```python
34
+ import asyncio
35
+
36
+ from aiohttp import ClientSession
37
+
38
+ from habiticalib import Habitica, TaskType
39
+
40
+
41
+ async def main():
42
+ async with ClientSession() as session:
43
+ habitica = Habitica(session)
44
+
45
+ # Login to Habitica
46
+ habitica.login(username="your_username", password="your_password")
47
+
48
+ # Fetch user data
49
+ user_data = await habitica.user()
50
+ print(f"Your current health: {user_data.stats.hp}")
51
+
52
+ # Fetch all tasks (to-dos, dailies, habits, and rewards)
53
+ tasks = await habitica.get_tasks()
54
+ print("All tasks:")
55
+ for task in tasks:
56
+ print(f"- {task.text} (type: {task.type})")
57
+
58
+ # Fetch only to-dos
59
+ todos = await habitica.get_tasks(task_type=TaskType.TODO)
60
+ print("\nTo-Do tasks:")
61
+ for todo in todos:
62
+ print(f"- {todo.text} (due: {todo.date})")
63
+
64
+ # Fetch only dailies
65
+ dailies = await habitica.tasks(task_type=TaskType.DAILY)
66
+ print("\nDailies:")
67
+ for daily in dailies:
68
+ print(f"- {daily.text}")
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Documentation
74
+
75
+ For full documentation and detailed usage examples, please visit the [Habiticalib documentation](https://tr4nt0r.github.io/habiticalib/).
76
+
77
+ ## License
78
+
79
+ This project is licensed under the terms of the MIT license.
@@ -25,7 +25,7 @@ max-complexity = 25
25
25
 
26
26
  [tool.ruff.lint.per-file-ignores]
27
27
  "types.py" = ["N815"]
28
- "tests/*" = ["SLF001", "S101"]
28
+ "tests/*" = ["SLF001", "S101", "ARG001"]
29
29
 
30
30
  [tool.pytest.ini_options]
31
31
  testpaths = ["tests"]
@@ -61,7 +61,7 @@ allow-direct-references = true
61
61
  [tool.hatch.version]
62
62
  source = "regex_commit"
63
63
  commit_extra_args = ["-e"]
64
- path = "src/habiticalib/__init__.py"
64
+ path = "src/habiticalib/const.py"
65
65
 
66
66
  [tool.hatch.envs.default]
67
67
  python = "3.12"
@@ -69,7 +69,7 @@ dependencies = [
69
69
  "aiohttp==3.10.10",
70
70
  "mashumaro==3.13.1",
71
71
  "orjson==3.10.7",
72
- "Pillow==10.4.0",
72
+ "Pillow==11.0.0",
73
73
  "mypy==1.12.0",
74
74
  "ruff==0.7.0",
75
75
  "pytest==8.3.3",
@@ -125,7 +125,7 @@ dependencies = [
125
125
  "aiohttp~=3.9",
126
126
  "mashumaro~=3.13",
127
127
  "orjson~=3.10",
128
- "Pillow~=10.4"
128
+ "Pillow~=11.0"
129
129
  ]
130
130
 
131
131
  [project.urls]
@@ -0,0 +1,71 @@
1
+ """Modern asynchronous Python client library for the Habitica API."""
2
+
3
+ from .const import ASSETS_URL, DEFAULT_URL, __version__
4
+ from .exceptions import (
5
+ BadRequestError,
6
+ HabiticaException,
7
+ NotAuthorizedError,
8
+ NotFoundError,
9
+ TooManyRequestsError,
10
+ )
11
+ from .lib import Habitica
12
+ from .types import (
13
+ Attributes,
14
+ Direction,
15
+ Frequency,
16
+ HabiticaClass,
17
+ HabiticaClassSystemResponse,
18
+ HabiticaErrorResponse,
19
+ HabiticaLoginResponse,
20
+ HabiticaResponse,
21
+ HabiticaScoreResponse,
22
+ HabiticaStatsResponse,
23
+ HabiticaTagResponse,
24
+ HabiticaTagsResponse,
25
+ HabiticaTaskOrderResponse,
26
+ HabiticaTaskResponse,
27
+ HabiticaTasksResponse,
28
+ HabiticaUserExport,
29
+ HabiticaUserResponse,
30
+ Language,
31
+ Skill,
32
+ Task,
33
+ TaskFilter,
34
+ TaskType,
35
+ UserStyles,
36
+ )
37
+
38
+ __all__ = [
39
+ "__version__",
40
+ "ASSETS_URL",
41
+ "Attributes",
42
+ "BadRequestError",
43
+ "DEFAULT_URL",
44
+ "Direction",
45
+ "Frequency",
46
+ "Habitica",
47
+ "HabiticaClass",
48
+ "HabiticaClassSystemResponse",
49
+ "HabiticaErrorResponse",
50
+ "HabiticaException",
51
+ "HabiticaLoginResponse",
52
+ "HabiticaResponse",
53
+ "HabiticaScoreResponse",
54
+ "HabiticaStatsResponse",
55
+ "HabiticaTagResponse",
56
+ "HabiticaTagsResponse",
57
+ "HabiticaTaskOrderResponse",
58
+ "HabiticaTaskResponse",
59
+ "HabiticaTasksResponse",
60
+ "HabiticaUserExport",
61
+ "HabiticaUserResponse",
62
+ "Language",
63
+ "NotAuthorizedError",
64
+ "NotFoundError",
65
+ "Skill",
66
+ "Task",
67
+ "TaskFilter",
68
+ "TaskType",
69
+ "TooManyRequestsError",
70
+ "UserStyles",
71
+ ]
@@ -1,5 +1,7 @@
1
1
  """Constants for Habiticalib."""
2
2
 
3
+ __version__ = "0.1.0a3"
4
+
3
5
  DEFAULT_URL = "https://habitica.com/"
4
6
  ASSETS_URL = "https://habitica-assets.s3.amazonaws.com/mobileApp/images/"
5
7
 
@@ -0,0 +1,50 @@
1
+ """Exceptions for Habiticalib."""
2
+
3
+ from datetime import datetime
4
+ from typing import Self
5
+
6
+ from multidict import CIMultiDictProxy
7
+
8
+ from habiticalib.types import HabiticaErrorResponse
9
+
10
+
11
+ class HabiticaException(Exception): # noqa: N818
12
+ """Base class for Habitica errors."""
13
+
14
+ def __init__(
15
+ self: Self,
16
+ error: HabiticaErrorResponse,
17
+ headers: CIMultiDictProxy,
18
+ ) -> None:
19
+ """Initialize the Exception."""
20
+ self.error = error
21
+ self.rate_limit: int | None = (
22
+ int(r) if (r := headers.get("x-ratelimit-limit")) else None
23
+ )
24
+ self.rate_limit_remaining: int | None = (
25
+ int(r) if (r := headers.get("x-ratelimit-remaining")) else None
26
+ )
27
+ self.rate_limit_reset: datetime | None = (
28
+ datetime.strptime(r[:33], "%a %b %d %Y %H:%M:%S %Z%z")
29
+ if (r := headers.get("x-ratelimit-reset"))
30
+ else None
31
+ )
32
+ self.retry_after: int = round(float(headers.get("retry-after", 0)))
33
+
34
+ super().__init__(error.message)
35
+
36
+
37
+ class NotAuthorizedError(HabiticaException):
38
+ """NotAuthorized error."""
39
+
40
+
41
+ class NotFoundError(HabiticaException):
42
+ """NotFound error."""
43
+
44
+
45
+ class BadRequestError(HabiticaException):
46
+ """BadRequest error."""
47
+
48
+
49
+ class TooManyRequestsError(HabiticaException):
50
+ """TooManyRequests error."""
@@ -1,13 +1,15 @@
1
1
  """Helper functions for Habiticalib."""
2
2
 
3
- from dataclasses import asdict
4
- from importlib.metadata import version
3
+ from dataclasses import asdict, is_dataclass
4
+ from datetime import date, datetime
5
+ from enum import Enum
5
6
  import platform
7
+ from typing import Any
6
8
  import uuid
7
9
 
8
10
  import aiohttp
9
11
 
10
- from .const import DEVELOPER_ID
12
+ from .const import DEVELOPER_ID, __version__
11
13
  from .types import HabiticaUserResponse, UserData, UserStyles
12
14
 
13
15
 
@@ -58,7 +60,7 @@ def get_user_agent() -> str:
58
60
  os_info = f"{os_name} {os_release} ({os_version}); {arch}"
59
61
 
60
62
  return (
61
- f"Habiticalib/{version("habiticalib")} ({os_info}) "
63
+ f"Habiticalib/{__version__} ({os_info}) "
62
64
  f"aiohttp/{aiohttp.__version__} Python/{platform.python_version()} "
63
65
  " +https://github.com/tr4nt0r/habiticalib)"
64
66
  )
@@ -106,10 +108,31 @@ def get_x_client(x_client: str | None = None) -> str:
106
108
 
107
109
  return x_client
108
110
 
109
- return f"{DEVELOPER_ID} - Habiticalib/{version("habiticalib")}"
111
+ return f"{DEVELOPER_ID} - Habiticalib/{__version__}"
110
112
 
111
113
 
112
114
  def extract_user_styles(user_data: HabiticaUserResponse) -> UserStyles:
113
115
  """Extract user styles from a user data object."""
114
116
  data: UserData = user_data.data
115
117
  return UserStyles.from_dict(asdict(data))
118
+
119
+
120
+ def deserialize_task(value: Any) -> Any: # noqa: PLR0911
121
+ """Recursively convert Enums to values, dates to ISO strings, UUIDs to strings."""
122
+
123
+ if is_dataclass(value) and not isinstance(value, type):
124
+ # Convert dataclass to dict and recursively deserialize
125
+ return deserialize_task(asdict(value))
126
+ if isinstance(value, Enum):
127
+ return value.value # Convert Enum to its value
128
+ if isinstance(value, uuid.UUID):
129
+ return str(value) # Convert UUID to string
130
+ if isinstance(value, datetime | date):
131
+ return value.isoformat() # Convert datetime/date to ISO string
132
+ if isinstance(value, list):
133
+ # Recursively apply deserialization to each item in the list
134
+ return [deserialize_task(item) for item in value]
135
+ if isinstance(value, dict):
136
+ # Recursively apply deserialization to each key-value pair in the dictionary
137
+ return {k: deserialize_task(v) for k, v in value.items()}
138
+ return value # Return other types unchanged