Skip to content

Instantly share code, notes, and snippets.

@Bahus
Forked from mihalt/tasks.py
Last active August 1, 2022 15:19
Show Gist options
  • Save Bahus/19c9350f083678797e5bacb873ef827b to your computer and use it in GitHub Desktop.
Save Bahus/19c9350f083678797e5bacb873ef827b to your computer and use it in GitHub Desktop.
planning of architecture
from decimal import Decimal
import pydantic
@shared_task
def get_provider_api_stocks(api_key, url_constructor, deserialize_constructor):
# api_key в принципе нельзя передавать так
return True
from typing import Protocol
class BaseCodeProviderClient(Protocol):
@abstractmethod
def get_one_game_detail_stock():
return True
@abstractmethod
def get_codes_from_order():
return True
@abstractmethod
def get_orders_history():
return True
@abstractmethod
def get_detail_of_order(self, order_id: str):
return True
def get_profitable_stocks_info(params):
get_provider_api_stocks(params)
get_one_game_detail_stock(params)
class HTTPClient:
def __init__(
self,
base_url: str,
retry_policy: Retry(connect=2, read=2),
timeout_policy: TimePolicy(5, 20),
auth: BasicHTTPAuth(user, password),
middleware = [HTTPLoggingMiddleware, HTTPMetricsMiddleware, HTTPXRequestID],
session: Optional[requests.Session] = None # requests.Session(),
):
pass
class OrderDetails(pydantic.BaseModel):
id: int
amount: Decimal
code: str
class GameEXECodesProvider(BaseCodeProviderClient):
slug = 'game_exe'
def __init__(self, provider: 'CodeProvider'):
self._provider = provider
self._http_client = HTTPClient(
base_url=provider.url,
auth=provider.get_auth(),
)
def get_detail_of_order(self, order_id: str) -> OrderDetails:
response = self._http_client.get(f'/orders/{order_id}')
return OrderDetails.parse_obj(response.json())
class Codes1Provider(BaseCodeProviderClient):
slug = 'code_1'
def get_one_game_detail_stock(self):
"""do something"""
class Codes2Provider(BaseCodeProviderClient):
slug = 'code_2'
def get_orders_history(self, ...):
"""do something"""
AVAILABLE_PROVIDERS = {
GameEXECodesProvider.slug: GameEXECodesProvider,
Codes1Provider.slug: Codes1Provider,
Codes2Provider.slug: Codes2Provider,
}
class CodeProvider(Model):
slug = TextField(unique=True)
url = URLField()
# Authentication data can be different between providers
auth_data = JSONField()
@shared_task
def gather_new_codes_dispatcher():
"""Scheduled tasks, executed every hour and gather all new available codes from providers."""
for provider_id in CodeProvider.objects.values_list('id', flat=True):
gather_new_cored_for_provider(provider_id=provider_id).apply_async()
@shared_task
def gather_new_cored_for_provider(*, provider_id: int):
provider = CodeProvider.objects.get(id=provider_id)
provider_client: BaseCodeProviderClient = AVAILABLE_PROVIDERS[provider.slug](provider=provider)
codes = provider_client.gather_codes()
save_codes()
@mihalt
Copy link

mihalt commented Jul 29, 2022

а почему нельзя прямо в коде во время инициализации объекта доставать его из базы? Зачем его явно передавать?

def __init__(self):

        self._provider = Provider.objects.get(slug=self.slug)
        self._http_client = HTTPClient(
            base_url=self._provider.url,
            auth=self._provider.get_auth(),
        )

@Bahus
Copy link
Author

Bahus commented Jul 30, 2022

в __init__ не стоит вообще никаких сложных действий делать (ходить в базу, ходить по http) и т.д. так как это считается сайд эффектом, в инициализаторе обычно идет присвоение данных, сами данные передаются из получаются из вне.

В указанном тобой случае класс бы выглядел так:

class GameEXECodesProvider(BaseCodeProviderClient):

    def __init__(self, provider_slug: str):
        self._provider_slug = provider_slug

    @cached_property
    def provider(self) -> 'Provider':
        return Provider.objects.get(slug=self.slug)

@mihalt
Copy link

mihalt commented Jul 31, 2022

в __init__ не стоит вообще никаких сложных действий делать (ходить в базу, ходить по http) и т.д. так как это считается сайд эффектом, в инициализаторе обычно идет присвоение данных, сами данные передаются из получаются из вне.

В указанном тобой случае класс бы выглядел так:

class GameEXECodesProvider(BaseCodeProviderClient):

    def __init__(self, provider_slug: str):
        self._provider_slug = provider_slug

    @cached_property
    def provider(self) -> 'Provider':
        return Provider.objects.get(slug=self.slug)

аа, ну да. Не вопрос. Так, что такой вариант c получением объекта из класса, тоже норм ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment