-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
808a4a5
commit 39e13c0
Showing
25 changed files
with
566 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import asyncio | ||
from contextlib import asynccontextmanager | ||
|
||
from faststream import FastStream, ContextRepo | ||
|
||
from periphery import mongo | ||
from periphery.brokers import kafka_broker | ||
from presentation.event_routes import router | ||
|
||
|
||
@asynccontextmanager | ||
async def faststream_lifespan(context: ContextRepo) -> None: | ||
yield | ||
await mongo.client.close() | ||
|
||
|
||
async def start_faststream() -> None: | ||
faststream = FastStream(kafka_broker, lifespan=faststream_lifespan) | ||
kafka_broker.include_router(router) | ||
|
||
await faststream.run() | ||
|
||
|
||
async def main() -> None: | ||
await start_faststream() | ||
|
||
|
||
if __name__ == "__main__": | ||
asyncio.run(main()) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from infrastructure import ( | ||
mq_gateway as mq_gateway, | ||
repos as repos, | ||
yandex_lavka_gateway as yandex_lavka_gateway, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from pydantic import BaseModel | ||
|
||
from periphery.brokers import redis_broker | ||
|
||
|
||
class _MultiplicationOccurred(BaseModel): | ||
a: int | ||
b: int | ||
result: int | ||
|
||
|
||
async def push_multiplication_occurred(a: int, b: int, result: int) -> None: | ||
event = _MultiplicationOccurred(a=a, b=b, result=result) | ||
|
||
async with redis_broker: | ||
await redis_broker.publish(event, "multiplication_occurred") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from infrastructure.repos import ( | ||
users as users, | ||
products as products, | ||
categories as categories, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from model.domain.entities import Category | ||
from periphery.mongo import db | ||
|
||
|
||
async def add(category: Category) -> None: | ||
await db.categories.insert_one({ | ||
"id": category.id, | ||
"user_id": category.user_id, | ||
"name": category.name, | ||
"product_ids": category.product_ids, | ||
"subcategory_ids": category.subcategory_ids, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from typing import Iterable | ||
|
||
from model.domain.entities import Product | ||
from periphery.mongo import db | ||
|
||
|
||
async def add(product: Product) -> None: | ||
await db.products.insert_one(_document_of(product)) | ||
|
||
|
||
async def extend_by(products: Iterable[Product]) -> None: | ||
await db.products.insert_many(map(_document_of, products)) | ||
|
||
|
||
def _document_of(product: Product) -> dict: | ||
return { | ||
"id": product.id, | ||
"name": product.name, | ||
"price_rubles": product.price.rubles, | ||
"page_url": product.page.url, | ||
"quantity": { | ||
"total": product.quantity.total, | ||
"unit_code": product.quantity.unit.value, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from typing import Optional | ||
|
||
from model.domain.entities import User | ||
from model.domain.vos import Address, City | ||
from periphery.mongo import db | ||
|
||
|
||
async def add(user: User) -> None: | ||
await db.users.insert_one({ | ||
"id": user.id, | ||
"telegram_chat_id": user.telegram_chat_id, | ||
"city": user.address.city.name, | ||
}) | ||
|
||
|
||
async def get_by_telegram_chat_id(telegram_chat_id: int) -> Optional[User]: | ||
user_record = await db.users.find_one( | ||
{"telegram_chat_id": telegram_chat_id}, | ||
{"id": 1, "city": 1, "_id": 0}, | ||
) | ||
|
||
if user_record is None: | ||
return None | ||
|
||
id_ = user_record.get("id") | ||
city_name = user_record.get("city") | ||
|
||
if None in [id_, city_name]: | ||
return None | ||
|
||
address = Address(city=City(name=city_name)) | ||
return User(id=id_, telegram_chat_id=telegram_chat_id, address=address) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from typing import Iterable | ||
|
||
import aiohttp | ||
|
||
from model.domain.entities import User, Product | ||
from model.domain.vos import Page, Price, QuantityUnit, Quantity | ||
|
||
|
||
async def searth_products_for( | ||
user: User, | ||
*, | ||
query: str, | ||
latitude: float, | ||
longitude: float, | ||
products_limit: int = 32, | ||
subcategories_limit: int = 0, | ||
) -> Iterable[Product]: | ||
url = "https://lavka.yandex.ru/api/v1/providers/search/v2/lavka" | ||
headers = { | ||
"accept": "application/json", | ||
"accept-language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7", | ||
"content-type": "application/json", | ||
"sec-ch-ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"", | ||
"sec-ch-ua-mobile": "?0", | ||
"sec-ch-ua-platform": "\"Windows\"", | ||
"sec-fetch-dest": "empty", | ||
"sec-fetch-mode": "cors", | ||
"sec-fetch-site": "same-origin", | ||
"x-csrf-token": "3248921c6150c0e59d4f10b94d2b4ac3d9222fb3:1719991575", | ||
"x-lavka-web-city": "213", | ||
"x-lavka-web-locale": "ru-RU" | ||
} | ||
body = { | ||
"additionalData": { | ||
"city": user.city.name, | ||
}, | ||
"text": query, | ||
"position": {"location": [latitude, longitude]}, | ||
"productsLimit": products_limit, | ||
"subcategories_limit": subcategories_limit, | ||
"depotType": "regular" | ||
} | ||
|
||
async with aiohttp.ClientSession() as session: | ||
async with session.post(url, json=body, headers=headers) as response: | ||
if response.status != 200: | ||
return None | ||
|
||
raw_product_by_index = await response.json().get("products") | ||
|
||
if raw_product_by_index is None: | ||
return None | ||
|
||
raw_products = raw_product_by_index.values() | ||
|
||
for raw_product in raw_products: | ||
rubels = raw_product.get("numberDiscountPrice") | ||
if rubels is None: | ||
rubels = raw_product["numberPrice"] | ||
|
||
if "г" in raw_product["amount"] or "кг" in raw_product["amount"]: | ||
quantity_unit = QuantityUnit.kilogram | ||
elif "мл" in raw_product["amount"] or "л" in raw_product["amount"]: | ||
quantity_unit = QuantityUnit.liter | ||
else: | ||
quantity_unit = None | ||
|
||
total = int(raw_product["amount"].split("&")[0]) | ||
|
||
url = f"https://lavka.yandex.ru/213/good/{raw_product["deepLink"]}" | ||
name = raw_product["title"] | ||
|
||
yield Product( | ||
name=name, | ||
page=Page(url=url), | ||
price=Price(rubels=rubels), | ||
quantity=Quantity(total=total, unit=quantity_unit) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from model import ( | ||
domain as domain, | ||
cases as cases, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from cases import ( | ||
add_category_by_query as add_category_by_query, | ||
registration as registration, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from dataclasses import dataclass | ||
|
||
from infrastructure import yandex_lavka_gateway | ||
from infrastructure.repos import users, products, categories | ||
from model.domain.entities import Category, Product, User | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class OutputDTO: | ||
user: User | ||
category: Category | ||
products: tuple[Product, ...] | ||
|
||
|
||
class NoUserError(Exception): ... | ||
|
||
|
||
async def perform( | ||
query: str, | ||
latitude: float, | ||
longitude: float, | ||
telegram_chat_id: int, | ||
) -> OutputDTO: | ||
user = await users.get_by_telegram_chat_id(telegram_chat_id) | ||
|
||
if user is None: | ||
raise NoUserError | ||
|
||
found_products = tuple(await yandex_lavka_gateway.searth_products_for( | ||
user, | ||
query=query, | ||
latitude=latitude, | ||
longitude=longitude, | ||
)) | ||
|
||
await products.extend_by(found_products) | ||
|
||
product_ids = [product.id for product in found_products] | ||
|
||
category = Category( | ||
user_id=user.id, | ||
name=query, | ||
product_ids=product_ids, | ||
subcategory_ids=list(), | ||
) | ||
|
||
await categories.add(category) | ||
|
||
return OutputDTO(user=user, category=category, products=products) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from infrastructure.repos import users | ||
from model.domain.entities import User | ||
from model.domain.vos import Address, City | ||
|
||
|
||
async def perform(telegram_chat_id: int, city_name: str) -> User: | ||
user = await users.get_by_telegram_chat_id(telegram_chat_id) | ||
|
||
if user is not None: | ||
return user | ||
|
||
address = Address(city=City(name=city_name)) | ||
user = User(telegram_chat_id=telegram_chat_id, address=address) | ||
|
||
await users.add(user) | ||
|
||
return user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from model.domain import ( | ||
entities as entities, | ||
errors as errors, | ||
vos as vos, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from dataclasses import dataclass, field | ||
from uuid import UUID, uuid4 | ||
|
||
from model.domain.errors import FreeProductError | ||
from model.domain.vos import Price, Page, Quantity, Address | ||
|
||
|
||
@dataclass(kw_only=True) | ||
class User: | ||
id: UUID = field(default_factory=uuid4) | ||
telegram_chat_id: int | ||
address: Address | ||
|
||
|
||
@dataclass(kw_only=True) | ||
class Category: | ||
id: UUID = field(default_factory=uuid4) | ||
user_id: UUID | ||
name: str | ||
product_ids: list[UUID] | ||
subcategory_ids: list[UUID] | ||
|
||
|
||
@dataclass(kw_only=True) | ||
class Product: | ||
id: UUID = field(default_factory=uuid4) | ||
name: str | ||
price: Price | ||
quantity: Quantity | ||
page: Page | ||
|
||
def __post_init__(self) -> None: | ||
if self.price.rubles == 0: | ||
raise FreeProductError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
class ValidationError(Exception): ... | ||
|
||
|
||
class FreeProductError(ValidationError): ... | ||
|
||
|
||
class NegativePriceError(ValidationError): ... | ||
|
||
|
||
class NegativeQuantityError(ValidationError): ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from dataclasses import dataclass, field | ||
from enum import Enum, auto | ||
from typing import Optional | ||
|
||
from model.domain.errors import NegativePriceError, NegativeQuantityError | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class Price: | ||
rubles: int | ||
|
||
def __post_init__(self) -> None: | ||
if self.rubles < 0: | ||
raise NegativePriceError | ||
|
||
|
||
class QuantityUnit(Enum): | ||
kilogram = auto() | ||
liter = auto() | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class Quantity: | ||
total: int | ||
unit: Optional[QuantityUnit] = field(default=None) | ||
|
||
def __post_init__(self) -> None: | ||
if self.rubles < 0: | ||
raise NegativeQuantityError | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class Page: | ||
url: str | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class City: | ||
name: str | ||
|
||
|
||
@dataclass(frozen=True, kw_only=True) | ||
class Address: | ||
city: City |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from periphery import ( | ||
mongo as mongo, | ||
envs as envs, | ||
brokers as brokers, | ||
) |
Oops, something went wrong.