From 8befcd2896a6c367abefa3a7dfa9578ea3a80756 Mon Sep 17 00:00:00 2001 From: hlad Date: Sun, 31 Jul 2022 01:48:38 +0200 Subject: [PATCH] initial commit --- Pipfile | 7 +++ README.md | 0 pyrohlik/__init__.py | 4 ++ pyrohlik/client.py | 36 +++++++++++++ pyrohlik/endpoints.py | 10 ++++ pyrohlik/models/order.py | 79 ++++++++++++++++++++++++++++ pyrohlik/models/product.py | 84 ++++++++++++++++++++++++++++++ pyrohlik/models/recipe.py | 103 +++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 9 files changed, 324 insertions(+) create mode 100644 Pipfile create mode 100644 README.md create mode 100644 pyrohlik/__init__.py create mode 100644 pyrohlik/client.py create mode 100644 pyrohlik/endpoints.py create mode 100644 pyrohlik/models/order.py create mode 100644 pyrohlik/models/product.py create mode 100644 pyrohlik/models/recipe.py create mode 100644 requirements.txt diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..eaec536 --- /dev/null +++ b/Pipfile @@ -0,0 +1,7 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +api-client-pydantic = "*" diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pyrohlik/__init__.py b/pyrohlik/__init__.py new file mode 100644 index 0000000..0f26bc8 --- /dev/null +++ b/pyrohlik/__init__.py @@ -0,0 +1,4 @@ +from .client import Rohlik + + +__all__ = ('Rohlik', ) diff --git a/pyrohlik/client.py b/pyrohlik/client.py new file mode 100644 index 0000000..ea7faac --- /dev/null +++ b/pyrohlik/client.py @@ -0,0 +1,36 @@ +from apiclient import APIClient, endpoint, JsonResponseHandler, HeaderAuthentication +from apiclient_pydantic import serialize + +from .models.order import Order, Orders +from .models.product import Product +from .models.recipe import Recipe +from .endpoints import Endpoints + + +class Rohlik(APIClient): + def __init__(self, api_key: str): + super().__init__(response_handler=JsonResponseHandler, authentication_method=HeaderAuthentication(token=api_key,parameter="x-api-authorization",scheme="token")) +# self.response_handler=JsonResponseHandler +# self.authentication_method=HeaderAuthentication(token=api_key,parameter="x-api-authorization",scheme="token") + + @serialize() + def get_product(self, product_id: int) -> Product: + product = self.get(Endpoints.product.format(product_id=product_id)) + prices = self.get(Endpoints.prices.format(product_id=product_id)) + stock = self.get(Endpoints.stock.format(product_id=product_id)) + product['prices'] = prices + product['stock'] = stock + return product + + @serialize() + def get_order(self, order_id: int) -> Order: + return self.get(Endpoints.order.format(order_id=order_id)) + + @serialize() + def get_orders(self) -> Orders: + return self.get(Endpoints.orders_delivered) + + @serialize() + def get_recipe(self, recipe_id: int) -> Recipe: + recipe_response = self.get(Endpoints.recipe.format(recipe_id=recipe_id)) + return recipe_response['data'] diff --git a/pyrohlik/endpoints.py b/pyrohlik/endpoints.py new file mode 100644 index 0000000..b5b1376 --- /dev/null +++ b/pyrohlik/endpoints.py @@ -0,0 +1,10 @@ +from apiclient import endpoint + +@endpoint(base_url='https://www.rohlik.cz') +class Endpoints: + product: str = 'api/v1/products/{product_id}' + order: str = 'api/v3/orders/{order_id}' + orders_delivered: str = 'api/v3/orders/delivered' + recipe: str = 'services/frontend-service/recipe/{recipe_id}' + prices: str = 'api/v1/products/{product_id}/prices' + stock: str = 'api/v1/products/{product_id}/stock' diff --git a/pyrohlik/models/order.py b/pyrohlik/models/order.py new file mode 100644 index 0000000..bea4ce5 --- /dev/null +++ b/pyrohlik/models/order.py @@ -0,0 +1,79 @@ +from decimal import Decimal +from datetime import datetime +from typing import List, Optional +from pydantic import BaseModel, Field + + +class Price(BaseModel): + amount: Decimal + currency: str + + +class PriceCompositionOrder(BaseModel): + total: Price + goods: Price + delivery: Price + credits_used: Price = Field(alias='creditsUsed') + courier_tip: Price = Field(alias='courierTip') + fines: Price + reusable_bags_deposit: Price = Field(alias='reusableBagsDeposit') + + +class PriceCompositionOrderProduct(BaseModel): + total: Price + unit: Price + +class PriceCompositionOrderList(BaseModel): + total: Price + + +class OrderProduct(BaseModel): + name: str + unit: str + textual_amount: str = Field(..., alias='textualAmount') + amount: int + images: List[str] + price_composition: PriceCompositionOrderProduct = Field(..., alias='priceComposition') + compensated: bool + id: int + + +class DeliverySlot(BaseModel): + id: int + type: str + since: datetime + till: datetime + + +class OrderList(BaseModel): + id: int + items_count: int = Field(alias='itemsCount') + order_time: datetime = Field(alias='orderTime') + price_composition: PriceCompositionOrderList = Field(..., alias='priceComposition') + delivery_slot: Optional[DeliverySlot] = Field(..., alias='deliverySlot') + + +class Document(BaseModel): + type: str + title: str + link: str + + +class Order(BaseModel): + id: int + items_count: int = Field(..., alias='itemsCount') + price_composition: PriceCompositionOrder = Field(..., alias='priceComposition') + order_time: datetime = Field(..., alias='orderTime') + delivery_type: str = Field(..., alias='deliveryType') + delivery_slot: DeliverySlot = Field(..., alias='deliverySlot') + state: str + payment: int + address: str + delivery_note: str = Field(..., alias='deliveryNote') + documents: List[Document] + available_actions: List[str] = Field(..., alias='availableActions') + items: List[OrderProduct] + + +class Orders(BaseModel): + __root__: List[OrderList] diff --git a/pyrohlik/models/product.py b/pyrohlik/models/product.py new file mode 100644 index 0000000..a9dd13b --- /dev/null +++ b/pyrohlik/models/product.py @@ -0,0 +1,84 @@ +from decimal import Decimal +from datetime import datetime +from typing import List, Optional +from pydantic import BaseModel, Field + + +class Price(BaseModel): + amount: Decimal + currency: str + + +class Badge(BaseModel): + type: str + title: str + subtitle: str + tooltip: Optional[str] + + +class Country(BaseModel): + name: str + name_id: str = Field(..., alias='nameId') + code: str + + +class SalePrice(BaseModel): + id: int + type: str + trigger_amount: int = Field(..., alias='triggerAmount') + price: Price + price_per_unit: Price = Field(..., alias='pricePerUnit') + badges: List[Badge] + valid_till: str = Field(..., alias='validTill') + active: bool + +class ProductPrice(BaseModel): + price: Price + price_per_unit: Price = Field(..., alias='pricePerUnit') + sales: List[SalePrice] + + +class SaleStock(BaseModel): + id: int + amount: int + unlimited_amount: bool = Field(..., alias='unlimitedAmount') + shelf_life: Optional[str] = Field(..., alias='shelfLife') + + +class Stock(BaseModel): + warehouse_id: int = Field(..., alias='warehouseId') + unavailability_reason: Optional[str] = Field(..., alias='unavailabilityReason') + preorder_enabled: bool = Field(..., alias='preorderEnabled') + max_basket_amount: int = Field(..., alias='maxBasketAmount') + max_basket_amount_reason: str = Field(..., alias='maxBasketAmountReason') + delivery_restriction: Optional[str] = Field(..., alias='deliveryRestriction') + expected_replenishment: Optional[datetime] = Field(..., alias='expectedReplenishment') + availability_dimension: int = Field(..., alias='availabilityDimension') + shelf_life: Optional[str] = Field(..., alias='shelfLife') + billable_packaging: Optional[str] = Field(..., alias='billablePackaging') + sales: List[SaleStock] + in_stock: bool = Field(..., alias='inStock') + + +class Product(BaseModel): + id: int + name: str + slug: str + main_category_id: int = Field(..., alias='mainCategoryId') + unit: str + textual_amount: str = Field(..., alias='textualAmount') + badges: List[Badge] + archived: bool + premium_only: bool = Field(..., alias='premiumOnly') + brand: Optional[str] + images: List[str] + countries: List[Country] + can_be_favorite: bool = Field(..., alias='canBeFavorite') + information: List + weighted_item: bool = Field(..., alias='weightedItem') + package_ratio: int = Field(..., alias='packageRatio') + seller_id: int = Field(..., alias='sellerId') + flag: Optional[str] + attachments: List + prices: ProductPrice + stock: Stock diff --git a/pyrohlik/models/recipe.py b/pyrohlik/models/recipe.py new file mode 100644 index 0000000..05ee48e --- /dev/null +++ b/pyrohlik/models/recipe.py @@ -0,0 +1,103 @@ +from typing import List, Optional +from pydantic import BaseModel, Field + + +class Srcset(BaseModel): + small: str + big: str + + +class Image(BaseModel): + path: str + alt: str + srcset: Srcset + + +class Serving(BaseModel): + name: str + default: bool + + +class Author(BaseModel): + name: str + annotation: str + image_path: str = Field(..., alias='imagePath') + +class Tip(BaseModel): + content: str + images: List[str] + + +class Step(BaseModel): + step_number: int = Field(..., alias='stepNumber') + name: str + content: str + images: List[str] + + +class Direction(BaseModel): + name: str + position: int + steps: List[Step] + + +class Tag(BaseModel): + slug: str + label: str + + +class Breadcrumb(BaseModel): + title: str + link: str + children: List[str] + + +class Item(BaseModel): + name: str + ingredient_id: int = Field(..., alias='ingredientId') + position: int + products_count: int = Field(..., alias='productsCount') + img_path: str = Field(..., alias='imgPath') + ingredient_name: str = Field(..., alias='ingredientName') + + +class Ingredient(BaseModel): + name: str + position: int + items: List[Item] + + +class IngredientsSummaryItem(BaseModel): + name: str + position: int + items: List[str] + + +class Recipe(BaseModel): + id: int + name: str + annotation: str + servings: List[Serving] + image: Image + author: Author + tips: List[Tip] + directions: List[Direction] + ingredients: List[Ingredient] + breadcrumbs: List[Breadcrumb] + ingredients_summary: List[IngredientsSummaryItem] = Field( + ..., alias='ingredientsSummary' + ) + tags: List[Tag] + type: str + has_all_ingredients_available: bool = Field(..., alias='hasAllIngredientsAvailable') + link: str + favorite: bool + is_favorite: bool = Field(..., alias='isFavorite') + is_new: bool = Field(..., alias='isNew') + is_best_seller: bool = Field(..., alias='isBestSeller') + + +class RecipeResponse(BaseModel): + status: int + messages: List + recipe: Recipe diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a6111c0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +api-client-pydantic