weathergrabber 0.0.1__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 (70) hide show
  1. weathergrabber/__init__.py +0 -0
  2. weathergrabber/adapter/client/weather_api.py +27 -0
  3. weathergrabber/adapter/client/weather_search_api.py +50 -0
  4. weathergrabber/adapter/tty/console_tty.py +102 -0
  5. weathergrabber/adapter/tty/json_tty.py +19 -0
  6. weathergrabber/adapter/tty/waybar_tty.py +110 -0
  7. weathergrabber/cli.py +37 -0
  8. weathergrabber/core.py +31 -0
  9. weathergrabber/domain/adapter/icon_enum.py +5 -0
  10. weathergrabber/domain/adapter/mapper/air_quality_index_mapper.py +13 -0
  11. weathergrabber/domain/adapter/mapper/city_location_mapper.py +8 -0
  12. weathergrabber/domain/adapter/mapper/color_mapper.py +10 -0
  13. weathergrabber/domain/adapter/mapper/current_conditions_mapper.py +16 -0
  14. weathergrabber/domain/adapter/mapper/daily_predictions_mapper.py +15 -0
  15. weathergrabber/domain/adapter/mapper/day_night_mapper.py +12 -0
  16. weathergrabber/domain/adapter/mapper/forecast_mapper.py +20 -0
  17. weathergrabber/domain/adapter/mapper/health_activities_mapper.py +8 -0
  18. weathergrabber/domain/adapter/mapper/hourly_predictions_mapper.py +19 -0
  19. weathergrabber/domain/adapter/mapper/label_value_mapper.py +7 -0
  20. weathergrabber/domain/adapter/mapper/moon_phase_mapper.py +9 -0
  21. weathergrabber/domain/adapter/mapper/precipitation_mapper.py +7 -0
  22. weathergrabber/domain/adapter/mapper/search_mapper.py +7 -0
  23. weathergrabber/domain/adapter/mapper/sunrise_sunset_mapper.py +13 -0
  24. weathergrabber/domain/adapter/mapper/temperature_high_low_mapper.py +8 -0
  25. weathergrabber/domain/adapter/mapper/timestamp_mapper.py +8 -0
  26. weathergrabber/domain/adapter/mapper/today_details_mapper.py +21 -0
  27. weathergrabber/domain/adapter/mapper/uv_index_mapper.py +9 -0
  28. weathergrabber/domain/adapter/mapper/weather_icon_enum_mapper.py +8 -0
  29. weathergrabber/domain/adapter/mapper/wind_mapper.py +7 -0
  30. weathergrabber/domain/adapter/output_enum.py +6 -0
  31. weathergrabber/domain/adapter/params.py +58 -0
  32. weathergrabber/domain/air_quality_index.py +78 -0
  33. weathergrabber/domain/city_location.py +37 -0
  34. weathergrabber/domain/color.py +43 -0
  35. weathergrabber/domain/current_conditions.py +53 -0
  36. weathergrabber/domain/daily_predictions.py +58 -0
  37. weathergrabber/domain/day_night.py +50 -0
  38. weathergrabber/domain/forecast.py +68 -0
  39. weathergrabber/domain/health_activities.py +39 -0
  40. weathergrabber/domain/hourly_predictions.py +76 -0
  41. weathergrabber/domain/label_value.py +19 -0
  42. weathergrabber/domain/moon_phase.py +22 -0
  43. weathergrabber/domain/moon_phase_enum.py +65 -0
  44. weathergrabber/domain/precipitation.py +20 -0
  45. weathergrabber/domain/search.py +15 -0
  46. weathergrabber/domain/sunrise_sunset.py +40 -0
  47. weathergrabber/domain/temperature_hight_low.py +32 -0
  48. weathergrabber/domain/timestamp.py +39 -0
  49. weathergrabber/domain/today_details.py +79 -0
  50. weathergrabber/domain/uv_index.py +43 -0
  51. weathergrabber/domain/weather_icon_enum.py +58 -0
  52. weathergrabber/domain/wind.py +28 -0
  53. weathergrabber/service/extract_aqi_service.py +30 -0
  54. weathergrabber/service/extract_current_conditions_service.py +47 -0
  55. weathergrabber/service/extract_daily_forecast_oldstyle_service.py +51 -0
  56. weathergrabber/service/extract_daily_forecast_service.py +56 -0
  57. weathergrabber/service/extract_health_activities_service.py +25 -0
  58. weathergrabber/service/extract_hourly_forecast_oldstyle_service.py +50 -0
  59. weathergrabber/service/extract_hourly_forecast_service.py +64 -0
  60. weathergrabber/service/extract_temperature_service.py +18 -0
  61. weathergrabber/service/extract_today_details_service.py +85 -0
  62. weathergrabber/service/read_weather_service.py +23 -0
  63. weathergrabber/service/search_location_service.py +35 -0
  64. weathergrabber/usecase/use_case.py +85 -0
  65. weathergrabber/weathergrabber_application.py +78 -0
  66. weathergrabber-0.0.1.dist-info/METADATA +176 -0
  67. weathergrabber-0.0.1.dist-info/RECORD +70 -0
  68. weathergrabber-0.0.1.dist-info/WHEEL +5 -0
  69. weathergrabber-0.0.1.dist-info/entry_points.txt +2 -0
  70. weathergrabber-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,6 @@
1
+ from enum import StrEnum
2
+
3
+ class OutputEnum(StrEnum):
4
+ CONSOLE = "console"
5
+ JSON = "json"
6
+ WAYBAR = "waybar"
@@ -0,0 +1,58 @@
1
+ from .output_enum import OutputEnum
2
+ from .icon_enum import IconEnum
3
+ from typing import Optional
4
+
5
+ class Params:
6
+ class Location:
7
+ def __init__(self, search_name: str = None, id: str = None):
8
+ self._search_name = search_name
9
+ self._id = id
10
+
11
+ @property
12
+ def search_name(self) -> str | None:
13
+ return self._search_name
14
+
15
+ @property
16
+ def id(self) -> str | None:
17
+ return self._id
18
+
19
+ def __str__(self):
20
+ return f"Location(search_name={self.search_name}, id={self.id})"
21
+
22
+ def __init__(
23
+ self,
24
+ location: Optional["Params.Location"] = None,
25
+ language: str = "en-US",
26
+ output_format: OutputEnum = OutputEnum.CONSOLE,
27
+ keep_open: bool = False,
28
+ icons: IconEnum = IconEnum.EMOJI
29
+ ):
30
+ self._location = location
31
+ self._language = language
32
+ self._output_format = output_format
33
+ self._keep_open = keep_open
34
+ self._icons = icons
35
+
36
+ @property
37
+ def location(self) -> Optional["Params.Location"]:
38
+ return self._location
39
+
40
+ @property
41
+ def language(self) -> str:
42
+ return self._language
43
+
44
+ @property
45
+ def output_format(self) -> OutputEnum:
46
+ return self._output_format
47
+
48
+ @property
49
+ def keep_open(self) -> bool:
50
+ return self._keep_open
51
+
52
+ @property
53
+ def icons(self) -> IconEnum:
54
+ return self._icons
55
+
56
+ def __str__(self):
57
+ return f"Params(location={self.location}, language={self.language}, output_format={self.output_format}, keep_open={self.keep_open}, icons={self.icons})"
58
+
@@ -0,0 +1,78 @@
1
+ from .color import Color
2
+ from typing import Optional
3
+
4
+ class AirQualityIndex:
5
+ def __init__(self,
6
+ title: str,
7
+ value: int,
8
+ category: Optional[str] = None,
9
+ description: Optional[str] = None,
10
+ acronym: Optional[str] = None,
11
+ color: Optional[Color] = None
12
+ ):
13
+ self._title = title
14
+ self._value = value
15
+ self._category = category
16
+ self._description = description
17
+ self._acronym = acronym
18
+ self._color = color
19
+
20
+ @property
21
+ def title(self) -> str:
22
+ return self._title
23
+
24
+ @property
25
+ def value(self) -> int:
26
+ return self._value
27
+
28
+ @property
29
+ def category(self) -> str | None:
30
+ return self._category
31
+
32
+ @property
33
+ def description(self) -> str | None:
34
+ return self._description
35
+
36
+ @property
37
+ def acronym(self) -> str | None:
38
+ return self._acronym
39
+
40
+ @property
41
+ def color(self) -> Color | None:
42
+ return self._color
43
+
44
+ def __str__(self) -> str:
45
+ return f"Title: {self.title}. AQI: {self.value}, Category: {self.category}, Description: {self.description}, Acronym: {self.acronym}, Color: {self.color}"
46
+
47
+ def __repr__(self) -> str:
48
+ return f"AirQualityIndex(title={self.title}, value={self.value}, category={self.category}, description={self.description}, acronym:{self.acronym}, color={self.color})"
49
+
50
+ @staticmethod
51
+ def _extract_aqi(data: str):
52
+ parts = data.split('\n')
53
+ title = parts[0].strip()
54
+ aqi = int(parts[1].strip())
55
+ category = parts[2].strip() if len(parts) > 2 else None
56
+ description = parts[3].strip() if len(parts) > 3 else None
57
+ acronym = ''.join(word[0].strip().upper() for word in title.split())
58
+
59
+ return title, aqi, category, description, acronym
60
+
61
+ # 'Air Quality Index\n26\nGood\nAir quality is considered satisfactory, and air pollution poses little or no risk.'
62
+ @classmethod
63
+ def from_string(cls, data: str) -> 'AirQualityIndex':
64
+ try:
65
+ title, aqi, category, description, acronym = AirQualityIndex._extract_aqi(data)
66
+ return cls(title, aqi, category, description, acronym)
67
+ except (ValueError, IndexError) as e:
68
+ raise ValueError("Invalid AQI data format") from e
69
+
70
+ @classmethod
71
+ def aqi_color_from_string(cls, aqi_data: str, color_data: str):
72
+ try:
73
+ title, aqi, category, description, acronym = AirQualityIndex._extract_aqi(aqi_data)
74
+ color = Color.from_string(color_data)
75
+ return cls(title, aqi, category, description, acronym, color)
76
+ except(ValueError, IndexError) as e:
77
+ raise ValueError("Invalid AQI data format or color data format") from e
78
+
@@ -0,0 +1,37 @@
1
+ class CityLocation:
2
+
3
+ def __init__(self, city: str, state_province: str = None, country: str = None):
4
+ self._city = city
5
+ self._state_province = state_province
6
+ self._country = country
7
+
8
+ @property
9
+ def city(self) -> str:
10
+ return self._city
11
+
12
+ @property
13
+ def state_province(self) -> str:
14
+ return self._state_province
15
+
16
+ @property
17
+ def country(self) -> str:
18
+ return self._country
19
+
20
+ def __repr__(self):
21
+ return f"CityLocation(city={self.city}, state_province={self.state_province}, country={self.country})"
22
+
23
+ @classmethod
24
+ def from_string(cls, data: str) -> "CityLocation":
25
+ #'Nova Friburgo, Rio de Janeiro, Brazil'
26
+ parts = data.split(", ")
27
+ if len(parts) == 3:
28
+ city, state_province, country = parts
29
+ return cls(city=city, state_province=state_province, country=country)
30
+ if len(parts) == 2:
31
+ city, state_province = parts
32
+ return cls(city=city, state_province=state_province)
33
+ elif len(parts) == 1:
34
+ city = parts[0]
35
+ return cls(city=city)
36
+ else:
37
+ raise ValueError("Invalid city location string format")
@@ -0,0 +1,43 @@
1
+ import re
2
+
3
+ class Color:
4
+
5
+ def __init__(self, red: str, green: str, blue: str):
6
+ self._red = red
7
+ self._green = green
8
+ self._blue = blue
9
+
10
+ @property
11
+ def red(self):
12
+ return self._red
13
+
14
+ @property
15
+ def green(self):
16
+ return self._green
17
+
18
+ @property
19
+ def blue(self):
20
+ return self._blue
21
+
22
+ @classmethod
23
+ def from_string(cls,string_value: str) -> "Color":
24
+
25
+ color_pattern = r"#([0-9A-Fa-f]{6})"
26
+
27
+ match = re.search(color_pattern, string_value)
28
+ color = f"#{match.group(1)}"
29
+ hex_color = color.lstrip('#')
30
+ r, g, b = int(hex_color[:2], 16), int(hex_color[2:4], 16), int(hex_color[4:], 16)
31
+
32
+ return cls(r, g, b)
33
+
34
+ @property
35
+ def hex(self) -> str:
36
+ return f"#{self.red:02x}{self.green:02x}{self.blue:02x}"
37
+
38
+ @property
39
+ def rgb(self) -> str:
40
+ return f"rgb({self.red}, {self.green}, {self.blue})"
41
+
42
+ def __repr__(self):
43
+ return f"Color(red='{self.red}', green='{self.green}', blue='{self.blue}')"
@@ -0,0 +1,53 @@
1
+ from .city_location import CityLocation
2
+ from .timestamp import Timestamp
3
+ from .weather_icon_enum import WeatherIconEnum
4
+ from .day_night import DayNight
5
+ from typing import Optional
6
+
7
+ class CurrentConditions:
8
+ def __init__(
9
+ self,
10
+ location: Optional[CityLocation],
11
+ timestamp: Optional[Timestamp],
12
+ temperature: str,
13
+ icon: Optional[WeatherIconEnum],
14
+ summary: str,
15
+ day_night: Optional[DayNight]
16
+ ):
17
+ self._location = location
18
+ self._timestamp = timestamp
19
+ self._temperature = temperature
20
+ self._icon = icon
21
+ self._summary = summary
22
+ self._day_night = day_night
23
+
24
+ @property
25
+ def location(self) -> Optional[CityLocation]:
26
+ return self._location
27
+
28
+ @property
29
+ def timestamp(self) -> Optional[Timestamp]:
30
+ return self._timestamp
31
+
32
+ @property
33
+ def temperature(self) -> str:
34
+ return self._temperature
35
+
36
+ @property
37
+ def icon(self) -> Optional[WeatherIconEnum]:
38
+ return self._icon
39
+
40
+ @property
41
+ def summary(self) -> str:
42
+ return self._summary
43
+
44
+ @property
45
+ def day_night(self) -> Optional[DayNight]:
46
+ return self._day_night
47
+
48
+ def __repr__(self):
49
+ return (
50
+ f"CurrentConditions(location={self._location!r}, timestamp={self._timestamp!r}, "
51
+ f"temperature={self._temperature!r}, icon={self._icon!r}, summary={self._summary!r}, "
52
+ f"day_night={self._day_night!r})"
53
+ )
@@ -0,0 +1,58 @@
1
+ from .temperature_hight_low import TemperatureHighLow
2
+ from .weather_icon_enum import WeatherIconEnum
3
+ from .moon_phase import MoonPhase
4
+ from .precipitation import Precipitation
5
+ from typing import Optional
6
+
7
+ class DailyPredictions:
8
+ def __init__(
9
+ self,
10
+ title: str,
11
+ high_low: Optional[TemperatureHighLow],
12
+ icon: Optional[WeatherIconEnum],
13
+ summary: str,
14
+ precipitation: Optional[Precipitation],
15
+ moon_phase: Optional[MoonPhase] = None
16
+ ):
17
+ self._title = title
18
+ self._high_low = high_low
19
+ self._icon = icon
20
+ self._summary = summary
21
+ self._precipitation = precipitation
22
+ self._moon_phase = moon_phase
23
+
24
+ # ---- Properties ----
25
+ @property
26
+ def title(self) -> str:
27
+ return self._title
28
+
29
+ @property
30
+ def high_low(self) -> Optional[TemperatureHighLow]:
31
+ return self._high_low
32
+
33
+ @property
34
+ def icon(self) -> Optional[WeatherIconEnum]:
35
+ return self._icon
36
+
37
+ @property
38
+ def summary(self) -> str:
39
+ return self._summary
40
+
41
+ @property
42
+ def precipitation(self) -> Optional[Precipitation]:
43
+ return self._precipitation
44
+
45
+ @property
46
+ def moon_phase(self) -> Optional[MoonPhase]:
47
+ return self._moon_phase
48
+
49
+ def __repr__(self) -> str:
50
+ return (
51
+ f"DailyPredictions("
52
+ f"title={self._title!r}, "
53
+ f"high_low={self._high_low!r}, "
54
+ f"icon={self._icon!r}, "
55
+ f"summary={self._summary!r}, "
56
+ f"precipitation={self._precipitation!r}, "
57
+ f"moon_phase={self._moon_phase!r})"
58
+ )
@@ -0,0 +1,50 @@
1
+ class DayNight:
2
+ class Temperature:
3
+ def __init__(self, label: str, value: str):
4
+ self._label = label
5
+ self._value = value
6
+
7
+ @property
8
+ def label(self):
9
+ return self._label
10
+
11
+ @property
12
+ def value(self):
13
+ return self._value
14
+
15
+ @classmethod
16
+ def from_string(cls, text:str) -> 'DayNight.Temperature':
17
+ label, value = text.split("\xa0")
18
+ return cls(label.strip(), value.strip())
19
+
20
+ def __repr__(self):
21
+ return f"DayNight.Temperature(label={self.label!r}, value={self.value!r})"
22
+
23
+ def __str__(self):
24
+ return f"{self.label}: {self.value}"
25
+
26
+
27
+ def __init__(self, day: "DayNight.Temperature", night: "DayNight.Temperature"):
28
+ self._day = day
29
+ self._night = night
30
+
31
+ @property
32
+ def day(self):
33
+ return self._day
34
+
35
+ @property
36
+ def night(self):
37
+ return self._night
38
+
39
+ @classmethod
40
+ def from_string(cls, text: str) -> 'DayNight':
41
+ day_label, night_label = text.split("•")
42
+ day = DayNight.Temperature.from_string(day_label.strip())
43
+ night = DayNight.Temperature.from_string(night_label.strip())
44
+ return cls(day, night)
45
+
46
+ def __repr__(self):
47
+ return f"DayNight(day={self.day!r}, night={self.night!r})"
48
+
49
+ def __str__(self):
50
+ return f"Day: {self.day}, Night: {self.night}"
@@ -0,0 +1,68 @@
1
+ from typing import Optional, List
2
+ from .search import Search
3
+ from .weather_icon_enum import WeatherIconEnum
4
+ from .today_details import TodayDetails
5
+ from .air_quality_index import AirQualityIndex
6
+ from .health_activities import HealthActivities
7
+ from .hourly_predictions import HourlyPredictions
8
+ from .daily_predictions import DailyPredictions
9
+ from .current_conditions import CurrentConditions
10
+
11
+
12
+ class Forecast:
13
+ def __init__(
14
+ self,
15
+ search: Optional[Search],
16
+ current_conditions: Optional[CurrentConditions],
17
+ today_details: Optional[TodayDetails],
18
+ air_quality_index: Optional[AirQualityIndex],
19
+ health_activities: Optional[HealthActivities],
20
+ hourly_predictions: List[HourlyPredictions],
21
+ daily_predictions: List[DailyPredictions],
22
+ ):
23
+ self._search = search
24
+ self._current_conditions = current_conditions
25
+ self._today_details = today_details
26
+ self._air_quality_index = air_quality_index
27
+ self._health_activities = health_activities
28
+ self._hourly_predictions = hourly_predictions
29
+ self._daily_predictions = daily_predictions
30
+
31
+ @property
32
+ def search(self) -> Optional[Search]:
33
+ return self._search
34
+
35
+ @property
36
+ def current_conditions(self) -> Optional[CurrentConditions]:
37
+ return self._current_conditions
38
+
39
+ @property
40
+ def today_details(self) -> Optional[TodayDetails]:
41
+ return self._today_details
42
+
43
+ @property
44
+ def air_quality_index(self) -> Optional[AirQualityIndex]:
45
+ return self._air_quality_index
46
+
47
+ @property
48
+ def health_activities(self) -> Optional[HealthActivities]:
49
+ return self._health_activities
50
+
51
+ @property
52
+ def hourly_predictions(self) -> List[HourlyPredictions]:
53
+ return self._hourly_predictions
54
+
55
+ @property
56
+ def daily_predictions(self) -> List[DailyPredictions]:
57
+ return self._daily_predictions
58
+
59
+ def __repr__(self) -> str:
60
+ return (
61
+ f"Forecast(search={self._search}, "
62
+ f"current_conditions={self._current_conditions}, "
63
+ f"today_details={self._today_details}, "
64
+ f"air_quality_index={self._air_quality_index}, "
65
+ f"health_activities={self._health_activities}, "
66
+ f"hourly_predictions={len(self._hourly_predictions)} items, "
67
+ f"daily_predictions={len(self._daily_predictions)} items)"
68
+ )
@@ -0,0 +1,39 @@
1
+ class HealthActivities:
2
+ def __init__(self, category_name: str, title: str, description: str):
3
+ self._category_name = category_name
4
+ self._title = title
5
+ self._description = description
6
+
7
+ @property
8
+ def category_name(self) -> str:
9
+ return self._category_name
10
+
11
+ @property
12
+ def title(self) -> str:
13
+ return self._title
14
+
15
+ @property
16
+ def description(self) -> str:
17
+ return self._description
18
+
19
+ def __str__(self) -> str:
20
+ return f"{self._category_name}: {self._title} - {self._description}"
21
+
22
+ def __repr__(self) -> str:
23
+ return f"HealthActivities(category_name={self._category_name!r}, title={self._title!r}, description={self._description!r})"
24
+
25
+ # 'Health & Activities\nGrass\nSeasonal Allergies and Pollen Count Forecast\nGrass pollen is low in your area'
26
+ @staticmethod
27
+ def from_text(text: str):
28
+ try:
29
+ lines = text.split('\n')
30
+ if len(lines) >= 4:
31
+ category_name = lines[0].strip()
32
+ #Ignore the "grass" line
33
+ title = lines[2].strip()
34
+ description = ' '.join(line.strip() for line in lines[3:]).strip()
35
+ return HealthActivities(category_name, title, description)
36
+ else:
37
+ raise ValueError("Insufficient data to parse HealthActivities")
38
+ except Exception as e:
39
+ raise ValueError("Could not parse HealthActivities from text") from e
@@ -0,0 +1,76 @@
1
+ from .weather_icon_enum import WeatherIconEnum
2
+ from .precipitation import Precipitation
3
+ from .wind import Wind
4
+ from .uv_index import UVIndex
5
+ from typing import Optional
6
+ class HourlyPredictions:
7
+
8
+ def __init__(
9
+ self,
10
+ title: str,
11
+ temperature: str,
12
+ icon: WeatherIconEnum,
13
+ summary: str,
14
+ precipitation: Optional[Precipitation],
15
+ wind: Optional[Wind] = None,
16
+ feels_like: str = None,
17
+ humidity: str = None,
18
+ uv_index: Optional[UVIndex] = None,
19
+ cloud_cover: str = None
20
+ ):
21
+ self._title = title
22
+ self._temperature = temperature
23
+ self._icon = icon
24
+ self._summary = summary
25
+ self._precipitation = precipitation
26
+ self._wind = wind
27
+ self._feels_like = feels_like
28
+ self._humidity = humidity
29
+ self._uv_index = uv_index
30
+ self._cloud_cover = cloud_cover
31
+
32
+ @property
33
+ def title(self) -> str:
34
+ return self._title
35
+
36
+ @property
37
+ def temperature(self) -> str:
38
+ return self._temperature
39
+
40
+ @property
41
+ def icon(self) -> WeatherIconEnum:
42
+ return self._icon
43
+
44
+ @property
45
+ def summary(self) -> str:
46
+ return self._summary
47
+
48
+ @property
49
+ def precipitation(self) -> Optional[Precipitation]:
50
+ return self._precipitation
51
+
52
+ @property
53
+ def wind(self) -> Optional[Wind]:
54
+ return self._wind
55
+
56
+ @property
57
+ def feels_like(self) -> str:
58
+ return self._feels_like
59
+
60
+ @property
61
+ def humidity(self) -> str:
62
+ return self._humidity
63
+
64
+ @property
65
+ def uv_index(self) -> Optional[UVIndex]:
66
+ return self._uv_index
67
+
68
+ @property
69
+ def cloud_cover(self) -> str:
70
+ return self._cloud_cover
71
+
72
+ def __repr__(self):
73
+ return (f"HourlyPredictions(title={self._title!r}, temperature={self._temperature!r}, icon={self._icon!r}, "
74
+ f"summary={self._summary!r}, precipitation={self._precipitation!r}, wind={self._wind!r}, "
75
+ f"feels_like={self._feels_like!r}, humidity={self._humidity!r}, uv_index={self._uv_index!r}, "
76
+ f"cloud_cover={self._cloud_cover!r})")
@@ -0,0 +1,19 @@
1
+ class LabelValue:
2
+ def __init__(self, label: str, value: str):
3
+ self._label = label
4
+ self._value = value
5
+
6
+ @property
7
+ def label(self) -> str:
8
+ return self._label
9
+
10
+ @property
11
+ def value(self) -> str:
12
+ return self._value
13
+
14
+ def __repr__(self) -> str:
15
+ return f"LabelValue(label={self.label!r}, value={self.value!r})"
16
+
17
+
18
+ def __str__(self) -> str:
19
+ return f"{self.label} {self.value}"
@@ -0,0 +1,22 @@
1
+ from weathergrabber.domain.moon_phase_enum import MoonPhaseEnum
2
+
3
+ class MoonPhase:
4
+ def __init__(self, icon: MoonPhaseEnum, phase: str, label:str = None):
5
+ self._label = label
6
+ self._icon = icon
7
+ self._phase = phase
8
+
9
+ @property
10
+ def label(self) -> str:
11
+ return self._label
12
+
13
+ @property
14
+ def icon(self) -> MoonPhaseEnum:
15
+ return self._icon
16
+
17
+ @property
18
+ def phase(self) -> str:
19
+ return self._phase
20
+
21
+ def __repr__(self) -> str:
22
+ return f"MoonPhase(icon={self.icon!r}, phase={self.phase!r},label={self.label!r})"
@@ -0,0 +1,65 @@
1
+ from enum import Enum
2
+
3
+ class MoonPhaseEnum(Enum):
4
+ # New Moon
5
+ PHASE_0 = ("phase-0", "\uf186", "🌑")
6
+ # Waxing Crescent
7
+ PHASE_1 = ("phase-1", "\uf186", "🌒")
8
+ PHASE_2 = ("phase-2", "\uf186", "🌒")
9
+ PHASE_3 = ("phase-3", "\uf186", "🌒")
10
+ PHASE_4 = ("phase-4", "\uf186", "🌒")
11
+ PHASE_5 = ("phase-5", "\uf186", "🌒")
12
+ PHASE_6 = ("phase-6", "\uf186", "🌒")
13
+ # First Quarter
14
+ PHASE_7 = ("phase-7", "\uf186", "🌓")
15
+ # Waxing Gibbous
16
+ PHASE_8 = ("phase-8", "\uf186", "🌔")
17
+ PHASE_9 = ("phase-9", "\uf186", "🌔")
18
+ PHASE_10 = ("phase-10", "\uf186", "🌔")
19
+ PHASE_11 = ("phase-11", "\uf186", "🌔")
20
+ PHASE_12 = ("phase-12", "\uf186", "🌔")
21
+ PHASE_13 = ("phase-13", "\uf186", "🌔")
22
+ # Full Moon
23
+ PHASE_14 = ("phase-14", "\uf186", "🌕")
24
+ # Waning Gibbous
25
+ PHASE_15 = ("phase-15", "\uf186", "🌖")
26
+ PHASE_16 = ("phase-16", "\uf186", "🌖")
27
+ PHASE_17 = ("phase-17", "\uf186", "🌖")
28
+ PHASE_18 = ("phase-18", "\uf186", "🌖")
29
+ PHASE_19 = ("phase-19", "\uf186", "🌖")
30
+ PHASE_20 = ("phase-20", "\uf186", "🌖")
31
+ # Last Quarter
32
+ PHASE_21 = ("phase-21", "\uf186", "🌗")
33
+ # Waning Crescent
34
+ PHASE_22 = ("phase-22", "\uf186", "🌘")
35
+ PHASE_23 = ("phase-23", "\uf186", "🌘")
36
+ PHASE_24 = ("phase-24", "\uf186", "🌘")
37
+ PHASE_25 = ("phase-25", "\uf186", "🌘")
38
+ PHASE_26 = ("phase-26", "\uf186", "🌘")
39
+ PHASE_27 = ("phase-27", "\uf186", "🌘")
40
+ PHASE_28 = ("phase-28", "\uf186", "🌘")
41
+ DEFAULT = ("default", "\uf186", "🌑")
42
+
43
+ def __init__(self, name: str, fa_icon: str, emoji_icon: str):
44
+ self._name = name
45
+ self._fa_icon = fa_icon
46
+ self._emoji_icon = emoji_icon
47
+
48
+ @property
49
+ def name(self):
50
+ return self._name
51
+
52
+ @property
53
+ def fa_icon(self):
54
+ return self._fa_icon
55
+
56
+ @property
57
+ def emoji_icon(self):
58
+ return self._emoji_icon
59
+
60
+ @staticmethod
61
+ def from_name(name: str):
62
+ for item in MoonPhaseEnum:
63
+ if item._name == name:
64
+ return item
65
+ return None