weathergrabber 0.0.8b6__py3-none-any.whl → 0.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. weathergrabber/__init__.py +1 -1
  2. weathergrabber/adapter/repository/forecast_repository.py +135 -0
  3. weathergrabber/adapter/tty/console_tty.py +4 -4
  4. weathergrabber/adapter/tty/json_tty.py +3 -3
  5. weathergrabber/adapter/tty/statistics_tty.py +35 -0
  6. weathergrabber/adapter/tty/waybar_tty.py +4 -4
  7. weathergrabber/{service → application/services}/extract_aqi_service.py +1 -1
  8. weathergrabber/{service → application/services}/extract_current_conditions_service.py +5 -5
  9. weathergrabber/{service → application/services}/extract_daily_forecast_oldstyle_service.py +4 -4
  10. weathergrabber/{service → application/services}/extract_daily_forecast_service.py +6 -6
  11. weathergrabber/{service → application/services}/extract_health_activities_service.py +1 -1
  12. weathergrabber/{service → application/services}/extract_hourly_forecast_oldstyle_service.py +3 -3
  13. weathergrabber/{service → application/services}/extract_hourly_forecast_service.py +5 -5
  14. weathergrabber/{service → application/services}/extract_today_details_service.py +8 -8
  15. weathergrabber/application/services/retrieve_forecast_from_cache_service.py +27 -0
  16. weathergrabber/application/services/retrieve_statistics_service.py +20 -0
  17. weathergrabber/application/services/save_forecast_to_cache_service.py +21 -0
  18. weathergrabber/application/usecases/statistics_uc.py +19 -0
  19. weathergrabber/application/usecases/weather_forecast_uc.py +123 -0
  20. weathergrabber/application/weathergrabber_application.py +92 -0
  21. weathergrabber/cli.py +6 -2
  22. weathergrabber/core.py +7 -3
  23. weathergrabber/domain/adapter/mappers/air_quality_index_mapper.py +25 -0
  24. weathergrabber/domain/adapter/mappers/city_location_mapper.py +15 -0
  25. weathergrabber/domain/adapter/mappers/color_mapper.py +17 -0
  26. weathergrabber/domain/adapter/mappers/current_conditions_mapper.py +32 -0
  27. weathergrabber/domain/adapter/mappers/daily_predictions_mapper.py +30 -0
  28. weathergrabber/domain/adapter/mappers/day_night_mapper.py +23 -0
  29. weathergrabber/domain/adapter/mappers/forecast_mapper.py +38 -0
  30. weathergrabber/domain/adapter/mappers/health_activities_mapper.py +15 -0
  31. weathergrabber/domain/adapter/mappers/hourly_predictions_mapper.py +38 -0
  32. weathergrabber/domain/adapter/mappers/label_value_mapper.py +13 -0
  33. weathergrabber/domain/adapter/mappers/moon_phase_mapper.py +16 -0
  34. weathergrabber/domain/adapter/mappers/precipitation_mapper.py +13 -0
  35. weathergrabber/domain/adapter/mappers/search_mapper.py +13 -0
  36. weathergrabber/domain/adapter/mappers/statistics_mapper.py +25 -0
  37. weathergrabber/domain/adapter/mappers/sunrise_sunset_mapper.py +23 -0
  38. weathergrabber/domain/adapter/mappers/temperature_high_low_mapper.py +15 -0
  39. weathergrabber/domain/adapter/mappers/timestamp_mapper.py +15 -0
  40. weathergrabber/domain/adapter/mappers/today_details_mapper.py +42 -0
  41. weathergrabber/domain/adapter/mappers/uv_index_mapper.py +17 -0
  42. weathergrabber/domain/adapter/mappers/weather_icon_enum_mapper.py +11 -0
  43. weathergrabber/domain/adapter/mappers/wind_mapper.py +16 -0
  44. weathergrabber/domain/adapter/output_enum.py +1 -1
  45. weathergrabber/domain/adapter/params.py +25 -4
  46. weathergrabber/domain/{moon_phase.py → entities/moon_phase.py} +1 -1
  47. weathergrabber/domain/entities/statistics.py +46 -0
  48. weathergrabber/domain/{sunrise_sunset.py → entities/sunrise_sunset.py} +0 -1
  49. weathergrabber/domain/{weather_icon_enum.py → entities/weather_icon_enum.py} +1 -0
  50. {weathergrabber-0.0.8b6.dist-info → weathergrabber-0.0.9.dist-info}/METADATA +32 -7
  51. weathergrabber-0.0.9.dist-info/RECORD +80 -0
  52. {weathergrabber-0.0.8b6.dist-info → weathergrabber-0.0.9.dist-info}/WHEEL +1 -1
  53. weathergrabber/domain/adapter/mapper/air_quality_index_mapper.py +0 -13
  54. weathergrabber/domain/adapter/mapper/city_location_mapper.py +0 -8
  55. weathergrabber/domain/adapter/mapper/color_mapper.py +0 -10
  56. weathergrabber/domain/adapter/mapper/current_conditions_mapper.py +0 -16
  57. weathergrabber/domain/adapter/mapper/daily_predictions_mapper.py +0 -15
  58. weathergrabber/domain/adapter/mapper/day_night_mapper.py +0 -12
  59. weathergrabber/domain/adapter/mapper/forecast_mapper.py +0 -20
  60. weathergrabber/domain/adapter/mapper/health_activities_mapper.py +0 -8
  61. weathergrabber/domain/adapter/mapper/hourly_predictions_mapper.py +0 -19
  62. weathergrabber/domain/adapter/mapper/label_value_mapper.py +0 -7
  63. weathergrabber/domain/adapter/mapper/moon_phase_mapper.py +0 -9
  64. weathergrabber/domain/adapter/mapper/precipitation_mapper.py +0 -7
  65. weathergrabber/domain/adapter/mapper/search_mapper.py +0 -7
  66. weathergrabber/domain/adapter/mapper/sunrise_sunset_mapper.py +0 -13
  67. weathergrabber/domain/adapter/mapper/temperature_high_low_mapper.py +0 -8
  68. weathergrabber/domain/adapter/mapper/timestamp_mapper.py +0 -8
  69. weathergrabber/domain/adapter/mapper/today_details_mapper.py +0 -21
  70. weathergrabber/domain/adapter/mapper/uv_index_mapper.py +0 -9
  71. weathergrabber/domain/adapter/mapper/weather_icon_enum_mapper.py +0 -8
  72. weathergrabber/domain/adapter/mapper/wind_mapper.py +0 -7
  73. weathergrabber/usecase/use_case.py +0 -87
  74. weathergrabber/weathergrabber_application.py +0 -78
  75. weathergrabber-0.0.8b6.dist-info/RECORD +0 -72
  76. /weathergrabber/{service → application/services}/extract_temperature_service.py +0 -0
  77. /weathergrabber/{service → application/services}/read_weather_service.py +0 -0
  78. /weathergrabber/{service → application/services}/search_location_service.py +0 -0
  79. /weathergrabber/domain/{air_quality_index.py → entities/air_quality_index.py} +0 -0
  80. /weathergrabber/domain/{city_location.py → entities/city_location.py} +0 -0
  81. /weathergrabber/domain/{color.py → entities/color.py} +0 -0
  82. /weathergrabber/domain/{current_conditions.py → entities/current_conditions.py} +0 -0
  83. /weathergrabber/domain/{daily_predictions.py → entities/daily_predictions.py} +0 -0
  84. /weathergrabber/domain/{day_night.py → entities/day_night.py} +0 -0
  85. /weathergrabber/domain/{forecast.py → entities/forecast.py} +0 -0
  86. /weathergrabber/domain/{health_activities.py → entities/health_activities.py} +0 -0
  87. /weathergrabber/domain/{hourly_predictions.py → entities/hourly_predictions.py} +0 -0
  88. /weathergrabber/domain/{label_value.py → entities/label_value.py} +0 -0
  89. /weathergrabber/domain/{moon_phase_enum.py → entities/moon_phase_enum.py} +0 -0
  90. /weathergrabber/domain/{precipitation.py → entities/precipitation.py} +0 -0
  91. /weathergrabber/domain/{search.py → entities/search.py} +0 -0
  92. /weathergrabber/domain/{temperature_hight_low.py → entities/temperature_hight_low.py} +0 -0
  93. /weathergrabber/domain/{timestamp.py → entities/timestamp.py} +0 -0
  94. /weathergrabber/domain/{today_details.py → entities/today_details.py} +0 -0
  95. /weathergrabber/domain/{uv_index.py → entities/uv_index.py} +0 -0
  96. /weathergrabber/domain/{wind.py → entities/wind.py} +0 -0
  97. {weathergrabber-0.0.8b6.dist-info → weathergrabber-0.0.9.dist-info}/entry_points.txt +0 -0
  98. {weathergrabber-0.0.8b6.dist-info → weathergrabber-0.0.9.dist-info}/licenses/LICENSE +0 -0
  99. {weathergrabber-0.0.8b6.dist-info → weathergrabber-0.0.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,92 @@
1
+ import logging
2
+ from time import sleep
3
+ from weathergrabber.adapter.repository.forecast_repository import ForecastRepository
4
+ from weathergrabber.domain.adapter.params import Params
5
+ from weathergrabber.domain.adapter.output_enum import OutputEnum
6
+ from weathergrabber.adapter.client.weather_api import WeatherApi
7
+ from weathergrabber.adapter.client.weather_search_api import WeatherSearchApi
8
+ from .services.search_location_service import SearchLocationService
9
+ from .services.read_weather_service import ReadWeatherService
10
+ from .services.retrieve_statistics_service import RetrieveStatisticsService
11
+ from .services.retrieve_forecast_from_cache_service import RetrieveForecastFromCacheService
12
+ from .services.save_forecast_to_cache_service import SaveForecastToCacheService
13
+ from .services.extract_current_conditions_service import ExtractCurrentConditionsService
14
+ from .services.extract_today_details_service import ExtractTodayDetailsService
15
+ from .services.extract_aqi_service import ExtractAQIService
16
+ from .services.extract_health_activities_service import ExtractHealthActivitiesService
17
+ from .services.extract_hourly_forecast_service import ExtractHourlyForecastService
18
+ from .services.extract_hourly_forecast_oldstyle_service import ExtractHourlyForecastOldstyleService
19
+ from .services.extract_daily_forecast_service import ExtractDailyForecastService
20
+ from .services.extract_daily_forecast_oldstyle_service import ExtractDailyForecastOldstyleService
21
+ from .usecases.weather_forecast_uc import WeatherForecastUC
22
+ from .usecases.statistics_uc import StatisticsUC
23
+
24
+
25
+ class WeatherGrabberApplication:
26
+
27
+ def _beans(self):
28
+ self.weather_search_api = WeatherSearchApi()
29
+ self.weather_api = WeatherApi()
30
+ self.forecast_repository = ForecastRepository()
31
+ self.search_location_service = SearchLocationService(self.weather_search_api)
32
+ self.read_weather_service = ReadWeatherService(self.weather_api)
33
+ self.extract_current_conditions_service = ExtractCurrentConditionsService()
34
+ self.extract_today_details_service = ExtractTodayDetailsService()
35
+ self.extract_aqi_service = ExtractAQIService()
36
+ self.extract_health_activities_service = ExtractHealthActivitiesService()
37
+ self.extract_hourly_forecast_service = ExtractHourlyForecastService()
38
+ self.extract_hourly_forecast_oldstyle_service = ExtractHourlyForecastOldstyleService()
39
+ self.extract_daily_forecast_service = ExtractDailyForecastService()
40
+ self.extract_daily_forecast_oldstyle_service = ExtractDailyForecastOldstyleService()
41
+ self.retrieve_forecast_from_cache_service = RetrieveForecastFromCacheService(self.forecast_repository)
42
+ self.save_forecast_to_cache_service = SaveForecastToCacheService(self.forecast_repository)
43
+ self.retrieve_statistics_service = RetrieveStatisticsService(self.forecast_repository)
44
+ self.weather_forecast_uc = WeatherForecastUC(
45
+ self.search_location_service,
46
+ self.read_weather_service,
47
+ self.extract_current_conditions_service,
48
+ self.extract_today_details_service,
49
+ self.extract_aqi_service,
50
+ self.extract_health_activities_service,
51
+ self.extract_hourly_forecast_service,
52
+ self.extract_hourly_forecast_oldstyle_service,
53
+ self.extract_daily_forecast_service,
54
+ self.extract_daily_forecast_oldstyle_service,
55
+ self.retrieve_forecast_from_cache_service,
56
+ self.save_forecast_to_cache_service,
57
+ )
58
+ self.statistics_uc = StatisticsUC(self.retrieve_statistics_service)
59
+
60
+ def _define_controller(self, params: Params):
61
+ if params.cache_statistics:
62
+ from weathergrabber.adapter.tty.statistics_tty import StatisticsTTY
63
+ self.controller = StatisticsTTY(self.statistics_uc)
64
+
65
+ elif params.output_format == OutputEnum.CONSOLE:
66
+ from weathergrabber.adapter.tty.console_tty import ConsoleTTY
67
+ self.controller = ConsoleTTY(self.weather_forecast_uc)
68
+
69
+ elif params.output_format == OutputEnum.JSON:
70
+ from weathergrabber.adapter.tty.json_tty import JsonTTY
71
+ self.controller = JsonTTY(self.weather_forecast_uc)
72
+
73
+ elif params.output_format == OutputEnum.WAYBAR:
74
+ from weathergrabber.adapter.tty.waybar_tty import WaybarTTY
75
+ self.controller = WaybarTTY(self.weather_forecast_uc)
76
+ else:
77
+ self.logger.error(f"Unsupported output")
78
+ raise ValueError(f"Unsupported output")
79
+
80
+ def __init__(self, params: Params):
81
+ self.logger = logging.getLogger(__name__)
82
+ self._beans()
83
+ self._define_controller(params)
84
+ self.logger.info("Starting WeatherGrabber Application")
85
+ if params.keep_open:
86
+ self.logger.info("Keep open mode enabled, the application will refresh every 5 minutes")
87
+ while True:
88
+ self.controller.execute(params)
89
+ sleep(1) # Sleep for 5 minutes
90
+ else:
91
+ self.controller.execute(params)
92
+ self.logger.info("WeatherGrabber Application finished")
weathergrabber/cli.py CHANGED
@@ -8,9 +8,11 @@ def main_cli():
8
8
  parser.add_argument("location_name", type=str, nargs='?', help="Location (city name, zip code, etc.)")
9
9
  parser.add_argument("--location-id", "-l", type=str, help="64-character-hex code for location obtained from weather.com")
10
10
  parser.add_argument("--lang", "-L", type=str, help="Language (pt-BR, fr-FR, etc.), If not set, uses the machine one.")
11
- parser.add_argument("--output", "-o", type=str, choices=['console','json','waybar'], default='console', help="Output format. console, json or waybar")
11
+ parser.add_argument("--output", "-o", type=str, choices=['console','json','waybar','statistics'], default='console', help="Output format. console, json, waybar or statistics")
12
12
  parser.add_argument("--keep-open", "-k",action='store_true', default=False, help="Keep open and refreshing every 5 minutes instead of exiting after execution. Does only makes sense for --output=console")
13
13
  parser.add_argument("--icons", "-i", type=str, choices=['fa','emoji'], default='emoji', help="Icon set. 'fa' for Font-Awesome, or 'emoji'")
14
+ parser.add_argument("--force-cache", "-f", action='store_true', default=False, help="Force retrieve data from cache instead of fetching new data")
15
+ parser.add_argument("--cache-statistics", "-s", action='store_true', default=False, help="Show cache statistics and exit")
14
16
  parser.add_argument("--version", "-v", action='version', version=f'Weathergrabber {weathergrabber.get_version()}', help="Show version and exit")
15
17
  parser.add_argument(
16
18
  "--log",
@@ -33,5 +35,7 @@ def main_cli():
33
35
  lang=lang,
34
36
  output=args.output,
35
37
  keep_open=args.keep_open,
36
- icons=args.icons
38
+ icons=args.icons,
39
+ force_cache=args.force_cache,
40
+ cache_statistics=args.cache_statistics,
37
41
  )
weathergrabber/core.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from weathergrabber.weathergrabber_application import WeatherGrabberApplication
2
+ from weathergrabber.application.weathergrabber_application import WeatherGrabberApplication
3
3
  from weathergrabber.domain.adapter.params import Params
4
4
  from weathergrabber.domain.adapter.output_enum import OutputEnum
5
5
  from weathergrabber.domain.adapter.icon_enum import IconEnum
@@ -9,7 +9,7 @@ logging.basicConfig(
9
9
  format="[%(levelname)s] %(message)s"
10
10
  )
11
11
 
12
- def main(log_level: str, location_name: str, location_id: str, lang: str, output: str, keep_open: bool, icons: str):
12
+ def main(log_level: str, location_name: str, location_id: str, lang: str, output: str, keep_open: bool, icons: str, force_cache: bool, cache_statistics: bool):
13
13
  logging.getLogger().setLevel(log_level.upper())
14
14
 
15
15
  logging.info(f"Log level set to {log_level}")
@@ -19,13 +19,17 @@ def main(log_level: str, location_name: str, location_id: str, lang: str, output
19
19
  logging.info(f"Output: {output}")
20
20
  logging.info(f"Keep open: {keep_open}")
21
21
  logging.info(f"Icons: {icons}")
22
+ logging.info(f"Force cache: {force_cache}")
23
+ logging.info(f"Cache statistics: {cache_statistics}")
22
24
 
23
25
  params = Params(
24
26
  location=Params.Location(search_name=location_name, id=location_id),
25
27
  language=lang if lang else "en-US",
26
28
  output_format= OutputEnum(output),
27
29
  keep_open=keep_open,
28
- icons=IconEnum(icons)
30
+ icons=IconEnum(icons),
31
+ force_cache=force_cache,
32
+ cache_statistics=cache_statistics,
29
33
  )
30
34
 
31
35
  app = WeatherGrabberApplication(params)
@@ -0,0 +1,25 @@
1
+
2
+ from weathergrabber.domain.entities.air_quality_index import AirQualityIndex
3
+
4
+
5
+ def air_quality_index_to_dict(aqi: AirQualityIndex) -> dict:
6
+ from weathergrabber.domain.adapter.mappers.color_mapper import color_to_dict
7
+ return {
8
+ "title": aqi.title,
9
+ "value": aqi.value,
10
+ "category": aqi.category,
11
+ "description": aqi.description,
12
+ "acronym": aqi.acronym,
13
+ "color": color_to_dict(aqi.color) if aqi.color else None,
14
+ }
15
+
16
+ def dict_to_air_quality_index(data: dict) -> AirQualityIndex:
17
+ from weathergrabber.domain.adapter.mappers.color_mapper import dict_to_color
18
+ return AirQualityIndex(
19
+ title=data["title"],
20
+ value=data["value"],
21
+ category=data["category"],
22
+ description=data["description"],
23
+ acronym=data["acronym"],
24
+ color=dict_to_color(data["color"]) if data.get("color") else None,
25
+ )
@@ -0,0 +1,15 @@
1
+ from weathergrabber.domain.entities.city_location import CityLocation
2
+
3
+ def city_location_to_dict(loc: CityLocation) -> dict:
4
+ return {
5
+ "city": loc.city,
6
+ "state_province": loc.state_province,
7
+ "country": loc.country,
8
+ }
9
+
10
+ def dict_to_city_location(data: dict) -> CityLocation:
11
+ return CityLocation(
12
+ city=data.get("city"),
13
+ state_province=data.get("state_province"),
14
+ country=data.get("country"),
15
+ )
@@ -0,0 +1,17 @@
1
+ from weathergrabber.domain.entities.color import Color
2
+
3
+ def color_to_dict(color: Color) -> dict:
4
+ return {
5
+ "red": color.red,
6
+ "green": color.green,
7
+ "blue": color.blue,
8
+ "hex": color.hex,
9
+ "rgb": color.rgb,
10
+ }
11
+
12
+ def dict_to_color(data: dict) -> Color:
13
+ return Color(
14
+ red=data["red"],
15
+ green=data["green"],
16
+ blue=data["blue"],
17
+ )
@@ -0,0 +1,32 @@
1
+ from weathergrabber.domain.entities.current_conditions import CurrentConditions
2
+
3
+
4
+
5
+ def current_conditions_to_dict(cc: CurrentConditions) -> dict:
6
+ from weathergrabber.domain.adapter.mappers.city_location_mapper import city_location_to_dict
7
+ from weathergrabber.domain.adapter.mappers.timestamp_mapper import timestamp_to_dict
8
+ from weathergrabber.domain.adapter.mappers.weather_icon_enum_mapper import weather_icon_enum_to_dict
9
+ from weathergrabber.domain.adapter.mappers.day_night_mapper import day_night_to_dict
10
+ return {
11
+ "location": city_location_to_dict(cc.location) if cc.location else None,
12
+ "timestamp": timestamp_to_dict(cc.timestamp) if cc.timestamp else None,
13
+ "temperature": cc.temperature,
14
+ "icon": weather_icon_enum_to_dict(cc.icon) if cc.icon else None,
15
+ "summary": cc.summary,
16
+ "day_night": day_night_to_dict(cc.day_night) if cc.day_night else None,
17
+ }
18
+
19
+ def dict_to_current_conditions(data: dict) -> CurrentConditions:
20
+ from weathergrabber.domain.adapter.mappers.city_location_mapper import dict_to_city_location
21
+ from weathergrabber.domain.adapter.mappers.timestamp_mapper import dict_to_timestamp
22
+ from weathergrabber.domain.adapter.mappers.weather_icon_enum_mapper import dict_to_weather_icon_enum
23
+ from weathergrabber.domain.adapter.mappers.day_night_mapper import dict_to_day_night
24
+
25
+ return CurrentConditions(
26
+ location=dict_to_city_location(data["location"]) if data.get("location") else None,
27
+ timestamp=dict_to_timestamp(data["timestamp"]) if data.get("timestamp") else None,
28
+ temperature=data.get("temperature"),
29
+ icon=dict_to_weather_icon_enum(data["icon"]) if data.get("icon") else None,
30
+ summary=data.get("summary"),
31
+ day_night=dict_to_day_night(data["day_night"]) if data.get("day_night") else None,
32
+ )
@@ -0,0 +1,30 @@
1
+ from weathergrabber.domain.entities.daily_predictions import DailyPredictions
2
+
3
+
4
+ def daily_predictions_to_dict(dp: DailyPredictions) -> dict:
5
+ from weathergrabber.domain.adapter.mappers.temperature_high_low_mapper import temperature_high_low_to_dict
6
+ from weathergrabber.domain.adapter.mappers.weather_icon_enum_mapper import weather_icon_enum_to_dict
7
+ from weathergrabber.domain.adapter.mappers.precipitation_mapper import precipitation_to_dict
8
+ from weathergrabber.domain.adapter.mappers.moon_phase_mapper import moon_phase_to_dict
9
+ return {
10
+ "title": dp.title,
11
+ "high_low": temperature_high_low_to_dict(dp.high_low) if dp.high_low else None,
12
+ "icon": weather_icon_enum_to_dict(dp.icon) if dp.icon else None,
13
+ "summary": dp.summary,
14
+ "precipitation": precipitation_to_dict(dp.precipitation) if dp.precipitation else None,
15
+ "moon_phase": moon_phase_to_dict(dp.moon_phase) if dp.moon_phase else None,
16
+ }
17
+
18
+ def dict_to_daily_predictions(data: dict) -> DailyPredictions:
19
+ from weathergrabber.domain.adapter.mappers.temperature_high_low_mapper import dict_to_temperature_high_low
20
+ from weathergrabber.domain.adapter.mappers.weather_icon_enum_mapper import dict_to_weather_icon_enum
21
+ from weathergrabber.domain.adapter.mappers.precipitation_mapper import dict_to_precipitation
22
+ from weathergrabber.domain.adapter.mappers.moon_phase_mapper import dict_to_moon_phase
23
+ return DailyPredictions(
24
+ title=data.get("title"),
25
+ high_low=dict_to_temperature_high_low(data["high_low"]) if data.get("high_low") else None,
26
+ icon=dict_to_weather_icon_enum(data["icon"]) if data.get("icon") else None,
27
+ summary=data.get("summary"),
28
+ precipitation=dict_to_precipitation(data["precipitation"]) if data.get("precipitation") else None,
29
+ moon_phase=dict_to_moon_phase(data["moon_phase"]) if data.get("moon_phase") else None,
30
+ )
@@ -0,0 +1,23 @@
1
+ from weathergrabber.domain.entities.day_night import DayNight
2
+
3
+ def day_night_to_dict(dn: DayNight) -> dict:
4
+ def temp_to_dict(temp):
5
+ return {
6
+ "label": temp.label,
7
+ "value": temp.value,
8
+ } if temp else None
9
+ return {
10
+ "day": temp_to_dict(dn.day) if dn.day else None,
11
+ "night": temp_to_dict(dn.night) if dn.night else None,
12
+ }
13
+
14
+ def dict_to_day_night(data: dict) -> DayNight:
15
+ def dict_to_temp(data):
16
+ return DayNight.Temperature(
17
+ label=data.get("label"),
18
+ value=data.get("value"),
19
+ ) if data else None
20
+ return DayNight(
21
+ day=dict_to_temp(data["day"]) if data.get("day") else None,
22
+ night=dict_to_temp(data["night"]) if data.get("night") else None,
23
+ )
@@ -0,0 +1,38 @@
1
+ from weathergrabber.domain.entities.forecast import Forecast
2
+
3
+ def forecast_to_dict(forecast: Forecast) -> dict:
4
+ from weathergrabber.domain.adapter.mappers.search_mapper import search_to_dict
5
+ from weathergrabber.domain.adapter.mappers.current_conditions_mapper import current_conditions_to_dict
6
+ from weathergrabber.domain.adapter.mappers.today_details_mapper import today_details_to_dict
7
+ from weathergrabber.domain.adapter.mappers.air_quality_index_mapper import air_quality_index_to_dict
8
+ from weathergrabber.domain.adapter.mappers.health_activities_mapper import health_activities_to_dict
9
+ from weathergrabber.domain.adapter.mappers.hourly_predictions_mapper import hourly_predictions_to_dict
10
+ from weathergrabber.domain.adapter.mappers.daily_predictions_mapper import daily_predictions_to_dict
11
+
12
+ return {
13
+ "search": search_to_dict(forecast.search) if forecast.search else None,
14
+ "current_conditions": current_conditions_to_dict(forecast.current_conditions) if forecast.current_conditions else None,
15
+ "today_details": today_details_to_dict(forecast.today_details) if forecast.today_details else None,
16
+ "air_quality_index": air_quality_index_to_dict(forecast.air_quality_index) if forecast.air_quality_index else None,
17
+ "health_activities": health_activities_to_dict(forecast.health_activities) if forecast.health_activities else None,
18
+ "hourly_predictions": [hourly_predictions_to_dict(h) for h in forecast.hourly_predictions],
19
+ "daily_predictions": [daily_predictions_to_dict(d) for d in forecast.daily_predictions],
20
+ }
21
+ def dict_to_forecast(data: dict) -> Forecast:
22
+ from weathergrabber.domain.adapter.mappers.search_mapper import dict_to_search
23
+ from weathergrabber.domain.adapter.mappers.current_conditions_mapper import dict_to_current_conditions
24
+ from weathergrabber.domain.adapter.mappers.today_details_mapper import dict_to_today_details
25
+ from weathergrabber.domain.adapter.mappers.air_quality_index_mapper import dict_to_air_quality_index
26
+ from weathergrabber.domain.adapter.mappers.health_activities_mapper import dict_to_health_activities
27
+ from weathergrabber.domain.adapter.mappers.hourly_predictions_mapper import dict_to_hourly_predictions
28
+ from weathergrabber.domain.adapter.mappers.daily_predictions_mapper import dict_to_daily_predictions
29
+
30
+ return Forecast(
31
+ search=dict_to_search(data["search"]) if data.get("search") else None,
32
+ current_conditions=dict_to_current_conditions(data["current_conditions"]) if data.get("current_conditions") else None,
33
+ today_details=dict_to_today_details(data["today_details"]) if data.get("today_details") else None,
34
+ air_quality_index=dict_to_air_quality_index(data["air_quality_index"]) if data.get("air_quality_index") else None,
35
+ health_activities=dict_to_health_activities(data["health_activities"]) if data.get("health_activities") else None,
36
+ hourly_predictions=[dict_to_hourly_predictions(h) for h in data.get("hourly_predictions", [])],
37
+ daily_predictions=[dict_to_daily_predictions(d) for d in data.get("daily_predictions", [])],
38
+ )
@@ -0,0 +1,15 @@
1
+ from weathergrabber.domain.entities.health_activities import HealthActivities
2
+
3
+ def health_activities_to_dict(ha: HealthActivities) -> dict:
4
+ return {
5
+ "category_name": ha.category_name,
6
+ "title": ha.title,
7
+ "description": ha.description,
8
+ }
9
+
10
+ def dict_to_health_activities(data: dict) -> HealthActivities:
11
+ return HealthActivities(
12
+ category_name=data["category_name"],
13
+ title=data["title"],
14
+ description=data["description"],
15
+ )
@@ -0,0 +1,38 @@
1
+ from weathergrabber.domain.entities.hourly_predictions import HourlyPredictions
2
+
3
+
4
+ def hourly_predictions_to_dict(hp: HourlyPredictions) -> dict:
5
+ from weathergrabber.domain.adapter.mappers.weather_icon_enum_mapper import weather_icon_enum_to_dict
6
+ from weathergrabber.domain.adapter.mappers.precipitation_mapper import precipitation_to_dict
7
+ from weathergrabber.domain.adapter.mappers.wind_mapper import wind_to_dict
8
+ from weathergrabber.domain.adapter.mappers.uv_index_mapper import uv_index_to_dict
9
+ return {
10
+ "title": hp.title,
11
+ "temperature": hp.temperature,
12
+ "icon": weather_icon_enum_to_dict(hp.icon) if hp.icon else None,
13
+ "summary": hp.summary,
14
+ "precipitation": precipitation_to_dict(hp.precipitation) if hp.precipitation else None,
15
+ "wind": wind_to_dict(hp.wind) if hp.wind else None,
16
+ "feels_like": hp.feels_like,
17
+ "humidity": hp.humidity,
18
+ "uv_index": uv_index_to_dict(hp.uv_index) if hp.uv_index else None,
19
+ "cloud_cover": hp.cloud_cover,
20
+ }
21
+
22
+ def dict_to_hourly_predictions(data: dict) -> HourlyPredictions:
23
+ from weathergrabber.domain.adapter.mappers.weather_icon_enum_mapper import dict_to_weather_icon_enum
24
+ from weathergrabber.domain.adapter.mappers.precipitation_mapper import dict_to_precipitation
25
+ from weathergrabber.domain.adapter.mappers.wind_mapper import dict_to_wind
26
+ from weathergrabber.domain.adapter.mappers.uv_index_mapper import dict_to_uv_index
27
+ return HourlyPredictions(
28
+ title=data["title"],
29
+ temperature=data["temperature"],
30
+ icon=dict_to_weather_icon_enum(data["icon"]) if data.get("icon") else None,
31
+ summary=data["summary"],
32
+ precipitation=dict_to_precipitation(data["precipitation"]) if data.get("precipitation") else None,
33
+ wind=dict_to_wind(data["wind"]) if data.get("wind") else None,
34
+ feels_like=data["feels_like"],
35
+ humidity=data["humidity"],
36
+ uv_index=dict_to_uv_index(data["uv_index"]) if data.get("uv_index") else None,
37
+ cloud_cover=data["cloud_cover"],
38
+ )
@@ -0,0 +1,13 @@
1
+ from weathergrabber.domain.entities.label_value import LabelValue
2
+
3
+ def label_value_to_dict(lv: LabelValue) -> dict:
4
+ return {
5
+ "label": lv.label,
6
+ "value": lv.value,
7
+ }
8
+
9
+ def dict_to_label_value(data: dict) -> LabelValue:
10
+ return LabelValue(
11
+ label=data["label"],
12
+ value=data["value"],
13
+ )
@@ -0,0 +1,16 @@
1
+ from weathergrabber.domain.entities.moon_phase import MoonPhase
2
+ from weathergrabber.domain.entities.moon_phase_enum import MoonPhaseEnum
3
+
4
+ def moon_phase_to_dict(mp: MoonPhase) -> dict:
5
+ return {
6
+ "icon": mp.icon.name if mp.icon else None,
7
+ "phase": mp.phase,
8
+ "label": mp.label,
9
+ }
10
+
11
+ def dict_to_moon_phase(data: dict) -> MoonPhase:
12
+ return MoonPhase(
13
+ icon=MoonPhaseEnum.from_name(data["icon"]) if data.get("icon") else None,
14
+ phase=data.get("phase"),
15
+ label=data.get("label"),
16
+ )
@@ -0,0 +1,13 @@
1
+ from weathergrabber.domain.entities.precipitation import Precipitation
2
+
3
+ def precipitation_to_dict(p: Precipitation) -> dict:
4
+ return {
5
+ "percentage": p.percentage,
6
+ "amount": p.amount,
7
+ }
8
+
9
+ def dict_to_precipitation(data: dict) -> Precipitation:
10
+ return Precipitation(
11
+ percentage=data["percentage"],
12
+ amount=data["amount"],
13
+ )
@@ -0,0 +1,13 @@
1
+ from weathergrabber.domain.entities.search import Search
2
+
3
+ def search_to_dict(search: Search) -> dict:
4
+ return {
5
+ "id": search.id,
6
+ "search_name": search.search_name,
7
+ }
8
+
9
+ def dict_to_search(data: dict) -> Search:
10
+ return Search(
11
+ id=data["id"],
12
+ search_name=data["search_name"],
13
+ )
@@ -0,0 +1,25 @@
1
+ from weathergrabber.domain.entities.statistics import Statistics
2
+
3
+
4
+ def statistics_to_dict(statistics: Statistics) -> dict:
5
+ """Convert a Statistics entity to a dictionary."""
6
+ data = {
7
+ "total_forecasts": statistics.total_forecasts,
8
+ "unique_locations": statistics.unique_locations,
9
+ "unique_search_names": statistics.unique_search_names,
10
+ "database_path": statistics.database_path,
11
+ }
12
+ if statistics.error:
13
+ data["error"] = statistics.error
14
+ return data
15
+
16
+
17
+ def dict_to_statistics(data: dict) -> Statistics:
18
+ """Convert a dictionary to a Statistics entity."""
19
+ return Statistics(
20
+ total_forecasts=data.get("total_forecasts", 0),
21
+ unique_locations=data.get("unique_locations", 0),
22
+ unique_search_names=data.get("unique_search_names", 0),
23
+ database_path=data.get("database_path", ""),
24
+ error=data.get("error")
25
+ )
@@ -0,0 +1,23 @@
1
+ from weathergrabber.domain.entities.sunrise_sunset import SunriseSunset
2
+
3
+ def sunrise_sunset_to_dict(ss: SunriseSunset) -> dict:
4
+ from weathergrabber.domain.adapter.mappers.weather_icon_enum_mapper import weather_icon_enum_to_dict
5
+ def icon_value_to_dict(iv: SunriseSunset.IconValue):
6
+ return {
7
+ "icon": weather_icon_enum_to_dict(iv.icon) if iv.icon else None,
8
+ "value": iv.value,
9
+ } if iv else None
10
+ return {
11
+ "sunrise": icon_value_to_dict(ss.sunrise) if ss.sunrise else None,
12
+ "sunset": icon_value_to_dict(ss.sunset) if ss.sunset else None,
13
+ }
14
+
15
+ def dict_to_sunrise_sunset(data: dict) -> SunriseSunset:
16
+
17
+ if data is None:
18
+ return None
19
+
20
+ return SunriseSunset(
21
+ sunrise=data.get("sunrise").get("value") if data.get("sunrise") and data.get("sunrise").get("value") else None,
22
+ sunset=data.get("sunset").get("value") if data.get("sunset") and data.get("sunset").get("value") else None
23
+ )
@@ -0,0 +1,15 @@
1
+ from weathergrabber.domain.entities.temperature_hight_low import TemperatureHighLow
2
+
3
+ def temperature_high_low_to_dict(thl: TemperatureHighLow) -> dict:
4
+ return {
5
+ "high": thl.high,
6
+ "low": thl.low,
7
+ "label": thl.label,
8
+ }
9
+
10
+ def dict_to_temperature_high_low(data: dict) -> TemperatureHighLow:
11
+ return TemperatureHighLow(
12
+ high=data.get("high"),
13
+ low=data.get("low"),
14
+ label=data.get("label"),
15
+ )
@@ -0,0 +1,15 @@
1
+ from weathergrabber.domain.entities.timestamp import Timestamp
2
+
3
+ def timestamp_to_dict(ts: Timestamp) -> dict:
4
+ return {
5
+ "time": ts.time,
6
+ "gmt": ts.gmt,
7
+ "text": ts.text,
8
+ }
9
+
10
+ def dict_to_timestamp(data: dict) -> Timestamp:
11
+ return Timestamp(
12
+ time=data.get("time"),
13
+ gmt=data.get("gmt"),
14
+ text=data.get("text"),
15
+ )
@@ -0,0 +1,42 @@
1
+
2
+ from weathergrabber.domain.entities.today_details import TodayDetails
3
+
4
+ def today_details_to_dict(td: TodayDetails) -> dict:
5
+ from weathergrabber.domain.adapter.mappers.label_value_mapper import label_value_to_dict
6
+ from weathergrabber.domain.adapter.mappers.sunrise_sunset_mapper import sunrise_sunset_to_dict
7
+ from weathergrabber.domain.adapter.mappers.temperature_high_low_mapper import temperature_high_low_to_dict
8
+ from weathergrabber.domain.adapter.mappers.uv_index_mapper import uv_index_to_dict
9
+ from weathergrabber.domain.adapter.mappers.moon_phase_mapper import moon_phase_to_dict
10
+
11
+ return {
12
+ "feelslike": label_value_to_dict(td.feelslike) if td.feelslike else None,
13
+ "sunrise_sunset": sunrise_sunset_to_dict(td.sunrise_sunset) if td.sunrise_sunset else None,
14
+ "high_low": temperature_high_low_to_dict(td.high_low) if td.high_low else None,
15
+ "wind": label_value_to_dict(td.wind) if td.wind else None,
16
+ "humidity": label_value_to_dict(td.humidity) if td.humidity else None,
17
+ "dew_point": label_value_to_dict(td.dew_point) if td.dew_point else None,
18
+ "pressure": label_value_to_dict(td.pressure) if td.pressure else None,
19
+ "uv_index": uv_index_to_dict(td.uv_index) if td.uv_index else None,
20
+ "visibility": label_value_to_dict(td.visibility) if td.visibility else None,
21
+ "moon_phase": moon_phase_to_dict(td.moon_phase) if td.moon_phase else None,
22
+ }
23
+
24
+ def dict_to_today_details(data: dict) -> TodayDetails:
25
+ from weathergrabber.domain.adapter.mappers.label_value_mapper import dict_to_label_value
26
+ from weathergrabber.domain.adapter.mappers.sunrise_sunset_mapper import dict_to_sunrise_sunset
27
+ from weathergrabber.domain.adapter.mappers.temperature_high_low_mapper import dict_to_temperature_high_low
28
+ from weathergrabber.domain.adapter.mappers.uv_index_mapper import dict_to_uv_index
29
+ from weathergrabber.domain.adapter.mappers.moon_phase_mapper import dict_to_moon_phase
30
+
31
+ return TodayDetails(
32
+ feelslike=dict_to_label_value(data["feelslike"]) if data.get("feelslike") else None,
33
+ sunrise_sunset=dict_to_sunrise_sunset(data["sunrise_sunset"]) if data.get("sunrise_sunset") else None,
34
+ high_low=dict_to_temperature_high_low(data["high_low"]) if data.get("high_low") else None,
35
+ wind=dict_to_label_value(data["wind"]) if data.get("wind") else None,
36
+ humidity=dict_to_label_value(data["humidity"]) if data.get("humidity") else None,
37
+ dew_point=dict_to_label_value(data["dew_point"]) if data.get("dew_point") else None,
38
+ pressure=dict_to_label_value(data["pressure"]) if data.get("pressure") else None,
39
+ uv_index=dict_to_uv_index(data["uv_index"]) if data.get("uv_index") else None,
40
+ visibility=dict_to_label_value(data["visibility"]) if data.get("visibility") else None,
41
+ moon_phase=dict_to_moon_phase(data["moon_phase"]) if data.get("moon_phase") else None,
42
+ )
@@ -0,0 +1,17 @@
1
+ from weathergrabber.domain.entities.uv_index import UVIndex
2
+
3
+ def uv_index_to_dict(uv: UVIndex) -> dict:
4
+ return {
5
+ "string_value": uv.string_value,
6
+ "index": uv.index,
7
+ "of": uv.of,
8
+ "label": uv.label,
9
+ }
10
+
11
+ def dict_to_uv_index(data: dict) -> UVIndex:
12
+ return UVIndex(
13
+ string_value=data.get("string_value"),
14
+ index=data.get("index"),
15
+ of=data.get("of"),
16
+ label=data.get("label"),
17
+ )
@@ -0,0 +1,11 @@
1
+ from weathergrabber.domain.entities.weather_icon_enum import WeatherIconEnum
2
+
3
+ def weather_icon_enum_to_dict(icon: WeatherIconEnum) -> dict:
4
+ return {
5
+ "name": icon.name,
6
+ "fa_icon": icon.fa_icon,
7
+ "emoji_icon": icon.emoji_icon,
8
+ }
9
+
10
+ def dict_to_weather_icon_enum(data: dict) -> WeatherIconEnum:
11
+ return WeatherIconEnum.from_name(data.get("name"))
@@ -0,0 +1,16 @@
1
+ def wind_to_dict(wind):
2
+ if wind is None:
3
+ return None
4
+ return {
5
+ "direction": wind.direction,
6
+ "speed": wind.speed
7
+ }
8
+
9
+ def dict_to_wind(data: dict):
10
+ if data is None:
11
+ return None
12
+ from weathergrabber.domain.entities.wind import Wind
13
+ return Wind(
14
+ direction=data["direction"],
15
+ speed=data["speed"]
16
+ )
@@ -3,4 +3,4 @@ from enum import StrEnum
3
3
  class OutputEnum(StrEnum):
4
4
  CONSOLE = "console"
5
5
  JSON = "json"
6
- WAYBAR = "waybar"
6
+ WAYBAR = "waybar"