channel3-sdk 0.1.0__py3-none-any.whl → 0.1.12__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.
Potentially problematic release.
This version of channel3-sdk might be problematic. Click here for more details.
- channel3_sdk/__init__.py +7 -5
- channel3_sdk/client.py +236 -3
- channel3_sdk/models.py +49 -40
- {channel3_sdk-0.1.0.dist-info → channel3_sdk-0.1.12.dist-info}/METADATA +80 -31
- channel3_sdk-0.1.12.dist-info/RECORD +7 -0
- channel3_sdk-0.1.0.dist-info/RECORD +0 -7
- {channel3_sdk-0.1.0.dist-info → channel3_sdk-0.1.12.dist-info}/WHEEL +0 -0
channel3_sdk/__init__.py
CHANGED
|
@@ -6,8 +6,9 @@ from .models import (
|
|
|
6
6
|
ProductDetail,
|
|
7
7
|
SearchFilters,
|
|
8
8
|
SearchRequest,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
SearchFilterPrice,
|
|
10
|
+
Brand,
|
|
11
|
+
Variant,
|
|
11
12
|
Price,
|
|
12
13
|
AvailabilityStatus,
|
|
13
14
|
)
|
|
@@ -20,7 +21,7 @@ from .exceptions import (
|
|
|
20
21
|
Channel3ConnectionError,
|
|
21
22
|
)
|
|
22
23
|
|
|
23
|
-
__version__ = "0.
|
|
24
|
+
__version__ = "0.2.0"
|
|
24
25
|
__all__ = [
|
|
25
26
|
# Clients
|
|
26
27
|
"Channel3Client",
|
|
@@ -30,8 +31,9 @@ __all__ = [
|
|
|
30
31
|
"ProductDetail",
|
|
31
32
|
"SearchFilters",
|
|
32
33
|
"SearchRequest",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
34
|
+
"SearchFilterPrice",
|
|
35
|
+
"Brand",
|
|
36
|
+
"Variant",
|
|
35
37
|
"Price",
|
|
36
38
|
"AvailabilityStatus",
|
|
37
39
|
# Exceptions
|
channel3_sdk/client.py
CHANGED
|
@@ -6,7 +6,7 @@ import aiohttp
|
|
|
6
6
|
import asyncio
|
|
7
7
|
from pydantic import ValidationError
|
|
8
8
|
|
|
9
|
-
from .models import Product, ProductDetail, SearchFilters, SearchRequest
|
|
9
|
+
from .models import Product, ProductDetail, SearchFilters, SearchRequest, Brand
|
|
10
10
|
from .exceptions import (
|
|
11
11
|
Channel3Error,
|
|
12
12
|
Channel3AuthenticationError,
|
|
@@ -127,7 +127,7 @@ class Channel3Client(BaseChannel3Client):
|
|
|
127
127
|
query=query,
|
|
128
128
|
image_url=image_url,
|
|
129
129
|
base64_image=base64_image,
|
|
130
|
-
filters=filters,
|
|
130
|
+
filters=filters or SearchFilters(),
|
|
131
131
|
limit=limit,
|
|
132
132
|
)
|
|
133
133
|
|
|
@@ -210,6 +210,119 @@ class Channel3Client(BaseChannel3Client):
|
|
|
210
210
|
except ValidationError as e:
|
|
211
211
|
raise Channel3Error(f"Invalid response format: {str(e)}")
|
|
212
212
|
|
|
213
|
+
def get_brands(
|
|
214
|
+
self,
|
|
215
|
+
query: Optional[str] = None,
|
|
216
|
+
page: int = 1,
|
|
217
|
+
size: int = 100,
|
|
218
|
+
) -> List[Brand]:
|
|
219
|
+
"""
|
|
220
|
+
Get all brands that the vendor currently sells.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
query: Optional text query to filter brands
|
|
224
|
+
page: Page number for pagination (default: 1)
|
|
225
|
+
size: Number of brands per page (default: 100)
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
List of Brand objects
|
|
229
|
+
|
|
230
|
+
Raises:
|
|
231
|
+
Channel3AuthenticationError: If API key is invalid
|
|
232
|
+
Channel3ValidationError: If request parameters are invalid
|
|
233
|
+
Channel3ServerError: If server encounters an error
|
|
234
|
+
Channel3ConnectionError: If there are connection issues
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
```python
|
|
238
|
+
brands = client.get_brands()
|
|
239
|
+
for brand in brands:
|
|
240
|
+
print(f"Brand: {brand.name}")
|
|
241
|
+
```
|
|
242
|
+
"""
|
|
243
|
+
url = f"{self.base_url}/brands"
|
|
244
|
+
params = {}
|
|
245
|
+
|
|
246
|
+
if query is not None:
|
|
247
|
+
params["query"] = query
|
|
248
|
+
if page != 1:
|
|
249
|
+
params["page"] = page
|
|
250
|
+
if size != 100:
|
|
251
|
+
params["size"] = size
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
response = requests.get(
|
|
255
|
+
url, headers=self.headers, params=params, timeout=30
|
|
256
|
+
)
|
|
257
|
+
response_data = response.json()
|
|
258
|
+
|
|
259
|
+
if response.status_code != 200:
|
|
260
|
+
self._handle_error_response(response.status_code, response_data, url)
|
|
261
|
+
|
|
262
|
+
# Parse and validate response
|
|
263
|
+
return [Brand(**item) for item in response_data]
|
|
264
|
+
|
|
265
|
+
except requests.exceptions.ConnectionError as e:
|
|
266
|
+
raise Channel3ConnectionError(
|
|
267
|
+
f"Failed to connect to Channel3 API: {str(e)}"
|
|
268
|
+
)
|
|
269
|
+
except requests.exceptions.Timeout as e:
|
|
270
|
+
raise Channel3ConnectionError(f"Request timed out: {str(e)}")
|
|
271
|
+
except requests.exceptions.RequestException as e:
|
|
272
|
+
raise Channel3Error(f"Request failed: {str(e)}")
|
|
273
|
+
except ValidationError as e:
|
|
274
|
+
raise Channel3Error(f"Invalid response format: {str(e)}")
|
|
275
|
+
|
|
276
|
+
def get_brand(self, brand_id: str) -> Brand:
|
|
277
|
+
"""
|
|
278
|
+
Get detailed information for a specific brand by its ID.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
brand_id: The unique identifier of the brand
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Brand object with detailed brand information
|
|
285
|
+
|
|
286
|
+
Raises:
|
|
287
|
+
Channel3AuthenticationError: If API key is invalid
|
|
288
|
+
Channel3NotFoundError: If brand is not found
|
|
289
|
+
Channel3ValidationError: If brand_id is invalid
|
|
290
|
+
Channel3ServerError: If server encounters an error
|
|
291
|
+
Channel3ConnectionError: If there are connection issues
|
|
292
|
+
|
|
293
|
+
Example:
|
|
294
|
+
```python
|
|
295
|
+
brand = client.get_brand("brand_123456")
|
|
296
|
+
print(f"Brand: {brand.name}")
|
|
297
|
+
print(f"Description: {brand.description}")
|
|
298
|
+
```
|
|
299
|
+
"""
|
|
300
|
+
if not brand_id or not brand_id.strip():
|
|
301
|
+
raise ValueError("brand_id cannot be empty")
|
|
302
|
+
|
|
303
|
+
url = f"{self.base_url}/brands/{brand_id}"
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
response = requests.get(url, headers=self.headers, timeout=30)
|
|
307
|
+
response_data = response.json()
|
|
308
|
+
|
|
309
|
+
if response.status_code != 200:
|
|
310
|
+
self._handle_error_response(response.status_code, response_data, url)
|
|
311
|
+
|
|
312
|
+
# Parse and validate response
|
|
313
|
+
return Brand(**response_data)
|
|
314
|
+
|
|
315
|
+
except requests.exceptions.ConnectionError as e:
|
|
316
|
+
raise Channel3ConnectionError(
|
|
317
|
+
f"Failed to connect to Channel3 API: {str(e)}"
|
|
318
|
+
)
|
|
319
|
+
except requests.exceptions.Timeout as e:
|
|
320
|
+
raise Channel3ConnectionError(f"Request timed out: {str(e)}")
|
|
321
|
+
except requests.exceptions.RequestException as e:
|
|
322
|
+
raise Channel3Error(f"Request failed: {str(e)}")
|
|
323
|
+
except ValidationError as e:
|
|
324
|
+
raise Channel3Error(f"Invalid response format: {str(e)}")
|
|
325
|
+
|
|
213
326
|
|
|
214
327
|
class AsyncChannel3Client(BaseChannel3Client):
|
|
215
328
|
"""Asynchronous Channel3 API client."""
|
|
@@ -255,7 +368,7 @@ class AsyncChannel3Client(BaseChannel3Client):
|
|
|
255
368
|
query=query,
|
|
256
369
|
image_url=image_url,
|
|
257
370
|
base64_image=base64_image,
|
|
258
|
-
filters=filters,
|
|
371
|
+
filters=filters or SearchFilters(),
|
|
259
372
|
limit=limit,
|
|
260
373
|
)
|
|
261
374
|
|
|
@@ -339,3 +452,123 @@ class AsyncChannel3Client(BaseChannel3Client):
|
|
|
339
452
|
raise Channel3Error(f"Request failed: {str(e)}")
|
|
340
453
|
except ValidationError as e:
|
|
341
454
|
raise Channel3Error(f"Invalid response format: {str(e)}")
|
|
455
|
+
|
|
456
|
+
async def get_brands(
|
|
457
|
+
self,
|
|
458
|
+
query: Optional[str] = None,
|
|
459
|
+
page: int = 1,
|
|
460
|
+
size: int = 100,
|
|
461
|
+
) -> List[Brand]:
|
|
462
|
+
"""
|
|
463
|
+
Get all brands that the vendor currently sells.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
query: Optional text query to filter brands
|
|
467
|
+
page: Page number for pagination (default: 1)
|
|
468
|
+
size: Number of brands per page (default: 100)
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
List of Brand objects
|
|
472
|
+
|
|
473
|
+
Raises:
|
|
474
|
+
Channel3AuthenticationError: If API key is invalid
|
|
475
|
+
Channel3ValidationError: If request parameters are invalid
|
|
476
|
+
Channel3ServerError: If server encounters an error
|
|
477
|
+
Channel3ConnectionError: If there are connection issues
|
|
478
|
+
|
|
479
|
+
Example:
|
|
480
|
+
```python
|
|
481
|
+
brands = await async_client.get_brands()
|
|
482
|
+
for brand in brands:
|
|
483
|
+
print(f"Brand: {brand.name}")
|
|
484
|
+
```
|
|
485
|
+
"""
|
|
486
|
+
url = f"{self.base_url}/brands"
|
|
487
|
+
params = {}
|
|
488
|
+
|
|
489
|
+
if query is not None:
|
|
490
|
+
params["query"] = query
|
|
491
|
+
if page != 1:
|
|
492
|
+
params["page"] = page
|
|
493
|
+
if size != 100:
|
|
494
|
+
params["size"] = size
|
|
495
|
+
|
|
496
|
+
try:
|
|
497
|
+
async with aiohttp.ClientSession() as session:
|
|
498
|
+
async with session.get(
|
|
499
|
+
url,
|
|
500
|
+
headers=self.headers,
|
|
501
|
+
params=params,
|
|
502
|
+
timeout=aiohttp.ClientTimeout(total=30),
|
|
503
|
+
) as response:
|
|
504
|
+
response_data = await response.json()
|
|
505
|
+
|
|
506
|
+
if response.status != 200:
|
|
507
|
+
self._handle_error_response(response.status, response_data, url)
|
|
508
|
+
|
|
509
|
+
# Parse and validate response
|
|
510
|
+
return [Brand(**item) for item in response_data]
|
|
511
|
+
|
|
512
|
+
except aiohttp.ClientConnectionError as e:
|
|
513
|
+
raise Channel3ConnectionError(
|
|
514
|
+
f"Failed to connect to Channel3 API: {str(e)}"
|
|
515
|
+
)
|
|
516
|
+
except asyncio.TimeoutError as e:
|
|
517
|
+
raise Channel3ConnectionError(f"Request timed out: {str(e)}")
|
|
518
|
+
except aiohttp.ClientError as e:
|
|
519
|
+
raise Channel3Error(f"Request failed: {str(e)}")
|
|
520
|
+
except ValidationError as e:
|
|
521
|
+
raise Channel3Error(f"Invalid response format: {str(e)}")
|
|
522
|
+
|
|
523
|
+
async def get_brand(self, brand_id: str) -> Brand:
|
|
524
|
+
"""
|
|
525
|
+
Get detailed information for a specific brand by its ID.
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
brand_id: The unique identifier of the brand
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
Brand object with detailed brand information
|
|
532
|
+
|
|
533
|
+
Raises:
|
|
534
|
+
Channel3AuthenticationError: If API key is invalid
|
|
535
|
+
Channel3NotFoundError: If brand is not found
|
|
536
|
+
Channel3ValidationError: If brand_id is invalid
|
|
537
|
+
Channel3ServerError: If server encounters an error
|
|
538
|
+
Channel3ConnectionError: If there are connection issues
|
|
539
|
+
|
|
540
|
+
Example:
|
|
541
|
+
```python
|
|
542
|
+
brand = await async_client.get_brand("brand_123456")
|
|
543
|
+
print(f"Brand: {brand.name}")
|
|
544
|
+
print(f"Description: {brand.description}")
|
|
545
|
+
```
|
|
546
|
+
"""
|
|
547
|
+
if not brand_id or not brand_id.strip():
|
|
548
|
+
raise ValueError("brand_id cannot be empty")
|
|
549
|
+
|
|
550
|
+
url = f"{self.base_url}/brands/{brand_id}"
|
|
551
|
+
|
|
552
|
+
try:
|
|
553
|
+
async with aiohttp.ClientSession() as session:
|
|
554
|
+
async with session.get(
|
|
555
|
+
url, headers=self.headers, timeout=aiohttp.ClientTimeout(total=30)
|
|
556
|
+
) as response:
|
|
557
|
+
response_data = await response.json()
|
|
558
|
+
|
|
559
|
+
if response.status != 200:
|
|
560
|
+
self._handle_error_response(response.status, response_data, url)
|
|
561
|
+
|
|
562
|
+
# Parse and validate response
|
|
563
|
+
return Brand(**response_data)
|
|
564
|
+
|
|
565
|
+
except aiohttp.ClientConnectionError as e:
|
|
566
|
+
raise Channel3ConnectionError(
|
|
567
|
+
f"Failed to connect to Channel3 API: {str(e)}"
|
|
568
|
+
)
|
|
569
|
+
except asyncio.TimeoutError as e:
|
|
570
|
+
raise Channel3ConnectionError(f"Request timed out: {str(e)}")
|
|
571
|
+
except aiohttp.ClientError as e:
|
|
572
|
+
raise Channel3Error(f"Request failed: {str(e)}")
|
|
573
|
+
except ValidationError as e:
|
|
574
|
+
raise Channel3Error(f"Invalid response format: {str(e)}")
|
channel3_sdk/models.py
CHANGED
|
@@ -30,25 +30,21 @@ class Price(BaseModel):
|
|
|
30
30
|
currency: str = Field(..., description="The currency code of the product.")
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
class
|
|
34
|
-
"""A
|
|
33
|
+
class Brand(BaseModel):
|
|
34
|
+
"""A brand."""
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
price: Price = Field(..., description="Price information")
|
|
41
|
-
availability: AvailabilityStatus = Field(
|
|
42
|
-
..., description="Product availability status"
|
|
43
|
-
)
|
|
36
|
+
id: str = Field(..., description="Unique identifier for the brand")
|
|
37
|
+
name: str = Field(..., description="Name of the brand")
|
|
38
|
+
logo_url: Optional[str] = Field(None, description="Logo URL for the brand")
|
|
39
|
+
description: Optional[str] = Field(None, description="Description of the brand")
|
|
44
40
|
|
|
45
41
|
|
|
46
|
-
class
|
|
47
|
-
"""A
|
|
42
|
+
class Variant(BaseModel):
|
|
43
|
+
"""A product variant."""
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
title: str = Field(..., description="Title of the
|
|
51
|
-
image_url: str = Field(..., description="Image URL for the
|
|
45
|
+
product_id: str = Field(..., description="Unique identifier for the product")
|
|
46
|
+
title: str = Field(..., description="Title of the variant")
|
|
47
|
+
image_url: str = Field(..., description="Image URL for the variant")
|
|
52
48
|
|
|
53
49
|
|
|
54
50
|
class Product(BaseModel):
|
|
@@ -56,50 +52,63 @@ class Product(BaseModel):
|
|
|
56
52
|
|
|
57
53
|
id: str = Field(..., description="Unique identifier for the product")
|
|
58
54
|
score: float = Field(..., description="Relevance score for the search query")
|
|
59
|
-
brand_name: str = Field(..., description="Brand name of the product")
|
|
60
55
|
title: str = Field(..., description="Product title")
|
|
61
|
-
description: str = Field(
|
|
56
|
+
description: Optional[str] = Field(None, description="Product description")
|
|
57
|
+
brand_name: str = Field(..., description="Brand name of the product")
|
|
62
58
|
image_url: str = Field(..., description="Main product image URL")
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
price: Price = Field(..., description="Price information")
|
|
60
|
+
availability: AvailabilityStatus = Field(
|
|
61
|
+
..., description="Product availability status"
|
|
65
62
|
)
|
|
66
|
-
|
|
67
|
-
default_factory=list, description="
|
|
63
|
+
variants: List[Variant] = Field(
|
|
64
|
+
default_factory=list, description="Product variants"
|
|
68
65
|
)
|
|
69
66
|
|
|
70
67
|
|
|
71
68
|
class ProductDetail(BaseModel):
|
|
72
69
|
"""Detailed information about a product."""
|
|
73
70
|
|
|
74
|
-
brand_id: str = Field(..., description="Unique identifier for the brand")
|
|
75
|
-
brand_name: str = Field(..., description="Brand name of the product")
|
|
76
71
|
title: str = Field(..., description="Product title")
|
|
77
|
-
description: str = Field(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
description: Optional[str] = Field(None, description="Product description")
|
|
73
|
+
brand_id: Optional[str] = Field(None, description="Unique identifier for the brand")
|
|
74
|
+
brand_name: Optional[str] = Field(None, description="Brand name of the product")
|
|
75
|
+
image_urls: Optional[List[str]] = Field(
|
|
76
|
+
None, description="List of product image URLs"
|
|
81
77
|
)
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
price: Price = Field(..., description="Price information")
|
|
79
|
+
availability: AvailabilityStatus = Field(
|
|
80
|
+
..., description="Product availability status"
|
|
84
81
|
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
default_factory=list, description="List of key product features"
|
|
82
|
+
key_features: Optional[List[str]] = Field(
|
|
83
|
+
None, description="List of key product features"
|
|
88
84
|
)
|
|
89
|
-
|
|
90
|
-
default_factory=list, description="
|
|
85
|
+
variants: List[Variant] = Field(
|
|
86
|
+
default_factory=list, description="Product variants"
|
|
91
87
|
)
|
|
92
88
|
|
|
93
89
|
|
|
90
|
+
class SearchFilterPrice(BaseModel):
|
|
91
|
+
"""Price filter for product search."""
|
|
92
|
+
|
|
93
|
+
min_price: Optional[float] = Field(None, description="Minimum price filter")
|
|
94
|
+
max_price: Optional[float] = Field(None, description="Maximum price filter")
|
|
95
|
+
|
|
96
|
+
|
|
94
97
|
class SearchFilters(BaseModel):
|
|
95
98
|
"""Search filters for product search."""
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
brand_ids: Optional[List[str]] = Field(
|
|
101
|
+
None, description="List of brand IDs to filter by"
|
|
102
|
+
)
|
|
103
|
+
gender: Optional[Literal["male", "female", "unisex"]] = Field(
|
|
104
|
+
None, description="Gender to filter by"
|
|
105
|
+
)
|
|
106
|
+
price: Optional[SearchFilterPrice] = Field(
|
|
107
|
+
None, description="Price range to filter by"
|
|
108
|
+
)
|
|
109
|
+
availability: Optional[List[AvailabilityStatus]] = Field(
|
|
110
|
+
None, description="Availability statuses to filter by"
|
|
100
111
|
)
|
|
101
|
-
min_price: Optional[float] = Field(None, description="Minimum price filter")
|
|
102
|
-
max_price: Optional[float] = Field(None, description="Maximum price filter")
|
|
103
112
|
|
|
104
113
|
|
|
105
114
|
class SearchRequest(BaseModel):
|
|
@@ -110,7 +119,7 @@ class SearchRequest(BaseModel):
|
|
|
110
119
|
base64_image: Optional[str] = Field(
|
|
111
120
|
None, description="Base64-encoded image for visual search"
|
|
112
121
|
)
|
|
113
|
-
filters: Optional[SearchFilters] = Field(None, description="Search filters")
|
|
114
122
|
limit: Optional[int] = Field(
|
|
115
123
|
default=20, description="Maximum number of results to return"
|
|
116
124
|
)
|
|
125
|
+
filters: Optional[SearchFilters] = Field(default=None, description="Search filters")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: channel3-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: The official Python SDK for Channel3 AI Shopping API
|
|
5
5
|
Home-page: https://github.com/channel3/sdk-python
|
|
6
6
|
License: MIT
|
|
@@ -50,14 +50,27 @@ products = client.search(query="blue denim jacket")
|
|
|
50
50
|
for product in products:
|
|
51
51
|
print(f"Product: {product.title}")
|
|
52
52
|
print(f"Brand: {product.brand_name}")
|
|
53
|
-
print(f"Price:
|
|
53
|
+
print(f"Price: {product.price.currency} {product.price.price}")
|
|
54
|
+
print(f"Availability: {product.availability}")
|
|
54
55
|
print("---")
|
|
55
56
|
|
|
56
57
|
# Get detailed product information
|
|
57
58
|
product_detail = client.get_product("prod_123456")
|
|
58
59
|
print(f"Detailed info for: {product_detail.title}")
|
|
59
|
-
print(f"
|
|
60
|
-
|
|
60
|
+
print(f"Brand: {product_detail.brand_name}")
|
|
61
|
+
if product_detail.key_features:
|
|
62
|
+
print(f"Key features: {product_detail.key_features}")
|
|
63
|
+
|
|
64
|
+
# Get all brands
|
|
65
|
+
brands = client.get_brands()
|
|
66
|
+
for brand in brands:
|
|
67
|
+
print(f"Brand: {brand.name}")
|
|
68
|
+
if brand.description:
|
|
69
|
+
print(f"Description: {brand.description}")
|
|
70
|
+
|
|
71
|
+
# Get specific brand details
|
|
72
|
+
brand = client.get_brand("brand_123")
|
|
73
|
+
print(f"Brand: {brand.name}")
|
|
61
74
|
```
|
|
62
75
|
|
|
63
76
|
### Asynchronous Client
|
|
@@ -76,11 +89,16 @@ async def main():
|
|
|
76
89
|
for product in products:
|
|
77
90
|
print(f"Product: {product.title}")
|
|
78
91
|
print(f"Score: {product.score}")
|
|
92
|
+
print(f"Price: {product.price.currency} {product.price.price}")
|
|
79
93
|
|
|
80
94
|
# Get detailed product information
|
|
81
95
|
if products:
|
|
82
96
|
product_detail = await client.get_product(products[0].id)
|
|
83
|
-
print(f"
|
|
97
|
+
print(f"Availability: {product_detail.availability}")
|
|
98
|
+
|
|
99
|
+
# Get brands
|
|
100
|
+
brands = await client.get_brands()
|
|
101
|
+
print(f"Found {len(brands)} brands")
|
|
84
102
|
|
|
85
103
|
# Run the async function
|
|
86
104
|
asyncio.run(main())
|
|
@@ -114,14 +132,14 @@ products = client.search(
|
|
|
114
132
|
### Search with Filters
|
|
115
133
|
|
|
116
134
|
```python
|
|
117
|
-
from channel3_sdk import SearchFilters
|
|
135
|
+
from channel3_sdk import SearchFilters, AvailabilityStatus
|
|
118
136
|
|
|
119
137
|
# Create search filters
|
|
120
138
|
filters = SearchFilters(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
max_price
|
|
139
|
+
brand_ids=["brand_123", "brand_456"],
|
|
140
|
+
gender="male",
|
|
141
|
+
availability=[AvailabilityStatus.IN_STOCK],
|
|
142
|
+
price={"min_price": 50.0, "max_price": 200.0}
|
|
125
143
|
)
|
|
126
144
|
|
|
127
145
|
# Search with filters
|
|
@@ -132,6 +150,21 @@ products = client.search(
|
|
|
132
150
|
)
|
|
133
151
|
```
|
|
134
152
|
|
|
153
|
+
### Brand Management
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# Get all brands with pagination
|
|
157
|
+
brands = client.get_brands(page=1, size=50)
|
|
158
|
+
|
|
159
|
+
# Search for specific brands
|
|
160
|
+
nike_brands = client.get_brands(query="nike")
|
|
161
|
+
|
|
162
|
+
# Get detailed brand information
|
|
163
|
+
brand_detail = client.get_brand("brand_123")
|
|
164
|
+
print(f"Brand: {brand_detail.name}")
|
|
165
|
+
print(f"Logo: {brand_detail.logo_url}")
|
|
166
|
+
```
|
|
167
|
+
|
|
135
168
|
## API Reference
|
|
136
169
|
|
|
137
170
|
### Client Classes
|
|
@@ -142,6 +175,8 @@ Synchronous client for the Channel3 API.
|
|
|
142
175
|
**Methods:**
|
|
143
176
|
- `search(query=None, image_url=None, base64_image=None, filters=None, limit=20)` → `List[Product]`
|
|
144
177
|
- `get_product(product_id)` → `ProductDetail`
|
|
178
|
+
- `get_brands(query=None, page=1, size=100)` → `List[Brand]`
|
|
179
|
+
- `get_brand(brand_id)` → `Brand`
|
|
145
180
|
|
|
146
181
|
#### `AsyncChannel3Client`
|
|
147
182
|
Asynchronous client for the Channel3 API.
|
|
@@ -149,48 +184,62 @@ Asynchronous client for the Channel3 API.
|
|
|
149
184
|
**Methods:**
|
|
150
185
|
- `async search(query=None, image_url=None, base64_image=None, filters=None, limit=20)` → `List[Product]`
|
|
151
186
|
- `async get_product(product_id)` → `ProductDetail`
|
|
187
|
+
- `async get_brands(query=None, page=1, size=100)` → `List[Brand]`
|
|
188
|
+
- `async get_brand(brand_id)` → `Brand`
|
|
152
189
|
|
|
153
190
|
### Models
|
|
154
191
|
|
|
155
192
|
#### `Product`
|
|
156
193
|
- `id: str` - Unique product identifier
|
|
157
194
|
- `score: float` - Search relevance score
|
|
158
|
-
- `brand_name: str` - Brand name
|
|
159
195
|
- `title: str` - Product title
|
|
160
|
-
- `description: str` - Product description
|
|
196
|
+
- `description: Optional[str]` - Product description
|
|
197
|
+
- `brand_name: str` - Brand name
|
|
161
198
|
- `image_url: str` - Main product image URL
|
|
162
|
-
- `
|
|
163
|
-
- `
|
|
199
|
+
- `price: Price` - Price information
|
|
200
|
+
- `availability: AvailabilityStatus` - Availability status
|
|
201
|
+
- `variants: List[Variant]` - Product variants
|
|
164
202
|
|
|
165
203
|
#### `ProductDetail`
|
|
166
|
-
- `brand_id: str` - Brand identifier
|
|
167
|
-
- `brand_name: str` - Brand name
|
|
168
204
|
- `title: str` - Product title
|
|
169
|
-
- `description: str` - Product description
|
|
170
|
-
- `
|
|
171
|
-
- `
|
|
172
|
-
- `
|
|
173
|
-
- `
|
|
174
|
-
- `
|
|
175
|
-
- `
|
|
205
|
+
- `description: Optional[str]` - Product description
|
|
206
|
+
- `brand_id: Optional[str]` - Brand identifier
|
|
207
|
+
- `brand_name: Optional[str]` - Brand name
|
|
208
|
+
- `image_urls: Optional[List[str]]` - Product image URLs
|
|
209
|
+
- `price: Price` - Price information
|
|
210
|
+
- `availability: AvailabilityStatus` - Availability status
|
|
211
|
+
- `key_features: Optional[List[str]]` - Key product features
|
|
212
|
+
- `variants: List[Variant]` - Product variants
|
|
213
|
+
|
|
214
|
+
#### `Brand`
|
|
215
|
+
- `id: str` - Unique brand identifier
|
|
216
|
+
- `name: str` - Brand name
|
|
217
|
+
- `logo_url: Optional[str]` - Brand logo URL
|
|
218
|
+
- `description: Optional[str]` - Brand description
|
|
219
|
+
|
|
220
|
+
#### `Variant`
|
|
221
|
+
- `product_id: str` - Associated product identifier
|
|
222
|
+
- `title: str` - Variant title
|
|
223
|
+
- `image_url: str` - Variant image URL
|
|
176
224
|
|
|
177
225
|
#### `SearchFilters`
|
|
178
|
-
- `
|
|
179
|
-
- `
|
|
226
|
+
- `brand_ids: Optional[List[str]]` - Brand ID filters
|
|
227
|
+
- `gender: Optional[Literal["male", "female", "unisex"]]` - Gender filter
|
|
228
|
+
- `price: Optional[SearchFilterPrice]` - Price range filter
|
|
229
|
+
- `availability: Optional[List[AvailabilityStatus]]` - Availability filters
|
|
230
|
+
|
|
231
|
+
#### `SearchFilterPrice`
|
|
180
232
|
- `min_price: Optional[float]` - Minimum price
|
|
181
233
|
- `max_price: Optional[float]` - Maximum price
|
|
182
234
|
|
|
183
|
-
#### `MerchantOffering`
|
|
184
|
-
- `url: str` - Purchase URL
|
|
185
|
-
- `merchant_name: str` - Merchant name
|
|
186
|
-
- `price: Price` - Price information
|
|
187
|
-
- `availability: AvailabilityStatus` - Availability status
|
|
188
|
-
|
|
189
235
|
#### `Price`
|
|
190
236
|
- `price: float` - Current price
|
|
191
237
|
- `compare_at_price: Optional[float]` - Original price (if discounted)
|
|
192
238
|
- `currency: str` - Currency code
|
|
193
239
|
|
|
240
|
+
#### `AvailabilityStatus`
|
|
241
|
+
Enum with values: `IN_STOCK`, `OUT_OF_STOCK`, `PRE_ORDER`, `LIMITED_AVAILABILITY`, `BACK_ORDER`, `DISCONTINUED`, `SOLD_OUT`, `UNKNOWN`
|
|
242
|
+
|
|
194
243
|
## Error Handling
|
|
195
244
|
|
|
196
245
|
The SDK provides specific exception types for different error conditions:
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
channel3_sdk/__init__.py,sha256=vMrsZwY59OoYmoPwEnZ0kuAuWFkOIERUaoy0lWpGIlc,976
|
|
2
|
+
channel3_sdk/client.py,sha256=8klt__VDUerEO6qq8-RA3w8CSImE6tfPf1v8m25_aS4,20843
|
|
3
|
+
channel3_sdk/exceptions.py,sha256=5HJgrhir4-_b4XKyWAB9qp-CjVZeigXSkyM9fbcB1IA,1044
|
|
4
|
+
channel3_sdk/models.py,sha256=PdFD-sESRhlZSsvVIHHlhwn-Zi5QbGcUiHo0NnP_Gqs,4520
|
|
5
|
+
channel3_sdk-0.1.12.dist-info/METADATA,sha256=fflKnYaOYNhHohAMm7_MvdDgxBxOHZfXtJCYsbwj4FA,7800
|
|
6
|
+
channel3_sdk-0.1.12.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
7
|
+
channel3_sdk-0.1.12.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
channel3_sdk/__init__.py,sha256=aoe0MhblA5pEOd7iAggJ94u1Os8-xCBs6k0UWhfOiiE,960
|
|
2
|
-
channel3_sdk/client.py,sha256=JuJFtXdWsezwOQk3nVUnxhdtQp300UEORsQZdCZ77V8,12525
|
|
3
|
-
channel3_sdk/exceptions.py,sha256=5HJgrhir4-_b4XKyWAB9qp-CjVZeigXSkyM9fbcB1IA,1044
|
|
4
|
-
channel3_sdk/models.py,sha256=AMgcsCK-jnaKA9iiL0CZvWJMprA1AxspIu4IM6vq-WE,4326
|
|
5
|
-
channel3_sdk-0.1.0.dist-info/METADATA,sha256=xGgwGQ8iQTs1u7zeYK22RSF-EW02fYvPlqeC-4xerqs,6137
|
|
6
|
-
channel3_sdk-0.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
7
|
-
channel3_sdk-0.1.0.dist-info/RECORD,,
|
|
File without changes
|