nys-client 0.1.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.
- nys_client-0.1.0/LICENSE +27 -0
- nys_client-0.1.0/MANIFEST.in +8 -0
- nys_client-0.1.0/PKG-INFO +92 -0
- nys_client-0.1.0/README.md +56 -0
- nys_client-0.1.0/nys_client/__init__.py +116 -0
- nys_client-0.1.0/nys_client/base.py +74 -0
- nys_client-0.1.0/nys_client/bot.py +132 -0
- nys_client-0.1.0/nys_client/client.py +139 -0
- nys_client-0.1.0/nys_client/events.py +83 -0
- nys_client-0.1.0/nys_client/inventory_view.py +106 -0
- nys_client-0.1.0/nys_client/job.py +107 -0
- nys_client-0.1.0/nys_client/loads.py +105 -0
- nys_client-0.1.0/nys_client/request.py +336 -0
- nys_client-0.1.0/nys_client/requests_and_jobs_view.py +95 -0
- nys_client-0.1.0/nys_client/skus.py +161 -0
- nys_client-0.1.0/nys_client/system.py +106 -0
- nys_client-0.1.0/nys_client/utils.py +14 -0
- nys_client-0.1.0/nys_client.egg-info/PKG-INFO +92 -0
- nys_client-0.1.0/nys_client.egg-info/SOURCES.txt +22 -0
- nys_client-0.1.0/nys_client.egg-info/dependency_links.txt +1 -0
- nys_client-0.1.0/nys_client.egg-info/requires.txt +7 -0
- nys_client-0.1.0/nys_client.egg-info/top_level.txt +1 -0
- nys_client-0.1.0/setup.cfg +4 -0
- nys_client-0.1.0/setup.py +34 -0
nys_client-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Noyes
|
|
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.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nys_client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client library for the Noyes API
|
|
5
|
+
Home-page: https://github.com/noyes/nys_client
|
|
6
|
+
Author: Noyes
|
|
7
|
+
Author-email: dev@noyes.com
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: requests>=2.28.1
|
|
20
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
21
|
+
Requires-Dist: typing-extensions>=4.12.2
|
|
22
|
+
Requires-Dist: pydantic<2.0.0,>=1.10.0
|
|
23
|
+
Requires-Dist: fastapi-pagination>=0.12.0
|
|
24
|
+
Requires-Dist: nys_constants>=0.1.0
|
|
25
|
+
Requires-Dist: nys_schemas>=0.1.0
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
36
|
+
|
|
37
|
+
# NysClient
|
|
38
|
+
|
|
39
|
+
A Python client library for the Noyes API.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install nys-client
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from nys_client import NysClient, TriggerStatus
|
|
51
|
+
|
|
52
|
+
# Initialize client
|
|
53
|
+
client = NysClient(api_key="your_api_key")
|
|
54
|
+
|
|
55
|
+
# Get requests with filtering
|
|
56
|
+
requests = client.requests.list(
|
|
57
|
+
status="ACCEPTED",
|
|
58
|
+
type="FULFILLMENT",
|
|
59
|
+
sort_by="created_at",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Create a new request
|
|
63
|
+
new_request = client.requests.create(
|
|
64
|
+
type="FULFILLMENT",
|
|
65
|
+
priority=10,
|
|
66
|
+
entities=[{
|
|
67
|
+
"entity_type": "SKU",
|
|
68
|
+
"sku_id": "SKU123",
|
|
69
|
+
"quantity": 1
|
|
70
|
+
}]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Get jobs with search
|
|
74
|
+
jobs = client.jobs.list(
|
|
75
|
+
quick_search="PICKING",
|
|
76
|
+
status="EXECUTING"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Trigger a job
|
|
80
|
+
client.trigger_job(
|
|
81
|
+
job_id="job-uuid",
|
|
82
|
+
trigger_status=TriggerStatus.SUCCEEDED_TRIGGER
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Development
|
|
87
|
+
|
|
88
|
+
This package is part of the Noyes monorepo. To install in development mode:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e .
|
|
92
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# NysClient
|
|
2
|
+
|
|
3
|
+
A Python client library for the Noyes API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install nys-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from nys_client import NysClient, TriggerStatus
|
|
15
|
+
|
|
16
|
+
# Initialize client
|
|
17
|
+
client = NysClient(api_key="your_api_key")
|
|
18
|
+
|
|
19
|
+
# Get requests with filtering
|
|
20
|
+
requests = client.requests.list(
|
|
21
|
+
status="ACCEPTED",
|
|
22
|
+
type="FULFILLMENT",
|
|
23
|
+
sort_by="created_at",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Create a new request
|
|
27
|
+
new_request = client.requests.create(
|
|
28
|
+
type="FULFILLMENT",
|
|
29
|
+
priority=10,
|
|
30
|
+
entities=[{
|
|
31
|
+
"entity_type": "SKU",
|
|
32
|
+
"sku_id": "SKU123",
|
|
33
|
+
"quantity": 1
|
|
34
|
+
}]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Get jobs with search
|
|
38
|
+
jobs = client.jobs.list(
|
|
39
|
+
quick_search="PICKING",
|
|
40
|
+
status="EXECUTING"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Trigger a job
|
|
44
|
+
client.trigger_job(
|
|
45
|
+
job_id="job-uuid",
|
|
46
|
+
trigger_status=TriggerStatus.SUCCEEDED_TRIGGER
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Development
|
|
51
|
+
|
|
52
|
+
This package is part of the Noyes monorepo. To install in development mode:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install -e .
|
|
56
|
+
```
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A Python client library for the Noyes API.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
>>> from nys_client import NysClient, RequestType, TriggerStatus
|
|
6
|
+
>>>
|
|
7
|
+
>>> # Initialize client
|
|
8
|
+
>>> client = NysClient(api_key="your_api_key")
|
|
9
|
+
>>>
|
|
10
|
+
>>> # Create a request
|
|
11
|
+
>>> new_request = client.requests.create( #TODO
|
|
12
|
+
... request_type=RequestType.FULFILLMENT,
|
|
13
|
+
... entities=[("123", 1)]
|
|
14
|
+
... )
|
|
15
|
+
>>>
|
|
16
|
+
>>> # Search for jobs
|
|
17
|
+
>>> jobs = client.jobs.search(
|
|
18
|
+
... filters={"status": "EXECUTING", "type": "PICKING"}
|
|
19
|
+
... )
|
|
20
|
+
>>>
|
|
21
|
+
>>> # Trigger a job
|
|
22
|
+
>>> client.jobs.trigger(
|
|
23
|
+
... job_id="job-123",
|
|
24
|
+
... trigger_status=TriggerStatus.SUCCEEDED_TRIGGER
|
|
25
|
+
... )
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from .client import NysClient
|
|
29
|
+
from .system import SystemResource
|
|
30
|
+
from .bot import BotResource
|
|
31
|
+
|
|
32
|
+
# Re-export commonly used types from nys_schemas and nys_constants
|
|
33
|
+
from nys_constants.nys_constants import (
|
|
34
|
+
RequestType,
|
|
35
|
+
RequestStatus,
|
|
36
|
+
JobType,
|
|
37
|
+
JobStatus,
|
|
38
|
+
TriggerStatus,
|
|
39
|
+
SortOrder,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
from nys_schemas.request_schema import (
|
|
43
|
+
RequestResponseComposite,
|
|
44
|
+
RequestFilter,
|
|
45
|
+
FulfillmentRequestCreateInput,
|
|
46
|
+
FetchRequestCreateInput,
|
|
47
|
+
ReplenishmentRequestCreateInput,
|
|
48
|
+
OnboardingRequestCreateInput,
|
|
49
|
+
OffboardingRequestCreateInput,
|
|
50
|
+
RFIDWriteRequestCreateInput,
|
|
51
|
+
PauseRequestCreateInput,
|
|
52
|
+
ResumeRequestCreateInput,
|
|
53
|
+
)
|
|
54
|
+
from nys_schemas.job_schema import (
|
|
55
|
+
JobResponse,
|
|
56
|
+
JobFilter,
|
|
57
|
+
)
|
|
58
|
+
from nys_schemas.bot_schema import (
|
|
59
|
+
Bot,
|
|
60
|
+
BotResponse,
|
|
61
|
+
BotChargingRequest,
|
|
62
|
+
BotChargingResponse,
|
|
63
|
+
BotStopChargingRequest,
|
|
64
|
+
BotStopChargingResponse,
|
|
65
|
+
BotRemoveResponse,
|
|
66
|
+
)
|
|
67
|
+
from nys_schemas.load_schema import (
|
|
68
|
+
LoadResponse,
|
|
69
|
+
LoadCreate,
|
|
70
|
+
LoadPatch,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
__all__ = [
|
|
74
|
+
# Main client class
|
|
75
|
+
'NysClient',
|
|
76
|
+
|
|
77
|
+
# Response models - Core entities
|
|
78
|
+
'RequestResponseComposite',
|
|
79
|
+
'JobResponse',
|
|
80
|
+
'BotResponse',
|
|
81
|
+
|
|
82
|
+
# Request/Input models
|
|
83
|
+
'RequestCreateInputGeneric',
|
|
84
|
+
'RequestFilter',
|
|
85
|
+
'JobFilter',
|
|
86
|
+
'BotChargingRequest',
|
|
87
|
+
'BotChargingResponse',
|
|
88
|
+
'BotStopChargingRequest',
|
|
89
|
+
'BotStopChargingResponse',
|
|
90
|
+
'BotRemoveResponse',
|
|
91
|
+
'Bot',
|
|
92
|
+
'FetchRequestCreateInput',
|
|
93
|
+
'FulfillmentRequestCreateInput',
|
|
94
|
+
'ReplenishmentRequestCreateInput',
|
|
95
|
+
'OnboardingRequestCreateInput',
|
|
96
|
+
'OffboardingRequestCreateInput',
|
|
97
|
+
'RFIDWriteRequestCreateInput',
|
|
98
|
+
'PauseRequestCreateInput',
|
|
99
|
+
'ResumeRequestCreateInput',
|
|
100
|
+
|
|
101
|
+
# Load models
|
|
102
|
+
'LoadResponse',
|
|
103
|
+
'LoadCreate',
|
|
104
|
+
'LoadPatch',
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# Enums and Constants
|
|
108
|
+
'RequestType',
|
|
109
|
+
'RequestStatus',
|
|
110
|
+
'JobType',
|
|
111
|
+
'JobStatus',
|
|
112
|
+
'TriggerStatus',
|
|
113
|
+
'SortOrder',
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union, Tuple
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from nys_constants.nys_constants import SortOrder
|
|
6
|
+
from .utils import prepare_filter_value
|
|
7
|
+
|
|
8
|
+
T = TypeVar('T', bound=BaseModel)
|
|
9
|
+
FilterType = TypeVar('FilterType', bound=BaseModel)
|
|
10
|
+
|
|
11
|
+
class BaseResource(Generic[T]):
|
|
12
|
+
"""Base class for API resources. e.g. Job, Request, Sku, etc"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, client: 'NysClient'):
|
|
15
|
+
self._client = client
|
|
16
|
+
|
|
17
|
+
#TODO consider renaming to _get
|
|
18
|
+
def _search(
|
|
19
|
+
self,
|
|
20
|
+
*,
|
|
21
|
+
endpoint: str,
|
|
22
|
+
response_model: Type[T],
|
|
23
|
+
filters: Optional[FilterType] = None,
|
|
24
|
+
search: Optional[str] = None,
|
|
25
|
+
sort_by: Optional[Union[str, List[Tuple[str, SortOrder]]]] = None,
|
|
26
|
+
page: int = 1,
|
|
27
|
+
size: int = 50
|
|
28
|
+
) -> List[T]:
|
|
29
|
+
"""Generic search method used by all resource-specific search methods."""
|
|
30
|
+
params: Dict[str, Any] = {
|
|
31
|
+
"page": page,
|
|
32
|
+
"size": size
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Add sorting parameters if provided
|
|
36
|
+
if sort_by:
|
|
37
|
+
if isinstance(sort_by, str):
|
|
38
|
+
params["sort_by"] = sort_by
|
|
39
|
+
else:
|
|
40
|
+
# Convert list of tuples to comma-separated string with prefixes
|
|
41
|
+
sort_str = ','.join(
|
|
42
|
+
f"{'-' if order == SortOrder.DESC else ''}{field}"
|
|
43
|
+
for field, order in sort_by
|
|
44
|
+
)
|
|
45
|
+
params["sort_by"] = sort_str
|
|
46
|
+
|
|
47
|
+
# Add search fields if provided
|
|
48
|
+
if search:
|
|
49
|
+
if isinstance(search, str):
|
|
50
|
+
# leave the validation to the API
|
|
51
|
+
params['search'] = search
|
|
52
|
+
else:
|
|
53
|
+
raise ValueError("search must be a string")
|
|
54
|
+
|
|
55
|
+
# Add filters if provided
|
|
56
|
+
if filters:
|
|
57
|
+
# Convert filter model to dict and remove None values
|
|
58
|
+
filter_dict = filters.dict(exclude_unset=True, exclude_defaults=True)
|
|
59
|
+
|
|
60
|
+
# Add each non-None filter to params, converting values as needed
|
|
61
|
+
for field_name, value in filter_dict.items():
|
|
62
|
+
if value is not None:
|
|
63
|
+
params[field_name] = prepare_filter_value(value)
|
|
64
|
+
|
|
65
|
+
# Make API request
|
|
66
|
+
response = self._client._request(
|
|
67
|
+
method="GET",
|
|
68
|
+
endpoint=endpoint,
|
|
69
|
+
params=params
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Parse response into list of model instances
|
|
73
|
+
data = response.json()
|
|
74
|
+
return [response_model(**item) for item in data["items"]]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
from nys_schemas.bot_schema import (
|
|
4
|
+
Bot,
|
|
5
|
+
BotResponse,
|
|
6
|
+
BotFilter,
|
|
7
|
+
BotSort,
|
|
8
|
+
BotChargingRequest,
|
|
9
|
+
BotChargingResponse,
|
|
10
|
+
BotStopChargingRequest,
|
|
11
|
+
BotStopChargingResponse,
|
|
12
|
+
BotRemoveResponse
|
|
13
|
+
)
|
|
14
|
+
from .base import BaseResource
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
'BotResource', # The main resource class
|
|
18
|
+
'Bot',
|
|
19
|
+
'BotResponse',
|
|
20
|
+
'BotFilter',
|
|
21
|
+
'BotSort',
|
|
22
|
+
'BotChargingRequest',
|
|
23
|
+
'BotChargingResponse',
|
|
24
|
+
'BotStopChargingRequest',
|
|
25
|
+
'BotStopChargingResponse',
|
|
26
|
+
'BotRemoveResponse'
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
class BotResource(BaseResource[BotResponse]):
|
|
30
|
+
"""Resource for interacting with Bots."""
|
|
31
|
+
|
|
32
|
+
def get_bots(self) -> List[BotResponse]:
|
|
33
|
+
"""
|
|
34
|
+
Get all bots.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List[BotResponse]: List of all bots in the system
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
>>> bots = client.bots.get_bots()
|
|
41
|
+
>>> for bot in bots:
|
|
42
|
+
... print(f"Bot ID: {bot.idx}, Level: {bot.level_id}, Battery: {bot.battery_level}%")
|
|
43
|
+
"""
|
|
44
|
+
response = self._client._request(
|
|
45
|
+
method="GET",
|
|
46
|
+
endpoint="api/v2/bots"
|
|
47
|
+
)
|
|
48
|
+
return [BotResponse(**item) for item in response.json()]
|
|
49
|
+
|
|
50
|
+
def go_charging(self, charging_request: BotChargingRequest) -> BotChargingResponse:
|
|
51
|
+
"""
|
|
52
|
+
Send specified bots to charging stations.
|
|
53
|
+
|
|
54
|
+
This endpoint proxies the request to the brain API which handles the ROS communication.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
charging_request: The request parameters including bot_idxs, force flag,
|
|
58
|
+
block_until_done flag, and timeout
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
BotChargingResponse: Response with success/failure information
|
|
62
|
+
|
|
63
|
+
Examples:
|
|
64
|
+
>>> from nys_client.bot import BotChargingRequest
|
|
65
|
+
>>> response = client.bots.go_charging(
|
|
66
|
+
... BotChargingRequest(
|
|
67
|
+
... bot_ids=["1", "2"],
|
|
68
|
+
... force=True,
|
|
69
|
+
... block_until_done=False,
|
|
70
|
+
... timeout=180
|
|
71
|
+
... )
|
|
72
|
+
... )
|
|
73
|
+
>>> print(f"Success: {response.success}, Message: {response.message}")
|
|
74
|
+
"""
|
|
75
|
+
response = self._client._request(
|
|
76
|
+
method="POST",
|
|
77
|
+
endpoint="api/v2/bot/go_charging",
|
|
78
|
+
json=charging_request.dict()
|
|
79
|
+
)
|
|
80
|
+
return BotChargingResponse(**response.json())
|
|
81
|
+
|
|
82
|
+
def stop_charging(self, stop_charging_request: BotStopChargingRequest) -> BotStopChargingResponse:
|
|
83
|
+
"""
|
|
84
|
+
Stop charging for specified bots.
|
|
85
|
+
|
|
86
|
+
This endpoint proxies the request to the brain API which handles the ROS communication.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
stop_charging_request: The request parameters including bot_idxs and force flag
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
BotStopChargingResponse: Response with success/failure information
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
>>> from nys_client.bot import BotStopChargingRequest
|
|
96
|
+
>>> response = client.bots.stop_charging(
|
|
97
|
+
... BotStopChargingRequest(
|
|
98
|
+
... bot_ids=["1", "2"],
|
|
99
|
+
... force=True
|
|
100
|
+
... )
|
|
101
|
+
... )
|
|
102
|
+
>>> print(f"Success: {response.success}, Message: {response.message}")
|
|
103
|
+
"""
|
|
104
|
+
response = self._client._request(
|
|
105
|
+
method="POST",
|
|
106
|
+
endpoint="api/v2/bot/stop_charging",
|
|
107
|
+
json=stop_charging_request.dict()
|
|
108
|
+
)
|
|
109
|
+
return BotStopChargingResponse(**response.json())
|
|
110
|
+
|
|
111
|
+
def remove_bots(self, bot_ids: List[str]) -> BotRemoveResponse:
|
|
112
|
+
"""
|
|
113
|
+
Remove specified bots from the database.
|
|
114
|
+
|
|
115
|
+
Warning: This is not the usual offboarding process. Use at your own risk!
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
bot_ids: List of bot IDs to remove from database; use ["*"] for all bots
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
BotRemoveResponse: Response with success/failure information
|
|
122
|
+
|
|
123
|
+
Examples:
|
|
124
|
+
>>> response = client.bots.remove_bots(["1", "2"])
|
|
125
|
+
>>> print(f"Success: {response.success}, Message: {response.message}")
|
|
126
|
+
"""
|
|
127
|
+
response = self._client._request(
|
|
128
|
+
method="DELETE",
|
|
129
|
+
endpoint="api/v2/bot",
|
|
130
|
+
params={"bot_ids": bot_ids}
|
|
131
|
+
)
|
|
132
|
+
return BotRemoveResponse(**response.json())
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import logging
|
|
3
|
+
import requests
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from .request import RequestResource
|
|
7
|
+
from .job import JobResource
|
|
8
|
+
from .skus import SkuResource
|
|
9
|
+
from .loads import LoadResource
|
|
10
|
+
from .requests_and_jobs_view import RequestsAndJobsViewResource
|
|
11
|
+
from .inventory_view import InventoryViewResource
|
|
12
|
+
from .system import SystemResource
|
|
13
|
+
from .bot import BotResource
|
|
14
|
+
from .events import EventResource
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
from requests.adapters import HTTPAdapter
|
|
17
|
+
from urllib3.util.retry import Retry
|
|
18
|
+
|
|
19
|
+
class NysClient:
|
|
20
|
+
"""Client for interacting with the Noyes API."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
api_key: str,
|
|
25
|
+
base_url: str = "http://noyes-api:8003",
|
|
26
|
+
# TODO: Increased timeout to 60 seconds for long-running requests. Eg. stop_brain endpoint
|
|
27
|
+
timeout: int = 60 # seconds
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Initialize the NysClient.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
api_key: Your API key
|
|
34
|
+
base_url: Base URL of the API (default: http://noyes-api:8003)
|
|
35
|
+
timeout: Request timeout in seconds (default: 60)
|
|
36
|
+
"""
|
|
37
|
+
self.api_key = api_key
|
|
38
|
+
self.base_url = base_url.rstrip("/")
|
|
39
|
+
self.timeout = timeout
|
|
40
|
+
|
|
41
|
+
self._headers = {
|
|
42
|
+
"Authorization": f"Bearer {api_key}",
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Initialize resources
|
|
47
|
+
self._requests = RequestResource(self)
|
|
48
|
+
self._jobs = JobResource(self)
|
|
49
|
+
self._skus = SkuResource(self)
|
|
50
|
+
self._loads = LoadResource(self)
|
|
51
|
+
self._requests_and_jobs_view = RequestsAndJobsViewResource(self)
|
|
52
|
+
self._inventory_view = InventoryViewResource(self)
|
|
53
|
+
self._system = SystemResource(self)
|
|
54
|
+
self._bots = BotResource(self)
|
|
55
|
+
self._events = EventResource(self)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def requests(self) -> RequestResource:
|
|
59
|
+
"""Access request-related operations."""
|
|
60
|
+
return self._requests
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def jobs(self) -> JobResource:
|
|
64
|
+
"""Access job-related operations."""
|
|
65
|
+
return self._jobs
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def skus(self) -> SkuResource:
|
|
69
|
+
"""Access SKU-related operations."""
|
|
70
|
+
return self._skus
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def loads(self) -> LoadResource:
|
|
74
|
+
"""Access load-related operations."""
|
|
75
|
+
return self._loads
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def requests_and_jobs_view(self) -> RequestsAndJobsViewResource:
|
|
79
|
+
"""Access requests and jobs view operations."""
|
|
80
|
+
return self._requests_and_jobs_view
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def inventory_view(self) -> InventoryViewResource:
|
|
84
|
+
"""Access inventory view operations."""
|
|
85
|
+
return self._inventory_view
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def system(self) -> SystemResource:
|
|
89
|
+
"""Access system-related operations."""
|
|
90
|
+
return self._system
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def bots(self) -> BotResource:
|
|
94
|
+
"""Access bot-related operations."""
|
|
95
|
+
return self._bots
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def events(self) -> EventResource:
|
|
99
|
+
"""Access event-related operations."""
|
|
100
|
+
return self._events
|
|
101
|
+
|
|
102
|
+
def _request(
|
|
103
|
+
self,
|
|
104
|
+
method: str,
|
|
105
|
+
endpoint: str,
|
|
106
|
+
params: Optional[dict] = None,
|
|
107
|
+
json: Optional[dict] = None,
|
|
108
|
+
**kwargs
|
|
109
|
+
) -> requests.Response:
|
|
110
|
+
"""
|
|
111
|
+
Make a request to the API.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
method: HTTP method (GET, POST, etc.)
|
|
115
|
+
endpoint: API endpoint
|
|
116
|
+
params: Query parameters
|
|
117
|
+
json: JSON body
|
|
118
|
+
**kwargs: Additional arguments to pass to requests
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
requests.Response: Response from the API
|
|
122
|
+
"""
|
|
123
|
+
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
124
|
+
response = requests.request(
|
|
125
|
+
method=method,
|
|
126
|
+
url=url,
|
|
127
|
+
params=params,
|
|
128
|
+
json=json,
|
|
129
|
+
timeout=self.timeout,
|
|
130
|
+
headers=self._headers,
|
|
131
|
+
**kwargs
|
|
132
|
+
)
|
|
133
|
+
try:
|
|
134
|
+
response.raise_for_status()
|
|
135
|
+
except requests.HTTPError as e:
|
|
136
|
+
logger.error(f"HTTP error occurred: {e}")
|
|
137
|
+
logger.error(f"Response content: {response.text}")
|
|
138
|
+
raise e
|
|
139
|
+
return response
|