diff --git a/README.md b/README.md index b731e52..caa6b71 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Install the appropriate software: To access the API, visit the appropriate resources and obtain an access token: - APILayer – Geography API (https://apilayer.com/marketplace/geo-api) + - APILayer – Currency API (https://apilayer.com/marketplace/fixer-api) - OpenWeather – Weather Free Plan (https://openweathermap.org/price#weather) Set received access tokens as environment variable values (in `.env` file): diff --git a/docs/source/index.rst b/docs/source/index.rst index faf8d10..af27c58 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -58,6 +58,7 @@ Чтобы получить доступ к API внешних систем, посетите соответствующие сервисы и получите токены доступа: + * APILayer – Currency API (https://apilayer.com/marketplace/fixer-api) * APILayer — Geography API (https://apilayer.com/marketplace/geo-api) * OpenWeather – Weather Free Plan (https://openweathermap.org/price#weather) diff --git a/src/collectors/collector.py b/src/collectors/collector.py index ebadf7e..e0b4885 100644 --- a/src/collectors/collector.py +++ b/src/collectors/collector.py @@ -91,6 +91,8 @@ async def read(cls) -> Optional[list[CountryDTO]]: result_list.append( CountryDTO( capital=item["capital"], + longitude=item["longitude"], + latitude=item["latitude"], alpha2code=item["alpha2code"], alt_spellings=item["alt_spellings"], currencies={ @@ -103,6 +105,7 @@ async def read(cls) -> Optional[list[CountryDTO]]: population=item["population"], subregion=item["subregion"], timezones=item["timezones"], + area=item["area"], ) ) @@ -204,7 +207,7 @@ async def read(cls, location: LocationDTO) -> Optional[WeatherInfoDTO]: Чтение данных из кэша. :param location: - :return: + :return: Погода """ filename = f"{location.capital}_{location.alpha2code}".lower() @@ -219,6 +222,8 @@ async def read(cls, location: LocationDTO) -> Optional[WeatherInfoDTO]: humidity=result["main"]["humidity"], wind_speed=result["wind"]["speed"], description=result["weather"][0]["description"], + visibility=result["visibility"], + offset_seconds=result["timezone"], ) return None diff --git a/src/collectors/models.py b/src/collectors/models.py index 7e36198..d4c6823 100644 --- a/src/collectors/models.py +++ b/src/collectors/models.py @@ -4,6 +4,8 @@ from pydantic import Field, BaseModel +from datetime import datetime + class HashableBaseModel(BaseModel): """ @@ -68,6 +70,8 @@ class CountryDTO(BaseModel): CountryDTO( capital="Mariehamn", + longitude=88.0, + latitude=88.0, alpha2code="AX", alt_spellings=[ "AX", @@ -81,6 +85,7 @@ class CountryDTO(BaseModel): ) }, flag="http://assets.promptapi.com/flags/AX.svg", + area"=888888.0 languages={ LanguagesInfoDTO( name="Swedish", @@ -93,14 +98,18 @@ class CountryDTO(BaseModel): timezones=[ "UTC+02:00", ], + ) """ capital: str + longitude: float | None + latitude: float | None alpha2code: str alt_spellings: list[str] currencies: set[CurrencyInfoDTO] flag: str + area: float | None languages: set[LanguagesInfoDTO] name: str population: int @@ -108,6 +117,8 @@ class CountryDTO(BaseModel): timezones: list[str] + + class CurrencyRatesDTO(BaseModel): """ Модель данных о курсах валют. @@ -140,6 +151,9 @@ class WeatherInfoDTO(BaseModel): humidity=54, wind_speed=4.63, description="scattered clouds", + visibility = 10000, + offset_seconds = 3600 + ) """ @@ -148,6 +162,9 @@ class WeatherInfoDTO(BaseModel): humidity: int wind_speed: float description: str + visibility: int + offset_seconds: int + class LocationInfoDTO(BaseModel): @@ -190,7 +207,8 @@ class LocationInfoDTO(BaseModel): pressure=1023, humidity=54, wind_speed=4.63, - description="scattered clouds", + description="scattered clouds",c + offset_seconds=3600, ), currency_rates={ "EUR": 0.016503, diff --git a/src/renderer.py b/src/renderer.py index 8b90dcc..28b6439 100644 --- a/src/renderer.py +++ b/src/renderer.py @@ -1,7 +1,7 @@ """ Функции для формирования выходной информации. """ - +import datetime from decimal import ROUND_HALF_UP, Decimal from collectors.models import LocationInfoDTO @@ -27,16 +27,39 @@ async def render(self) -> tuple[str, ...]: :return: Результат форматирования """ - - return ( - f"Страна: {self.location_info.location.name}", - f"Столица: {self.location_info.location.capital}", - f"Регион: {self.location_info.location.subregion}", - f"Языки: {await self._format_languages()}", - f"Население страны: {await self._format_population()} чел.", - f"Курсы валют: {await self._format_currency_rates()}", - f"Погода: {self.location_info.weather.temp} °C", - ) + render_information_country = { + "Страна": self.location_info.location.name, + "Площадь": self.location_info.location.area, + "Регион": self.location_info.location.subregion, + "Языки": await self._format_languages(), + "Население страны": await self._format_population(), + "Курсы валют": await self._format_currency_rates(), + + "Столица": self.location_info.location.capital, + "Широта": self.location_info.location.latitude, + "Долгота": self.location_info.location.longitude, + + "Погода": self.location_info.weather.temp, + "Время": await self._format_current_time(), + "Часовой пояс": await self._get_timezone(), + "Описание погоды": self.location_info.weather.description, + "Видимость": self.location_info.weather.visibility, + "Влажность": self.location_info.weather.humidity, + "Скорость ветра": self.location_info.weather.wind_speed, + "Давление": self.location_info.weather.pressure, + } + + first_column_width = max(len(key) for key in render_information_country) + 1 + second_column_width = max(len(str(value)) for value in render_information_country.values()) + 1 + formatted_render_information_country = [("-" * (first_column_width + second_column_width + 3))] + formatted_render_information_country.extend( + [ + f"|{key:<{first_column_width}}|{value:>{second_column_width}}|" + for key, value in render_information_country.items() + ]) + formatted_render_information_country.append("-" * (first_column_width + second_column_width + 3)) + + return tuple(formatted_render_information_country) async def _format_languages(self) -> str: """ @@ -50,6 +73,25 @@ async def _format_languages(self) -> str: for item in self.location_info.location.languages ) + async def _get_timezone(self) -> str: + """ + Форматирование информации о времени. + + :return: + """ + offset_hours = self.location_info.weather.offset_seconds / 3600.0 + return "UTC{:+d}:{:02d}".format(int(offset_hours), int((offset_hours % 1) * 60)) + + async def _format_current_time(self) -> str: + """ + Форматирование информации о времени. + :return: + """ + + render_time=datetime.datetime.now() + datetime.timedelta( + seconds=self.location_info.weather.offset_seconds) + return render_time.strftime("%X, %x") + async def _format_population(self) -> str: """ Форматирование информации о населении.