Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ LOGGING_LEVEL=DEBUG
API_KEY_APILAYER=
# https://openweathermap.org/price#weather
API_KEY_OPENWEATHER=
# https://newsapi.org/docs/get-started
API_KEY_NEWSAPI=

# время актуальности данных о странах (в секундах)
CACHE_TTL_COUNTRY=31_536_000
# время актуальности данных о курсах валют (в секундах)
CACHE_TTL_CURRENCY_RATES=86_400
# время актуальности данных о погоде (в секундах)
CACHE_TTL_WEATHER=10_700
# время актуальности данных о новостях (в секундах)
CACHE_TTL_NEWS=3_600
# количество новостей для отображения
NEWS_COUNT= 3
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ RUN apt-get update && apt-get install -y \
&& apt-get clean -y && rm -rf /var/lib/apt/lists/*

COPY ./requirements.txt ./setup.cfg ./black.toml ./.pylintrc /
COPY ./pytest.ini /pytest.ini

RUN --mount=type=cache,target=/root/.cache/pip \
pip install --upgrade pip -r /requirements.txt
Expand Down
28 changes: 23 additions & 5 deletions src/clients/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,45 @@
"""

from abc import ABC, abstractmethod
from typing import Optional
from http import HTTPStatus
from logging import getLogger
from typing import Any

import aiohttp

from logger import trace_config

logger = getLogger(__name__)


class BaseClient(ABC):
"""
Базовый класс, реализующий интерфейс для клиентов.
"""

params: dict[str, Any] = {}
headers: dict[str, Any] = {}

@abstractmethod
async def get_base_url(self) -> str:
"""
Получение базового URL для запросов.

:return:
"""

@abstractmethod
async def _request(self, endpoint: str) -> Optional[dict]:
async def _request(self, endpoint: str) -> dict | None:
"""
Формирование и выполнение запроса.

:param endpoint:
:return:
"""
async with aiohttp.ClientSession(trace_configs=[trace_config]) as session:
async with session.get(
endpoint, params=self.params, headers=self.headers
) as response:
if response.status == HTTPStatus.OK:
return await response.json()
logger.error(
"Error: %s %s %s", response.url, response.status, response.reason
)
return None
23 changes: 3 additions & 20 deletions src/clients/country.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
"""
Функции для взаимодействия с внешним сервисом-провайдером данных о странах.
"""
from http import HTTPStatus
from typing import Optional

import aiohttp

from clients.base import BaseClient
from logger import trace_config
from settings import API_KEY_APILAYER


Expand All @@ -16,27 +10,16 @@ class CountryClient(BaseClient):
Реализация функций для взаимодействия с внешним сервисом-провайдером данных о странах.
"""

headers = {"apikey": API_KEY_APILAYER}

async def get_base_url(self) -> str:
return "https://api.apilayer.com/geo/country"

async def _request(self, endpoint: str) -> Optional[dict]:

# формирование заголовков запроса
headers = {"apikey": API_KEY_APILAYER}

async with aiohttp.ClientSession(trace_configs=[trace_config]) as session:
async with session.get(endpoint, headers=headers) as response:
if response.status == HTTPStatus.OK:
return await response.json()

return None

async def get_countries(self, bloc: str = "eu") -> Optional[dict]:
async def get_countries(self, bloc: str = "eu") -> dict | None:
"""
Получение данных о странах.

:param bloc: Регион
:return:
"""

return await self._request(f"{await self.get_base_url()}/regional_bloc/{bloc}")
28 changes: 6 additions & 22 deletions src/clients/currency.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
"""
Функции для взаимодействия с внешним сервисом-провайдером данных о курсах валют.
"""
from http import HTTPStatus
from typing import Optional

import aiohttp

from clients.base import BaseClient
from logger import trace_config
from settings import API_KEY_APILAYER


Expand All @@ -16,27 +10,17 @@ class CurrencyClient(BaseClient):
Реализация функций для взаимодействия с внешним сервисом-провайдером данных о курсах валют.
"""

headers = {"apikey": API_KEY_APILAYER}

async def get_base_url(self) -> str:
return "https://api.apilayer.com/fixer/latest"

async def _request(self, endpoint: str) -> Optional[dict]:

# формирование заголовков запроса
headers = {"apikey": API_KEY_APILAYER}

async with aiohttp.ClientSession(trace_configs=[trace_config]) as session:
async with session.get(endpoint, headers=headers) as response:
if response.status == HTTPStatus.OK:
return await response.json()

return None

async def get_rates(self, base: str = "rub") -> Optional[dict]:
async def get_rates(self, base: str = "rub") -> dict | None:
"""
Получение данных о курсах валют.
Получение данных о курсах валют.

:param base: Базовая валюта
:return:
"""

return await self._request(f"{await self.get_base_url()}?base={base}")
self.params["base"] = base
return await self._request(await self.get_base_url())
26 changes: 26 additions & 0 deletions src/clients/news.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
Функции для взаимодействия с внешним сервисом-провайдером данных о новостях.
"""
from clients.base import BaseClient
from settings import API_KEY_NEWSAPI, NEWS_COUNT


class NewsClient(BaseClient):
"""
Реализация функций для взаимодействия с внешним сервисом-провайдером данных о новостях.
"""

params = {"apiKey": API_KEY_NEWSAPI, "pageSize": NEWS_COUNT}

async def get_base_url(self) -> str:
return "https://newsapi.org/v2/everything"

async def get_news(self, location: str) -> dict | None:
"""
Получение новостей по стране

:param location: Город и страна
:return:
"""
self.params["q"] = location
return await self._request(await self.get_base_url())
25 changes: 5 additions & 20 deletions src/clients/weather.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
"""
Функции для взаимодействия с внешним сервисом-провайдером данных о погоде.
"""
from http import HTTPStatus
from typing import Optional

import aiohttp

from clients.base import BaseClient
from logger import trace_config
from settings import API_KEY_OPENWEATHER


Expand All @@ -16,26 +10,17 @@ class WeatherClient(BaseClient):
Реализация функций для взаимодействия с внешним сервисом-провайдером данных о погоде.
"""

params = {"appid": API_KEY_OPENWEATHER, "units": "metric"}

async def get_base_url(self) -> str:
return "https://api.openweathermap.org/data/2.5/weather"

async def _request(self, endpoint: str) -> Optional[dict]:

async with aiohttp.ClientSession(trace_configs=[trace_config]) as session:
async with session.get(endpoint) as response:
if response.status == HTTPStatus.OK:
return await response.json()

return None

async def get_weather(self, location: str) -> Optional[dict]:
async def get_weather(self, location: str) -> dict | None:
"""
Получение данных о погоде.

:param location: Город и страна
:return:
"""

return await self._request(
f"{await self.get_base_url()}?units=metric&q={location}&appid={API_KEY_OPENWEATHER}"
)
self.params["q"] = location
return await self._request(await self.get_base_url())
Loading