From c04e18937990fc34d9a5aceeeedba829c309a031 Mon Sep 17 00:00:00 2001 From: Jay Sobel Date: Fri, 29 May 2026 15:37:14 -0700 Subject: [PATCH] Add COLA package filters to Python SDK --- README.md | 10 ++++- src/colacloud/async_client.py | 78 +++++++++++++++++++++++++++++++++-- src/colacloud/client.py | 78 +++++++++++++++++++++++++++++++++-- tests/test_client.py | 21 +++++++++- 4 files changed, 177 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 95772bb..38c1c08 100644 --- a/README.md +++ b/README.md @@ -104,14 +104,22 @@ asyncio.run(main()) ```python response = client.colas.list( - q="search query", # Full-text search + q="search query", # Brand, product, permit, applicant/company, etc. product_type="wine", # malt beverage, wine, distilled spirits + category="Wine", # Beer, Wine, Liquor + derived_subcategory="Wine > Red Wine", origin="France", # Country or state brand_name="Chateau", # Partial match + permit_number="CA-I-12345", + barcode_value="012345678905", approval_date_from="2024-01-01", approval_date_to="2024-12-31", abv_min=10.0, abv_max=20.0, + volume_unit="milliliters", # Required with volume_min/volume_max + volume_min=375, + volume_max=750, + container_type="bottle,can", page=1, per_page=20 # Max 100 ) diff --git a/src/colacloud/async_client.py b/src/colacloud/async_client.py index 9a9fe72..ad821bf 100644 --- a/src/colacloud/async_client.py +++ b/src/colacloud/async_client.py @@ -49,26 +49,46 @@ async def list( *, q: str | None = None, product_type: str | None = None, + category: str | None = None, + derived_subcategory: str | None = None, origin: str | None = None, + domestic_or_imported: str | None = None, + status: str | None = None, brand_name: str | None = None, + permit_number: str | None = None, + barcode_value: str | None = None, approval_date_from: str | None = None, approval_date_to: str | None = None, abv_min: float | None = None, abv_max: float | None = None, + volume_unit: str | None = None, + volume_min: float | None = None, + volume_max: float | None = None, + container_type: str | None = None, page: int = 1, per_page: int = 20, ) -> ColaListResponse: """List and search COLAs with pagination. Args: - q: Full-text search query. - product_type: Filter by product type. + q: Text search query, including applicant/company names. + product_type: Filter by one or more TTB product types. + category: Filter by derived top-level category. + derived_subcategory: Filter by derived category path prefix. origin: Filter by country/state of origin. + domestic_or_imported: Filter by domestic/imported origin. + status: Filter by application status. brand_name: Filter by brand name (partial match). + permit_number: Filter by exact permit number. + barcode_value: Filter by exact main barcode value. approval_date_from: Filter by minimum approval date (YYYY-MM-DD). approval_date_to: Filter by maximum approval date (YYYY-MM-DD). abv_min: Filter by minimum ABV percentage. abv_max: Filter by maximum ABV percentage. + volume_unit: Filter by volume unit. Required with volume_min/volume_max. + volume_min: Filter by minimum package volume. + volume_max: Filter by maximum package volume. + container_type: Filter by one or more derived container types. page: Page number (default: 1). per_page: Results per page (default: 20, max: 100). @@ -87,10 +107,22 @@ async def list( params["q"] = q if product_type: params["product_type"] = product_type + if category: + params["category"] = category + if derived_subcategory: + params["derived_subcategory"] = derived_subcategory if origin: params["origin"] = origin + if domestic_or_imported: + params["domestic_or_imported"] = domestic_or_imported + if status: + params["status"] = status if brand_name: params["brand_name"] = brand_name + if permit_number: + params["permit_number"] = permit_number + if barcode_value: + params["barcode_value"] = barcode_value if approval_date_from: params["approval_date_from"] = approval_date_from if approval_date_to: @@ -99,6 +131,14 @@ async def list( params["abv_min"] = abv_min if abv_max is not None: params["abv_max"] = abv_max + if volume_unit: + params["volume_unit"] = volume_unit + if volume_min is not None: + params["volume_min"] = volume_min + if volume_max is not None: + params["volume_max"] = volume_max + if container_type: + params["container_type"] = container_type data = await self._client._request("GET", "/colas", params=params) return ColaListResponse.model_validate(data) @@ -127,12 +167,22 @@ def iterate( *, q: str | None = None, product_type: str | None = None, + category: str | None = None, + derived_subcategory: str | None = None, origin: str | None = None, + domestic_or_imported: str | None = None, + status: str | None = None, brand_name: str | None = None, + permit_number: str | None = None, + barcode_value: str | None = None, approval_date_from: str | None = None, approval_date_to: str | None = None, abv_min: float | None = None, abv_max: float | None = None, + volume_unit: str | None = None, + volume_min: float | None = None, + volume_max: float | None = None, + container_type: str | None = None, per_page: int = 100, ) -> AsyncPaginatedIterator[ColaSummary]: """Iterate through all matching COLAs with automatic pagination. @@ -141,14 +191,24 @@ def iterate( pages as needed. Args: - q: Full-text search query. - product_type: Filter by product type. + q: Text search query, including applicant/company names. + product_type: Filter by one or more TTB product types. + category: Filter by derived top-level category. + derived_subcategory: Filter by derived category path prefix. origin: Filter by country/state of origin. + domestic_or_imported: Filter by domestic/imported origin. + status: Filter by application status. brand_name: Filter by brand name (partial match). + permit_number: Filter by exact permit number. + barcode_value: Filter by exact main barcode value. approval_date_from: Filter by minimum approval date (YYYY-MM-DD). approval_date_to: Filter by maximum approval date (YYYY-MM-DD). abv_min: Filter by minimum ABV percentage. abv_max: Filter by maximum ABV percentage. + volume_unit: Filter by volume unit. Required with volume_min/volume_max. + volume_min: Filter by minimum package volume. + volume_max: Filter by maximum package volume. + container_type: Filter by one or more derived container types. per_page: Results per page (default: 100, max: 100). Yields: @@ -165,12 +225,22 @@ async def fetch_page(page: int) -> tuple[list[ColaSummary], Pagination]: response = await self.list( q=q, product_type=product_type, + category=category, + derived_subcategory=derived_subcategory, origin=origin, + domestic_or_imported=domestic_or_imported, + status=status, brand_name=brand_name, + permit_number=permit_number, + barcode_value=barcode_value, approval_date_from=approval_date_from, approval_date_to=approval_date_to, abv_min=abv_min, abv_max=abv_max, + volume_unit=volume_unit, + volume_min=volume_min, + volume_max=volume_max, + container_type=container_type, page=page, per_page=per_page, ) diff --git a/src/colacloud/client.py b/src/colacloud/client.py index 62da7c9..33295f2 100644 --- a/src/colacloud/client.py +++ b/src/colacloud/client.py @@ -49,26 +49,46 @@ def list( *, q: str | None = None, product_type: str | None = None, + category: str | None = None, + derived_subcategory: str | None = None, origin: str | None = None, + domestic_or_imported: str | None = None, + status: str | None = None, brand_name: str | None = None, + permit_number: str | None = None, + barcode_value: str | None = None, approval_date_from: str | None = None, approval_date_to: str | None = None, abv_min: float | None = None, abv_max: float | None = None, + volume_unit: str | None = None, + volume_min: float | None = None, + volume_max: float | None = None, + container_type: str | None = None, page: int = 1, per_page: int = 20, ) -> ColaListResponse: """List and search COLAs with pagination. Args: - q: Full-text search query. - product_type: Filter by product type. + q: Text search query, including applicant/company names. + product_type: Filter by one or more TTB product types. + category: Filter by derived top-level category. + derived_subcategory: Filter by derived category path prefix. origin: Filter by country/state of origin. + domestic_or_imported: Filter by domestic/imported origin. + status: Filter by application status. brand_name: Filter by brand name (partial match). + permit_number: Filter by exact permit number. + barcode_value: Filter by exact main barcode value. approval_date_from: Filter by minimum approval date (YYYY-MM-DD). approval_date_to: Filter by maximum approval date (YYYY-MM-DD). abv_min: Filter by minimum ABV percentage. abv_max: Filter by maximum ABV percentage. + volume_unit: Filter by volume unit. Required with volume_min/volume_max. + volume_min: Filter by minimum package volume. + volume_max: Filter by maximum package volume. + container_type: Filter by one or more derived container types. page: Page number (default: 1). per_page: Results per page (default: 20, max: 100). @@ -87,10 +107,22 @@ def list( params["q"] = q if product_type: params["product_type"] = product_type + if category: + params["category"] = category + if derived_subcategory: + params["derived_subcategory"] = derived_subcategory if origin: params["origin"] = origin + if domestic_or_imported: + params["domestic_or_imported"] = domestic_or_imported + if status: + params["status"] = status if brand_name: params["brand_name"] = brand_name + if permit_number: + params["permit_number"] = permit_number + if barcode_value: + params["barcode_value"] = barcode_value if approval_date_from: params["approval_date_from"] = approval_date_from if approval_date_to: @@ -99,6 +131,14 @@ def list( params["abv_min"] = abv_min if abv_max is not None: params["abv_max"] = abv_max + if volume_unit: + params["volume_unit"] = volume_unit + if volume_min is not None: + params["volume_min"] = volume_min + if volume_max is not None: + params["volume_max"] = volume_max + if container_type: + params["container_type"] = container_type data = self._client._request("GET", "/colas", params=params) return ColaListResponse.model_validate(data) @@ -127,12 +167,22 @@ def iterate( *, q: str | None = None, product_type: str | None = None, + category: str | None = None, + derived_subcategory: str | None = None, origin: str | None = None, + domestic_or_imported: str | None = None, + status: str | None = None, brand_name: str | None = None, + permit_number: str | None = None, + barcode_value: str | None = None, approval_date_from: str | None = None, approval_date_to: str | None = None, abv_min: float | None = None, abv_max: float | None = None, + volume_unit: str | None = None, + volume_min: float | None = None, + volume_max: float | None = None, + container_type: str | None = None, per_page: int = 100, ) -> PaginatedIterator[ColaSummary]: """Iterate through all matching COLAs with automatic pagination. @@ -141,14 +191,24 @@ def iterate( pages as needed. Args: - q: Full-text search query. - product_type: Filter by product type. + q: Text search query, including applicant/company names. + product_type: Filter by one or more TTB product types. + category: Filter by derived top-level category. + derived_subcategory: Filter by derived category path prefix. origin: Filter by country/state of origin. + domestic_or_imported: Filter by domestic/imported origin. + status: Filter by application status. brand_name: Filter by brand name (partial match). + permit_number: Filter by exact permit number. + barcode_value: Filter by exact main barcode value. approval_date_from: Filter by minimum approval date (YYYY-MM-DD). approval_date_to: Filter by maximum approval date (YYYY-MM-DD). abv_min: Filter by minimum ABV percentage. abv_max: Filter by maximum ABV percentage. + volume_unit: Filter by volume unit. Required with volume_min/volume_max. + volume_min: Filter by minimum package volume. + volume_max: Filter by maximum package volume. + container_type: Filter by one or more derived container types. per_page: Results per page (default: 100, max: 100). Yields: @@ -165,12 +225,22 @@ def fetch_page(page: int) -> tuple[list[ColaSummary], Pagination]: response = self.list( q=q, product_type=product_type, + category=category, + derived_subcategory=derived_subcategory, origin=origin, + domestic_or_imported=domestic_or_imported, + status=status, brand_name=brand_name, + permit_number=permit_number, + barcode_value=barcode_value, approval_date_from=approval_date_from, approval_date_to=approval_date_to, abv_min=abv_min, abv_max=abv_max, + volume_unit=volume_unit, + volume_min=volume_min, + volume_max=volume_max, + container_type=container_type, page=page, per_page=per_page, ) diff --git a/tests/test_client.py b/tests/test_client.py index b472066..75cf69c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -75,13 +75,23 @@ def test_list_colas_with_filters(self, httpx_mock: HTTPXMock, cola_list_response with ColaCloud(api_key="test-key") as client: client.colas.list( q="whiskey", - product_type="distilled spirits", + product_type="distilled spirits,wine", + category="Liquor,Wine", + derived_subcategory="Liquor > Whiskey", origin="Kentucky", + domestic_or_imported="domestic", + status="approved", brand_name="Test", + permit_number="KY-I-12345", + barcode_value="012345678905", approval_date_from="2024-01-01", approval_date_to="2024-12-31", abv_min=35.0, abv_max=50.0, + volume_unit="milliliters", + volume_min=375, + volume_max=750, + container_type="bottle,can", page=1, per_page=50, ) @@ -90,6 +100,15 @@ def test_list_colas_with_filters(self, httpx_mock: HTTPXMock, cola_list_response assert "q=whiskey" in str(request.url) assert "product_type=distilled" in str(request.url) assert "abv_min=35" in str(request.url) + assert "category=Liquor%2CWine" in str(request.url) + assert "derived_subcategory=Liquor+%3E+Whiskey" in str(request.url) + assert "domestic_or_imported=domestic" in str(request.url) + assert "permit_number=KY-I-12345" in str(request.url) + assert "barcode_value=012345678905" in str(request.url) + assert "volume_unit=milliliters" in str(request.url) + assert "volume_min=375" in str(request.url) + assert "volume_max=750" in str(request.url) + assert "container_type=bottle%2Ccan" in str(request.url) def test_get_cola(self, httpx_mock: HTTPXMock, cola_detail_response): httpx_mock.add_response(json=cola_detail_response)