ritten-python-sdk 1.0.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 (26) hide show
  1. ritten_python_sdk-1.0.0/LICENSE +21 -0
  2. ritten_python_sdk-1.0.0/PKG-INFO +62 -0
  3. ritten_python_sdk-1.0.0/README.md +37 -0
  4. ritten_python_sdk-1.0.0/pyproject.toml +31 -0
  5. ritten_python_sdk-1.0.0/src/ritten/__init__.py +73 -0
  6. ritten_python_sdk-1.0.0/src/ritten/auth.py +91 -0
  7. ritten_python_sdk-1.0.0/src/ritten/config.py +85 -0
  8. ritten_python_sdk-1.0.0/src/ritten/decorators.py +45 -0
  9. ritten_python_sdk-1.0.0/src/ritten/exceptions.py +95 -0
  10. ritten_python_sdk-1.0.0/src/ritten/resources/__init__.py +32 -0
  11. ritten_python_sdk-1.0.0/src/ritten/resources/calendar.py +32 -0
  12. ritten_python_sdk-1.0.0/src/ritten/resources/cases.py +51 -0
  13. ritten_python_sdk-1.0.0/src/ritten/resources/contacts.py +52 -0
  14. ritten_python_sdk-1.0.0/src/ritten/resources/facilities.py +54 -0
  15. ritten_python_sdk-1.0.0/src/ritten/resources/forms.py +32 -0
  16. ritten_python_sdk-1.0.0/src/ritten/resources/insurance.py +29 -0
  17. ritten_python_sdk-1.0.0/src/ritten/resources/organizations.py +66 -0
  18. ritten_python_sdk-1.0.0/src/ritten/resources/patients.py +115 -0
  19. ritten_python_sdk-1.0.0/src/ritten/resources/programs.py +51 -0
  20. ritten_python_sdk-1.0.0/src/ritten/resources/resource.py +25 -0
  21. ritten_python_sdk-1.0.0/src/ritten/resources/users.py +51 -0
  22. ritten_python_sdk-1.0.0/src/ritten/ritten.py +171 -0
  23. ritten_python_sdk-1.0.0/src/ritten/storage/__init__.py +7 -0
  24. ritten_python_sdk-1.0.0/src/ritten/storage/memory_storage.py +22 -0
  25. ritten_python_sdk-1.0.0/src/ritten/storage/token_storage.py +25 -0
  26. ritten_python_sdk-1.0.0/src/ritten/utils.py +15 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wesley Gonçalves
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,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: ritten-python-sdk
3
+ Version: 1.0.0
4
+ Summary: Python SDK for connecting applications to the Ritten EMR Platform. Simplifies integration with the Behavioral Health Operations API.
5
+ License-File: LICENSE
6
+ Keywords: ritten,emr,healthcare,sdk,python
7
+ Author: Wesley Gonçalves
8
+ Author-email: dev@wesleygoncalves.com
9
+ Requires-Python: >=3.10
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Dist: httpx (>=0.28.1,<0.29.0)
17
+ Requires-Dist: pydantic (>=2.13.4,<3.0.0)
18
+ Project-URL: Documentation, https://github.com/wesleygoncalves/ritten-python-sdk/blob/main/README.md
19
+ Project-URL: Homepage, https://github.com/wesleygoncalves/ritten-python-sdk
20
+ Project-URL: changelog, https://github.com/wesleygoncalves/ritten-python-sdk/blob/main/CHANGELOG.md
21
+ Project-URL: issues, https://github.com/wesleygoncalves/ritten-python-sdk/issues
22
+ Project-URL: source, https://github.com/wesleygoncalves/ritten-python-sdk
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Ritten API Integration
26
+
27
+ Python SDK for connecting applications to the [Ritten EMR Platform](https://docs.ritten.io/). This library simplifies integration with Ritten's Behavioral Health Operations API, allowing developers to use Ritten API seamlessly from any Python environment.
28
+
29
+ Ritten API specifications:
30
+
31
+ - [Documentation](https://docs.ritten.io/)
32
+ - Version: 1
33
+
34
+ ## Installation
35
+
36
+ The SDK requires **Python 3.10 or higher**. Install it directly via `pip`:
37
+
38
+ ```bash
39
+ pip install ritten-python-sdk
40
+
41
+ # Using poetry
42
+ poetry add ritten-python-sdk
43
+
44
+ # Using pipenv
45
+ pipenv install ritten-python-sdk
46
+ ```
47
+
48
+ ## Quickstart Guide
49
+
50
+ To get started, you must first obtain your integrator `client_id` and `client_secret` from your Ritten administrator portal.
51
+
52
+ ---
53
+
54
+ ## Advanced Resources
55
+
56
+ - Review the official [Ritten API Documentation](https://docs.ritten.io/) for explicit payload schemas.
57
+ - Check the `/examples` directory inside this repository for production-ready boilerplate code.
58
+
59
+ ## About the Author
60
+
61
+ This SDK is developed and maintained by [Wesley Gonçalves](https://github.com/wesleygoncalves) under MIT license. Contributions and feedback are welcome!
62
+
@@ -0,0 +1,37 @@
1
+ # Ritten API Integration
2
+
3
+ Python SDK for connecting applications to the [Ritten EMR Platform](https://docs.ritten.io/). This library simplifies integration with Ritten's Behavioral Health Operations API, allowing developers to use Ritten API seamlessly from any Python environment.
4
+
5
+ Ritten API specifications:
6
+
7
+ - [Documentation](https://docs.ritten.io/)
8
+ - Version: 1
9
+
10
+ ## Installation
11
+
12
+ The SDK requires **Python 3.10 or higher**. Install it directly via `pip`:
13
+
14
+ ```bash
15
+ pip install ritten-python-sdk
16
+
17
+ # Using poetry
18
+ poetry add ritten-python-sdk
19
+
20
+ # Using pipenv
21
+ pipenv install ritten-python-sdk
22
+ ```
23
+
24
+ ## Quickstart Guide
25
+
26
+ To get started, you must first obtain your integrator `client_id` and `client_secret` from your Ritten administrator portal.
27
+
28
+ ---
29
+
30
+ ## Advanced Resources
31
+
32
+ - Review the official [Ritten API Documentation](https://docs.ritten.io/) for explicit payload schemas.
33
+ - Check the `/examples` directory inside this repository for production-ready boilerplate code.
34
+
35
+ ## About the Author
36
+
37
+ This SDK is developed and maintained by [Wesley Gonçalves](https://github.com/wesleygoncalves) under MIT license. Contributions and feedback are welcome!
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "ritten-python-sdk"
3
+ version = "1.0.0"
4
+ description = "Python SDK for connecting applications to the Ritten EMR Platform. Simplifies integration with the Behavioral Health Operations API."
5
+ keywords = ["ritten", "emr", "healthcare", "sdk", "python"]
6
+ authors = [
7
+ {name = "Wesley Gonçalves",email = "dev@wesleygoncalves.com"}
8
+ ]
9
+ license-files = ["LICENSE"]
10
+ readme = "README.md"
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "httpx >=0.28.1,<0.29.0",
14
+ "pydantic >=2.13.4,<3.0.0"
15
+ ]
16
+
17
+ [project.urls]
18
+ homepage = "https://github.com/wesleygoncalves/ritten-python-sdk"
19
+ source = "https://github.com/wesleygoncalves/ritten-python-sdk"
20
+ issues = "https://github.com/wesleygoncalves/ritten-python-sdk/issues"
21
+ changelog = "https://github.com/wesleygoncalves/ritten-python-sdk/blob/main/CHANGELOG.md"
22
+ documentation = "https://github.com/wesleygoncalves/ritten-python-sdk/blob/main/README.md"
23
+
24
+ [tool.poetry]
25
+ packages = [
26
+ { include = "ritten", from = "src" },
27
+ ]
28
+
29
+ [build-system]
30
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
31
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,73 @@
1
+ """
2
+ Ritten SDK.
3
+
4
+ :copyright: (c) 2026 by Wesley Gonçalves.
5
+ :license: MIT, see LICENSE for more details.
6
+ """
7
+
8
+ from importlib.metadata import version, PackageNotFoundError
9
+
10
+ from ritten.ritten import Ritten
11
+ from ritten.auth import Auth
12
+ from ritten.config import Config
13
+ from ritten.exceptions import (
14
+ RittenError,
15
+ RittenClientError,
16
+ RittenConnectionError,
17
+ RittenAPIError,
18
+ RittenAuthError,
19
+ RittenUnauthorizedError,
20
+ RittenValidationError,
21
+ RittenNotFoundError,
22
+ RittenRateLimitError,
23
+ RittenServerError,
24
+ )
25
+ from ritten.resources import (
26
+ Resource,
27
+ Calendar,
28
+ Cases,
29
+ Contacts,
30
+ Facilities,
31
+ Forms,
32
+ Insurance,
33
+ Organizations,
34
+ Patients,
35
+ Programs,
36
+ Users,
37
+ )
38
+ from ritten.storage import TokenStorage, MemoryStorage
39
+
40
+ __all__ = [
41
+ "Ritten",
42
+ "Auth",
43
+ "Config",
44
+ "TokenStorage",
45
+ "MemoryStorage",
46
+ "RittenError",
47
+ "RittenClientError",
48
+ "RittenConnectionError",
49
+ "RittenAPIError",
50
+ "RittenAuthError",
51
+ "RittenUnauthorizedError",
52
+ "RittenValidationError",
53
+ "RittenNotFoundError",
54
+ "RittenRateLimitError",
55
+ "RittenServerError",
56
+ "Resource",
57
+ "Calendar",
58
+ "Cases",
59
+ "Contacts",
60
+ "Facilities",
61
+ "Forms",
62
+ "Insurance",
63
+ "Organizations",
64
+ "Patients",
65
+ "Programs",
66
+ "Users",
67
+ ]
68
+
69
+
70
+ try:
71
+ __version__ = version("ritten-python-sdk")
72
+ except PackageNotFoundError:
73
+ __version__ = "unknown"
@@ -0,0 +1,91 @@
1
+ """
2
+ Ritten SDK Authentication Handler.
3
+
4
+ :copyright: (c) 2026 by Wesley Gonçalves.
5
+ :license: MIT, see LICENSE for more details.
6
+ """
7
+
8
+ import httpx
9
+ from ritten.config import Config
10
+ from ritten.exceptions import RittenAuthError, RittenUnauthorizedError
11
+
12
+
13
+ class Auth(httpx.Auth):
14
+ """Authentication handler for the Ritten API."""
15
+
16
+ def __init__(self, config: Config):
17
+ self.config = config
18
+
19
+ @property
20
+ def access_token(self) -> str | None:
21
+ """Fetch the access token."""
22
+ token = self.config.storage.get_token()
23
+ return token
24
+
25
+ @access_token.setter
26
+ def access_token(self, value: str) -> Auth:
27
+ """Set a new access token."""
28
+ self.config.storage.set_token(value)
29
+
30
+ return self
31
+
32
+ @property
33
+ def client_id(self) -> str | None:
34
+ """Fetch the client ID for authentication."""
35
+ return self.config.client_id
36
+
37
+ @property
38
+ def client_secret(self) -> str | None:
39
+ """Fetch the client secret for authentication."""
40
+ return self.config.client_secret
41
+
42
+ def _fetch_new_token(self) -> None:
43
+ """Fetch a new access token using client credentials."""
44
+ if not self.config.client_id or not self.config.client_secret:
45
+ raise RittenAuthError(
46
+ "Client ID and Client Secret are required to fetch a new access token."
47
+ )
48
+
49
+ response = httpx.post(
50
+ f"{self.config.base_url}/oauth/token",
51
+ json={
52
+ "grant_type": "client_credentials",
53
+ "client_id": self.config.client_id,
54
+ "client_secret": self.config.client_secret,
55
+ "audience": self.config.audience,
56
+ },
57
+ )
58
+
59
+ if response.status_code == 200:
60
+ self.access_token = response.json()["access_token"]
61
+
62
+ def add_bearer_token(self, request: httpx.Request) -> httpx.Request:
63
+ """Add the Bearer token to the request headers."""
64
+ request.headers["Authorization"] = f"Bearer {self.access_token}"
65
+ return request
66
+
67
+ def auth_flow(self, request: httpx.Request):
68
+ """Controls the entire authentication workflow."""
69
+
70
+ if not self.access_token:
71
+ self._fetch_new_token()
72
+ self.add_bearer_token(request)
73
+
74
+ response = yield request
75
+
76
+ # Handle Expiration (401 Unauthorized)
77
+ if response.status_code == 401:
78
+ self._fetch_new_token()
79
+ self.add_bearer_token(request)
80
+
81
+ # replays the API call
82
+ retry_request = yield request
83
+
84
+ # if still fails
85
+ if retry_request.status_code == 401:
86
+ retry_request.read()
87
+ response = retry_request.json()
88
+ raise RittenUnauthorizedError(
89
+ message=response.get("message", "Unauthorized request."),
90
+ status_code=retry_request.status_code,
91
+ )
@@ -0,0 +1,85 @@
1
+ """
2
+ Ritten SDK Configuration.
3
+
4
+ :copyright: (c) 2026 by Wesley Gonçalves.
5
+ :license: MIT, see LICENSE for more details.
6
+ """
7
+
8
+ from typing import Any
9
+ from pydantic import (
10
+ BaseModel,
11
+ Field,
12
+ field_validator,
13
+ ConfigDict,
14
+ )
15
+ from ritten.storage import TokenStorage, MemoryStorage
16
+ from ritten.exceptions import RittenError
17
+
18
+
19
+ class Config(BaseModel):
20
+ """Configuration for the Ritten SDK client."""
21
+
22
+ model_config = ConfigDict(arbitrary_types_allowed=True)
23
+
24
+ base_url: str = Field(
25
+ default="https://api.ritten.io/v1",
26
+ description="Base URL for the Ritten API.",
27
+ )
28
+
29
+ audience: str = Field(
30
+ default="https://external-api.ritten.io",
31
+ description="Audience for OAuth token requests.",
32
+ )
33
+
34
+ # Authentication
35
+ tenant_id: str | None = Field(
36
+ default="ritclinic",
37
+ description="Tenant ID for the Ritten API.",
38
+ )
39
+
40
+ client_id: str | None = Field(
41
+ default=None,
42
+ description="Client ID for authentication.",
43
+ )
44
+ client_secret: str | None = Field(
45
+ default=None,
46
+ description="Client secret for authentication.",
47
+ )
48
+
49
+ storage: TokenStorage = Field(
50
+ default=MemoryStorage(),
51
+ description="Token storage mechanism. Defaults to in-memory storage.",
52
+ )
53
+
54
+ @field_validator("storage", mode="after")
55
+ @classmethod
56
+ def validate_storage_interface(cls, value: Any) -> Any:
57
+ """Validates that the provided storage object implements the `TokenStorage` protocol if it is not None."""
58
+ if value is None:
59
+ return value
60
+
61
+ if not isinstance(value, TokenStorage):
62
+ raise RittenError(
63
+ "The provided storage object must implement the `TokenStorage` protocol."
64
+ )
65
+
66
+ return value
67
+
68
+ # Connection pooling and timeout settings
69
+ timeout: float = Field(
70
+ default=30.0,
71
+ description="The timeout in seconds for the HTTP client.",
72
+ )
73
+ max_connections: int = Field(
74
+ default=10,
75
+ description="Maximum number of concurrent connections in the pool.",
76
+ )
77
+ max_keepalive_connections: int = Field(
78
+ default=5,
79
+ description="Maximum number of keep-alive connections to maintain.",
80
+ )
81
+
82
+ keepalive_expiry: float = Field(
83
+ default=5.0,
84
+ description="Time in seconds before a keep-alive connection is closed. Keep this low for serverless.",
85
+ )
@@ -0,0 +1,45 @@
1
+ from functools import wraps
2
+ import httpx
3
+ from pydantic import ValidationError
4
+ import json
5
+ from ritten.exceptions import (
6
+ RittenError,
7
+ RittenConnectionError,
8
+ RittenValueError,
9
+ RittenParseError,
10
+ )
11
+
12
+
13
+ def exception_handler(func):
14
+ """
15
+ Decorator that translates third-party exceptions into native SDK exceptions.
16
+ """
17
+
18
+ @wraps(func)
19
+ def wrapper(*args, **kwargs):
20
+ try:
21
+ return func(*args, **kwargs)
22
+
23
+ # If it's a RittenError exception, reraise it.
24
+ except RittenError:
25
+ raise
26
+
27
+ # Network-level failures
28
+ except httpx.RequestError as e:
29
+ raise RittenConnectionError(f"A network error occurred: {str(e)}") from e
30
+
31
+ # Validation errors
32
+ except ValidationError as e:
33
+ raise RittenValueError(f"Data validation failed: {str(e)}") from e
34
+
35
+ # JSON parsing errors
36
+ except json.JSONDecodeError as e:
37
+ raise RittenParseError(
38
+ f"Failed to parse API response as JSON: {str(e)}"
39
+ ) from e
40
+
41
+ # The Ultimate Catch-All for anything else
42
+ except Exception as e:
43
+ raise RittenError(f"An unexpected SDK error occurred: {str(e)}") from e
44
+
45
+ return wrapper
@@ -0,0 +1,95 @@
1
+ """
2
+ Ritten SDK Exceptions.
3
+
4
+ :copyright: (c) 2026 by Wesley Gonçalves.
5
+ :license: MIT, see LICENSE for more details.
6
+ """
7
+
8
+
9
+ class RittenError(Exception):
10
+ """The base exception for all Ritten SDK errors."""
11
+
12
+ pass
13
+
14
+
15
+ class RittenClientError(RittenError):
16
+ """Client is misconfigured or used incorrectly."""
17
+
18
+ pass
19
+
20
+
21
+ class RittenConnectionError(RittenError):
22
+ """SDK cannot reach the API."""
23
+
24
+ pass
25
+
26
+
27
+ class RittenValueError(RittenError):
28
+ """Invalid value provided to a method."""
29
+
30
+ pass
31
+
32
+
33
+ class RittenParseError(RittenError):
34
+ """Failed to parse API response."""
35
+
36
+ pass
37
+
38
+
39
+ class RittenAuthError(RittenError):
40
+ """Authentication error."""
41
+
42
+ pass
43
+
44
+
45
+ # HTTP Response Errors
46
+ class RittenAPIError(RittenError):
47
+ """Base class for errors returned by the Ritten API (HTTP 4xx and 5xx)."""
48
+
49
+ def __init__(self, message: str, status_code: int, payload: dict | None = None):
50
+ super().__init__(message)
51
+ self.status_code = status_code
52
+ self.payload = payload or {}
53
+
54
+
55
+ class RittenUnauthorizedError(RittenAPIError):
56
+ """401 Unauthorized or 403 Forbidden."""
57
+
58
+ pass
59
+
60
+
61
+ class RittenValidationError(RittenAPIError):
62
+ """400 Bad Request or 422 Unprocessable Entity."""
63
+
64
+ pass
65
+
66
+
67
+ class RittenNotFoundError(RittenAPIError):
68
+ """404 Not Found."""
69
+
70
+ pass
71
+
72
+
73
+ class RittenRateLimitError(RittenAPIError):
74
+ """429 Too Many Requests."""
75
+
76
+ pass
77
+
78
+
79
+ class RittenServerError(RittenAPIError):
80
+ """5xx Server Errors (e.g., 500, 502, 503)."""
81
+
82
+ pass
83
+
84
+
85
+ ERROR_MAP = {
86
+ 400: RittenValidationError,
87
+ 403: RittenUnauthorizedError,
88
+ 404: RittenNotFoundError,
89
+ 422: RittenValidationError,
90
+ 429: RittenRateLimitError,
91
+ 500: RittenServerError,
92
+ 502: RittenServerError,
93
+ 503: RittenServerError,
94
+ }
95
+ """Mapping of HTTP status codes to specific error classes for structured error handling."""
@@ -0,0 +1,32 @@
1
+ """
2
+ Ritten SDK Resources.
3
+
4
+ :copyright: (c) 2026 by Wesley Gonçalves.
5
+ :license: MIT, see LICENSE for more details.
6
+ """
7
+
8
+ from ritten.resources.resource import Resource
9
+ from ritten.resources.calendar import Calendar
10
+ from ritten.resources.cases import Cases
11
+ from ritten.resources.contacts import Contacts
12
+ from ritten.resources.facilities import Facilities
13
+ from ritten.resources.forms import Forms
14
+ from ritten.resources.insurance import Insurance
15
+ from ritten.resources.organizations import Organizations
16
+ from ritten.resources.patients import Patients
17
+ from ritten.resources.programs import Programs
18
+ from ritten.resources.users import Users
19
+
20
+ __all__ = [
21
+ "Resource",
22
+ "Calendar",
23
+ "Cases",
24
+ "Contacts",
25
+ "Facilities",
26
+ "Forms",
27
+ "Insurance",
28
+ "Organizations",
29
+ "Patients",
30
+ "Programs",
31
+ "Users",
32
+ ]
@@ -0,0 +1,32 @@
1
+ """
2
+ Ritten SDK Calendar Resource.
3
+
4
+ :copyright: (c) 2026 by Wesley Gonçalves.
5
+ :license: MIT, see LICENSE for more details.
6
+ """
7
+
8
+ import httpx
9
+ from typing import Dict, Any
10
+
11
+ from ritten.resources.resource import Resource
12
+
13
+
14
+ class Calendar(Resource):
15
+ """
16
+ Handles all interactions with the Ritten API `/calendar/events` endpoints.
17
+ """
18
+
19
+ def __init__(self, client: httpx.Client):
20
+ self._client = client
21
+ self._base_path = "/calendar/events"
22
+
23
+ def list(self, payload: Dict[str, Any]) -> Dict[str, Any]:
24
+ """
25
+ Queries calendar events based on specific criteria.
26
+ The POST body is the query object.
27
+ """
28
+ return self._client.post(f"{self._base_path}/list", json=payload).json()
29
+
30
+ def create(self, payload: Dict[str, Any]) -> Dict[str, Any]:
31
+ """Creates a new calendar event."""
32
+ return self._client.post(f"{self._base_path}", json=payload).json()
@@ -0,0 +1,51 @@
1
+ """
2
+ Ritten SDK Cases Resource.
3
+
4
+ :copyright: (c) 2026 by Wesley Gonçalves.
5
+ :license: MIT, see LICENSE for more details.
6
+ """
7
+
8
+ import httpx
9
+ from typing import Dict, Any
10
+
11
+ from ritten.resources.resource import Resource
12
+
13
+
14
+ class Cases(Resource):
15
+ """
16
+ Handles all interactions with the Ritten API `/cases` endpoints.
17
+ """
18
+
19
+ def __init__(self, client: httpx.Client):
20
+ self._client = client
21
+ self._base_path = "/cases"
22
+
23
+ def list(self, limit: int = 20, offset: int = 0) -> Dict[str, Any]:
24
+ """Lists cases in a clinic."""
25
+ params = {
26
+ "limit": limit,
27
+ "offset": offset,
28
+ }
29
+ return self._client.get(self._base_path, params=params).json()
30
+
31
+ def get(self, id: str) -> Dict[str, Any]:
32
+ """Retrieves a single case by its ID."""
33
+ return self._client.get(f"{self._base_path}/{id}").json()
34
+
35
+ def create(self, payload: Dict[str, Any]) -> Dict[str, Any]:
36
+ """Creates a new case."""
37
+ return self._client.post(self._base_path, json=payload).json()
38
+
39
+ def update(self, id: str, payload: Dict[str, Any]) -> Dict[str, Any]:
40
+ """Updates an existing case by ID."""
41
+ return self._client.patch(
42
+ f"{self._base_path}/{id}",
43
+ json=payload,
44
+ ).json()
45
+
46
+ def create_note(self, id: str, payload: Dict[str, Any]) -> Dict[str, Any]:
47
+ """Creates a note for a specific case."""
48
+ return self._client.post(
49
+ f"{self._base_path}/{id}/notes",
50
+ json=payload,
51
+ ).json()