weathergrabber 0.0.8b2__py3-none-any.whl → 0.0.8b4__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.
@@ -4,4 +4,7 @@ from .core import main
4
4
  from .cli import main_cli
5
5
 
6
6
  __all__ = ["main", "main_cli"]
7
- __version__ = "0.0.8b2"
7
+ __version__ = "0.0.8b4"
8
+
9
+ def get_version():
10
+ return __version__
@@ -1,4 +1,4 @@
1
- """weathergrabber CLI entry point."""
2
1
  from .cli import main_cli
2
+
3
3
  if __name__ == "__main__":
4
4
  main_cli()
@@ -15,5 +15,5 @@ class JsonTTY:
15
15
  self.logger.info("Executing JSON output")
16
16
  forecast = self.use_case.execute(params)
17
17
  output: dict = forecast_to_dict(forecast)
18
- output_json = json.dumps(output, indent=4)
18
+ output_json = json.dumps(output)
19
19
  print(output_json)
@@ -62,7 +62,7 @@ class WaybarTTY:
62
62
 
63
63
  #Air quality index
64
64
  color = forecast.air_quality_index.color.hex
65
- aqi_category = f" <span color=\"{color}\">{forecast.air_quality_index.category}</span>"
65
+ aqi_category = f" <span color=\"#{color}\">{forecast.air_quality_index.category}</span>"
66
66
  aqi_acronym = forecast.air_quality_index.acronym
67
67
  aqi_value = forecast.air_quality_index.value
68
68
 
weathergrabber/cli.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import argparse
2
2
  import os
3
- from weathergrabber import main
3
+ import weathergrabber
4
4
 
5
5
  def main_cli():
6
6
  ## Get current locale, or use the default one
@@ -11,6 +11,7 @@ def main_cli():
11
11
  parser.add_argument("--output", "-o", type=str, choices=['console','json','waybar'], default='console', help="Output format. console, json or waybar")
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("--version", "-v", action='version', version=f'Weathergrabber {weathergrabber.get_version()}', help="Show version and exit")
14
15
  parser.add_argument(
15
16
  "--log",
16
17
  default="critical",
@@ -25,7 +26,7 @@ def main_cli():
25
26
  if not args.location_id and not args.location_name:
26
27
  location_id = os.getenv('WEATHER_LOCATION_ID')
27
28
 
28
- main(
29
+ weathergrabber.main(
29
30
  log_level=args.log,
30
31
  location_name = args.location_name,
31
32
  location_id = location_id,
@@ -34,6 +35,3 @@ def main_cli():
34
35
  keep_open=args.keep_open,
35
36
  icons=args.icons
36
37
  )
37
-
38
- if __name__ == "__main__":
39
- main_cli()
@@ -45,34 +45,32 @@ class AirQualityIndex:
45
45
  return f"Title: {self.title}. AQI: {self.value}, Category: {self.category}, Description: {self.description}, Acronym: {self.acronym}, Color: {self.color}"
46
46
 
47
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})"
48
+ return f"AirQualityIndex(title='{self.title}', value={self.value}, category='{self.category}', description='{self.description}', acronym='{self.acronym}', color='{self.color}')"
49
49
 
50
50
  @staticmethod
51
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())
52
+ try:
53
+ parts = data.split('\n')
54
+ title = parts[0].strip()
55
+ aqi = int(parts[1].strip())
56
+ category = parts[2].strip() if len(parts) > 2 else None
57
+ description = parts[3].strip() if len(parts) > 3 else None
58
+ acronym = ''.join(word[0].strip().upper() for word in title.split())
58
59
 
59
- return title, aqi, category, description, acronym
60
+ return title, aqi, category, description, acronym
61
+ except (ValueError, IndexError) as e:
62
+ raise ValueError("Invalid AQI data format") from e
60
63
 
61
64
  # 'Air Quality Index\n26\nGood\nAir quality is considered satisfactory, and air pollution poses little or no risk.'
62
65
  @classmethod
63
66
  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
67
+ title, aqi, category, description, acronym = AirQualityIndex._extract_aqi(data)
68
+ return cls(title, aqi, category, description, acronym)
69
69
 
70
70
  @classmethod
71
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
72
+ title, aqi, category, description, acronym = AirQualityIndex._extract_aqi(aqi_data)
73
+ color = Color.from_string(color_data)
74
+ return cls(title, aqi, category, description, acronym, color)
75
+
78
76
 
@@ -46,9 +46,12 @@ class CityLocation:
46
46
  country, state_province, city, location = None, None, None, None
47
47
  parts = data.split(", ")
48
48
 
49
+ if data.strip() == "":
50
+ raise ValueError("City location string cannot be empty")
51
+
49
52
  if len(parts) > 2:
50
53
  i = len(parts) - 1
51
- while i > 0:
54
+ while i >= 0:
52
55
  if not country:
53
56
  country = parts[i]
54
57
  elif not state_province:
@@ -66,6 +69,4 @@ class CityLocation:
66
69
  return cls(city=city, state_province=state_province)
67
70
  elif len(parts) == 1:
68
71
  city = parts[0]
69
- return cls(city=city)
70
- else:
71
- raise ValueError("Invalid city location string format")
72
+ return cls(city=city)
@@ -1,22 +1,29 @@
1
1
  import re
2
2
 
3
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
4
+
5
+ def __init__(self, red: str | int, green: str | int, blue: str | int):
6
+ self._red = self._int_or_hex(red)
7
+ self._green = self._int_or_hex(green)
8
+ self._blue = self._int_or_hex(blue)
9
+
10
+ def _int_or_hex(self, value: str | int) -> int:
11
+ if type(value) == int:
12
+ if not (0 <= value <= 255):
13
+ raise ValueError("RGB integer values must be between 0 and 255")
14
+ return value
15
+ return int(value, 16)
9
16
 
10
17
  @property
11
- def red(self):
18
+ def red(self) -> int:
12
19
  return self._red
13
20
 
14
21
  @property
15
- def green(self):
22
+ def green(self) -> int:
16
23
  return self._green
17
24
 
18
25
  @property
19
- def blue(self):
26
+ def blue(self) -> int:
20
27
  return self._blue
21
28
 
22
29
  @classmethod
@@ -24,20 +31,25 @@ class Color:
24
31
 
25
32
  color_pattern = r"#([0-9A-Fa-f]{6})"
26
33
 
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
+ try:
35
+ match = re.search(color_pattern, string_value)
36
+ color = f"#{match.group(1)}"
37
+ hex_color = color.lstrip('#')
38
+ r, g, b = hex_color[:2], hex_color[2:4], hex_color[4:]
39
+ return cls(r, g, b)
40
+ except (AttributeError, ValueError):
41
+ raise ValueError(f"Invalid color string: {string_value}")
42
+
34
43
  @property
35
44
  def hex(self) -> str:
36
- return f"#{self.red:02x}{self.green:02x}{self.blue:02x}"
45
+ return f"{self.red:02x}{self.green:02x}{self.blue:02x}".upper()
37
46
 
38
47
  @property
39
48
  def rgb(self) -> str:
40
49
  return f"rgb({self.red}, {self.green}, {self.blue})"
41
50
 
51
+ def __str__(self):
52
+ return f"{self.hex}"
53
+
42
54
  def __repr__(self):
43
55
  return f"Color(red='{self.red}', green='{self.green}', blue='{self.blue}')"
@@ -23,8 +23,8 @@ class HealthActivities:
23
23
  return f"HealthActivities(category_name={self._category_name!r}, title={self._title!r}, description={self._description!r})"
24
24
 
25
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):
26
+ @classmethod
27
+ def from_text(cls, text: str):
28
28
  try:
29
29
  lines = text.split('\n')
30
30
  if len(lines) >= 4:
@@ -32,7 +32,7 @@ class HealthActivities:
32
32
  #Ignore the "grass" line
33
33
  title = lines[2].strip()
34
34
  description = ' '.join(line.strip() for line in lines[3:]).strip()
35
- return HealthActivities(category_name, title, description)
35
+ return cls(category_name, title, description)
36
36
  else:
37
37
  raise ValueError("Insufficient data to parse HealthActivities")
38
38
  except Exception as e:
@@ -8,9 +8,9 @@ class MoonPhaseEnum(Enum):
8
8
  PHASE_2 = ("phase-2", "\uf186", "🌒")
9
9
  PHASE_3 = ("phase-3", "\uf186", "🌒")
10
10
  PHASE_4 = ("phase-4", "\uf186", "🌒")
11
- PHASE_5 = ("phase-5", "\uf186", "🌒")
12
- PHASE_6 = ("phase-6", "\uf186", "🌒")
13
11
  # First Quarter
12
+ PHASE_5 = ("phase-5", "\uf186", "🌓")
13
+ PHASE_6 = ("phase-6", "\uf186", "🌓")
14
14
  PHASE_7 = ("phase-7", "\uf186", "🌓")
15
15
  # Waxing Gibbous
16
16
  PHASE_8 = ("phase-8", "\uf186", "🌔")
@@ -21,8 +21,8 @@ class MoonPhaseEnum(Enum):
21
21
  PHASE_13 = ("phase-13", "\uf186", "🌕")
22
22
  # Full Moon
23
23
  PHASE_14 = ("phase-14", "\uf186", "🌕")
24
- # Waning Gibbous
25
24
  PHASE_15 = ("phase-15", "\uf186", "🌕")
25
+ # Waning Gibbous
26
26
  PHASE_16 = ("phase-16", "\uf186", "🌖")
27
27
  PHASE_17 = ("phase-17", "\uf186", "🌖")
28
28
  PHASE_18 = ("phase-18", "\uf186", "🌖")
@@ -30,10 +30,10 @@ class MoonPhaseEnum(Enum):
30
30
  PHASE_20 = ("phase-20", "\uf186", "🌖")
31
31
  # Last Quarter
32
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", "🌘")
33
+ PHASE_22 = ("phase-22", "\uf186", "🌗")
34
+ PHASE_23 = ("phase-23", "\uf186", "🌗")
35
+ PHASE_24 = ("phase-24", "\uf186", "🌗")
36
+
37
37
  PHASE_25 = ("phase-25", "\uf186", "🌘")
38
38
  PHASE_26 = ("phase-26", "\uf186", "🌘")
39
39
  PHASE_27 = ("phase-27", "\uf186", "🌘")
@@ -24,6 +24,8 @@ class UVIndex:
24
24
 
25
25
  @classmethod
26
26
  def from_string(cls, data: str, label: str = None) -> 'UVIndex':
27
+ if not data:
28
+ raise ValueError("UV Index string cannot be empty")
27
29
  parts = data.split(' ')
28
30
  if len(parts) == 1:
29
31
  return cls(string_value = data, index= parts[0].strip(), of="", label=label)
@@ -31,7 +33,7 @@ class UVIndex:
31
33
  index, of, some = parts
32
34
  return cls(string_value = data, index=index.strip(), of=some.strip(), label=label)
33
35
  else:
34
- raise ValueError(f"Cannot parse UV Index from string: {data}")
36
+ return cls(string_value = data, index="", of="", label=label)
35
37
 
36
38
  def __repr__(self) -> str:
37
39
  return f"UVIndex(string_value={self.string_value!r}, index={self.index!r}, of={self.of!r}, label={self.label!r})"
@@ -39,5 +41,5 @@ class UVIndex:
39
41
  def __str__(self) -> str:
40
42
  if self.string_value:
41
43
  return f"{self.label} {self.string_value}"
42
- elif self.index and self.of:
44
+ else:
43
45
  return f"{self.label} {self.index} {self.of}" if self.label else f"{self.index} {self.of}"
@@ -12,19 +12,16 @@ class ExtractAQIService:
12
12
 
13
13
  self.logger.debug("Extracting Air Quality Index (AQI)...")
14
14
 
15
- try:
16
- # 'Air Quality Index\n27\nGood\nAir quality is considered satisfactory, and air pollution poses little or no risk.\nSee Details\nInfo'
17
- aqi_data = weather_data("section[data-testid='AirQualityModule']").text()
15
+ # 'Air Quality Index\n27\nGood\nAir quality is considered satisfactory, and air pollution poses little or no risk.\nSee Details\nInfo'
16
+ aqi_data = weather_data("section[data-testid='AirQualityModule']").text()
18
17
 
19
- # 'stroke-width:5;stroke-dasharray:10.021680564951442 172.78759594743863;stroke:#00E838'
20
- color_data = weather_data("section[data-testid='AirQualityModule'] svg[data-testid='DonutChart'] circle:nth-of-type(2)").attr("style")
18
+ # 'stroke-width:5;stroke-dasharray:10.021680564951442 172.78759594743863;stroke:#00E838'
19
+ color_data = weather_data("section[data-testid='AirQualityModule'] svg[data-testid='DonutChart'] circle:nth-of-type(2)").attr("style")
21
20
 
22
- air_quality_index = AirQualityIndex.aqi_color_from_string(aqi_data,color_data)
21
+ air_quality_index = AirQualityIndex.aqi_color_from_string(aqi_data,color_data)
23
22
 
24
- self.logger.debug(f"Extracted AQI data: {air_quality_index}")
23
+ self.logger.debug(f"Extracted AQI data: {air_quality_index}")
25
24
 
26
- return air_quality_index
27
- except Exception as e:
28
- self.logger.error(f"Error extracting AQI data: {e}")
29
- raise ValueError("Could not extract AQI data") from e
25
+ return air_quality_index
26
+
30
27
 
@@ -14,51 +14,45 @@ class ExtractHourlyForecastService:
14
14
  pass
15
15
 
16
16
  def execute(self, weather_data: PyQuery) -> List[HourlyPredictions]:
17
- try:
18
- self.logger.debug("Extracting hourly forecast...")
19
-
20
- data = weather_data.find("section[data-testid='HourlyForecast'] div[class*='Card'] details")
21
-
22
- if len(data) == 0:
23
- raise ValueError("Unable to extract hourly forecast")
24
-
25
- details = [ {
26
- "title": PyQuery(item).find("h2").text(),
27
- "temperature" : PyQuery(item).find("div[data-testid='detailsTemperature']").text(),
28
- "icon" : PyQuery(item).find("svg[class*='DetailsSummary']").attr("name"),
29
- "summary" : PyQuery(item).find("span[class*='DetailsSummary--wxPhrase']").text(),
30
- "precip-percentage": PyQuery(item).find("div[data-testid='Precip'] span[data-testid='PercentageValue']").text(),
31
- "wind": PyQuery(item).find("span[data-testid='WindTitle']").next().eq(0).text(),
32
- "feels-like" : PyQuery(item).find("span[data-testid='FeelsLikeTitle']").next().text(),
33
- "humidity" : PyQuery(item).find("span[data-testid='HumidityTitle']").next().text(),
34
- "uv-index" : PyQuery(item).find("span[data-testid='UVIndexValue']").text(),
35
- "cloud-cover" : PyQuery(item).find("span[data-testid='CloudCoverTitle']").next().text(),
36
- "rain-amount" : PyQuery(item).find("span[data-testid='AccumulationTitle']").next().text()
37
- } for item in data ]
38
-
39
- self.logger.debug("Extracted %s register(s)...",len(details))
40
-
41
- hourly_forecasts = [HourlyPredictions(
42
- title=item["title"],
43
- temperature=item["temperature"],
44
- icon=WeatherIconEnum.from_name(item["icon"]),
45
- summary=item["summary"],
46
- precipitation=Precipitation(
47
- percentage=item["precip-percentage"],
48
- amount=item["rain-amount"]
49
- ),
50
- wind=Wind.from_string(item["wind"]),
51
- feels_like=item["feels-like"],
52
- humidity=item["humidity"],
53
- uv_index=UVIndex.from_string(item["uv-index"]),
54
- cloud_cover=item["cloud-cover"]
55
- ) for item in details]
56
-
57
- self.logger.debug("Created hourly forecast list with %s registers", len(hourly_forecasts))
58
-
59
- return hourly_forecasts
60
-
61
-
62
- except Exception as e:
63
- self.logger.error(f"Error extracting hourly forecast: {e}")
64
- raise ValueError("Could not extract hourly forecast.") from e
17
+ self.logger.debug("Extracting hourly forecast...")
18
+
19
+ data = weather_data.find("section[data-testid='HourlyForecast'] div[class*='Card'] details")
20
+
21
+ if len(data) == 0:
22
+ raise ValueError("There's no hourly forecast data available.")
23
+
24
+ details = [ {
25
+ "title": PyQuery(item).find("h2").text(),
26
+ "temperature" : PyQuery(item).find("div[data-testid='detailsTemperature']").text(),
27
+ "icon" : PyQuery(item).find("svg[class*='DetailsSummary']").attr("name"),
28
+ "summary" : PyQuery(item).find("span[class*='DetailsSummary--wxPhrase']").text(),
29
+ "precip-percentage": PyQuery(item).find("div[data-testid='Precip'] span[data-testid='PercentageValue']").text(),
30
+ "wind": PyQuery(item).find("span[data-testid='WindTitle']").next().eq(0).text(),
31
+ "feels-like" : PyQuery(item).find("span[data-testid='FeelsLikeTitle']").next().text(),
32
+ "humidity" : PyQuery(item).find("span[data-testid='HumidityTitle']").next().text(),
33
+ "uv-index" : PyQuery(item).find("span[data-testid='UVIndexValue']").text(),
34
+ "cloud-cover" : PyQuery(item).find("span[data-testid='CloudCoverTitle']").next().text(),
35
+ "rain-amount" : PyQuery(item).find("span[data-testid='AccumulationTitle']").next().text()
36
+ } for item in data ]
37
+
38
+ self.logger.debug("Extracted %s register(s)...",len(details))
39
+
40
+ hourly_forecasts = [HourlyPredictions(
41
+ title=item["title"],
42
+ temperature=item["temperature"],
43
+ icon=WeatherIconEnum.from_name(item["icon"]),
44
+ summary=item["summary"],
45
+ precipitation=Precipitation(
46
+ percentage=item["precip-percentage"],
47
+ amount=item["rain-amount"]
48
+ ),
49
+ wind=Wind.from_string(item["wind"]),
50
+ feels_like=item["feels-like"],
51
+ humidity=item["humidity"],
52
+ uv_index=UVIndex.from_string(item["uv-index"]),
53
+ cloud_cover=item["cloud-cover"]
54
+ ) for item in details]
55
+
56
+ self.logger.debug("Created hourly forecast list with %s registers", len(hourly_forecasts))
57
+
58
+ return hourly_forecasts
@@ -14,72 +14,67 @@ class ExtractTodayDetailsService:
14
14
  pass
15
15
 
16
16
  def execute(self, weather_data: PyQuery) -> TodayDetails:
17
- try:
18
- self.logger.debug("Extracting today's details...")
17
+ self.logger.debug("Extracting today's details...")
19
18
 
20
- today_details_data = weather_data.find("div#todayDetails")
21
- feelslike = PyQuery(today_details_data).find("div[data-testid='FeelsLikeSection'] span")
22
- sunrise_sunset = PyQuery(today_details_data).find("div[data-testid='sunriseSunsetContainer'] div p[class*='TwcSunChart']")
19
+ today_details_data = weather_data.find("div#todayDetails")
20
+ feelslike = PyQuery(today_details_data).find("div[data-testid='FeelsLikeSection'] span")
21
+ sunrise_sunset = PyQuery(today_details_data).find("div[data-testid='sunriseSunsetContainer'] div p[class*='TwcSunChart']")
23
22
 
24
- feelslike_label = feelslike.eq(0).text() #'Feels Like'
25
- feelslike_value = feelslike.eq(1).text() #'60°'
23
+ feelslike_label = feelslike.eq(0).text() #'Feels Like'
24
+ feelslike_value = feelslike.eq(1).text() #'60°'
26
25
 
27
- sunrise = sunrise_sunset.eq(0).text() #'6:12 AM'
28
- sunset = sunrise_sunset.eq(1).text() #'7:45 PM'
26
+ sunrise = sunrise_sunset.eq(0).text() #'6:12 AM'
27
+ sunset = sunrise_sunset.eq(1).text() #'7:45 PM'
29
28
 
30
- icons = today_details_data.find('svg[class*="WeatherDetailsListItem--icon"]')
31
- labels = today_details_data.find('div[class*="WeatherDetailsListItem--label"]')
32
- values = today_details_data.find('div[data-testid="wxData"]')
29
+ icons = today_details_data.find('svg[class*="WeatherDetailsListItem--icon"]')
30
+ labels = today_details_data.find('div[class*="WeatherDetailsListItem--label"]')
31
+ values = today_details_data.find('div[data-testid="wxData"]')
33
32
 
34
- self.logger.debug(f"Parsing today details values...")
35
- high_low_label = labels.eq(0).text() #'High / Low'
36
- high_low_value = values.eq(0).text() #'--/54°'
33
+ self.logger.debug(f"Parsing today details values...")
34
+ high_low_label = labels.eq(0).text() #'High / Low'
35
+ high_low_value = values.eq(0).text() #'--/54°'
37
36
 
38
- wind_label = labels.eq(1).text() #'Wind'
39
- wind_value = values.eq(1).text() #'7\xa0mph'
37
+ wind_label = labels.eq(1).text() #'Wind'
38
+ wind_value = values.eq(1).text() #'7\xa0mph'
40
39
 
41
- humidity_label = labels.eq(2).text() #'Humidity'
42
- humidity_value = values.eq(2).text() #'100%'
40
+ humidity_label = labels.eq(2).text() #'Humidity'
41
+ humidity_value = values.eq(2).text() #'100%'
43
42
 
44
- dew_point_label = labels.eq(3).text() #'Dew Point'
45
- dew_point_value = values.eq(3).text() #'60°'
43
+ dew_point_label = labels.eq(3).text() #'Dew Point'
44
+ dew_point_value = values.eq(3).text() #'60°'
46
45
 
47
- pressure_label = labels.eq(4).text() #'Pressure'
48
- pressure_value = values.eq(4).text() #'30.31\xa0in'
49
-
50
- uv_index_label = labels.eq(5).text() #'UV Index'
51
- uv_index_value = values.eq(5).text() #'5 of 10'
52
-
53
- visibility_label = labels.eq(6).text() #'Visibility'
54
- visibility_value = values.eq(6).text() #'10.0 mi'
55
-
56
- moon_phase_label = labels.eq(7).text() #'Moon Phase'
57
- moon_phase_icon = icons.eq(7).attr('name') #'phase-2'
58
- moon_phase_value = values.eq(7).text() #'Waxing Crescent'
59
-
60
- self.logger.debug(f"Creating domain objects for today details...")
61
-
62
- sunrise_sunset = SunriseSunset(sunrise=sunrise, sunset=sunset)
63
- high_low = TemperatureHighLow.from_string(high_low_value, label=high_low_label)
64
- uv_index = UVIndex.from_string(uv_index_value, label=uv_index_label)
65
- moon_phase = MoonPhase(MoonPhaseEnum.from_name(moon_phase_icon), moon_phase_value, moon_phase_label)
66
-
67
- today_details = TodayDetails(
68
- feelslike=LabelValue(label=feelslike_label, value=feelslike_value),
69
- sunrise_sunset=sunrise_sunset,
70
- high_low=high_low,
71
- wind=LabelValue(label=wind_label, value=wind_value),
72
- humidity=LabelValue(label=humidity_label, value=humidity_value),
73
- dew_point=LabelValue(label=dew_point_label, value=dew_point_value),
74
- pressure=LabelValue(label=pressure_label, value=pressure_value),
75
- uv_index=uv_index,
76
- visibility=LabelValue(label=visibility_label, value=visibility_value),
77
- moon_phase=moon_phase
78
- )
79
-
80
- self.logger.debug(f"Extracted today's details: {today_details}")
81
- return today_details
82
-
83
- except Exception as e:
84
- self.logger.error(f"Error extracting today's details: {e}")
85
- raise ValueError("Could not extract today's details.") from e
46
+ pressure_label = labels.eq(4).text() #'Pressure'
47
+ pressure_value = values.eq(4).text() #'30.31\xa0in'
48
+
49
+ uv_index_label = labels.eq(5).text() #'UV Index'
50
+ uv_index_value = values.eq(5).text() #'5 of 10'
51
+
52
+ visibility_label = labels.eq(6).text() #'Visibility'
53
+ visibility_value = values.eq(6).text() #'10.0 mi'
54
+
55
+ moon_phase_label = labels.eq(7).text() #'Moon Phase'
56
+ moon_phase_icon = icons.eq(7).attr('name') #'phase-2'
57
+ moon_phase_value = values.eq(7).text() #'Waxing Crescent'
58
+
59
+ self.logger.debug(f"Creating domain objects for today details...")
60
+
61
+ sunrise_sunset = SunriseSunset(sunrise=sunrise, sunset=sunset)
62
+ high_low = TemperatureHighLow.from_string(high_low_value, label=high_low_label)
63
+ uv_index = UVIndex.from_string(uv_index_value, label=uv_index_label)
64
+ moon_phase = MoonPhase(MoonPhaseEnum.from_name(moon_phase_icon), moon_phase_value, moon_phase_label)
65
+
66
+ today_details = TodayDetails(
67
+ feelslike=LabelValue(label=feelslike_label, value=feelslike_value),
68
+ sunrise_sunset=sunrise_sunset,
69
+ high_low=high_low,
70
+ wind=LabelValue(label=wind_label, value=wind_value),
71
+ humidity=LabelValue(label=humidity_label, value=humidity_value),
72
+ dew_point=LabelValue(label=dew_point_label, value=dew_point_value),
73
+ pressure=LabelValue(label=pressure_label, value=pressure_value),
74
+ uv_index=uv_index,
75
+ visibility=LabelValue(label=visibility_label, value=visibility_value),
76
+ moon_phase=moon_phase
77
+ )
78
+
79
+ self.logger.debug(f"Extracted today's details: {today_details}")
80
+ return today_details
@@ -57,16 +57,16 @@ class UseCase:
57
57
  health_activities = self.extract_health_activities_service.execute(weather_data)
58
58
 
59
59
  try:
60
- hourly_predictions = self.extract_hourly_forecast_service.execute(weather_data)
61
- except ValueError:
62
- self.logger.warning("Falling back to old style hourly forecast extraction")
63
60
  hourly_predictions = self.extract_hourly_forecast_oldstyle_service.execute(weather_data)
64
-
65
- try:
66
- daily_predictions = self.extract_daily_forecast_service.execute(weather_data)
67
61
  except ValueError:
68
- self.logger.warning("Falling back to old style daily forecast extraction")
62
+ self.logger.warning("Falling back to new style hourly forecast extraction")
63
+ hourly_predictions = self.extract_hourly_forecast_service.execute(weather_data)
64
+
65
+ try:
69
66
  daily_predictions = self.extract_daily_forecast_oldstyle_service.execute(weather_data)
67
+ except ValueError:
68
+ self.logger.warning("Falling back to new style daily forecast extraction")
69
+ daily_predictions = self.extract_daily_forecast_service.execute(weather_data)
70
70
 
71
71
  forecast = Forecast(
72
72
  search = Search(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weathergrabber
3
- Version: 0.0.8b2
3
+ Version: 0.0.8b4
4
4
  Summary: A grabber for weather.com data with various output formats.
5
5
  Author-email: Carlos Anselmo Mendes Junior <cjuniorfox@gmail.com>
6
6
  License: MIT
@@ -1,32 +1,32 @@
1
- weathergrabber/__init__.py,sha256=Fz2isfYK8Ge58jAn1cCUV1qLN1qDNdlKlKjsNOeCRco,175
2
- weathergrabber/__main__.py,sha256=K10wQv3-LSRD_FRLHWphdSs3OAcuzIb4sQaKmj7Fql0,105
3
- weathergrabber/cli.py,sha256=YOWoVLfwktgqK1bp89GUKkAEKyMM4DaNL4akBi6xVVk,1979
1
+ weathergrabber/__init__.py,sha256=zluMrDBKUQsyNxzMD46YlM7Dl5CLzgFXzkbWD4-tYjs,218
2
+ weathergrabber/__main__.py,sha256=yYL-jc4kVHqaYVADzjKZfmCN2mziRofsXgnzrECbGDo,68
3
+ weathergrabber/cli.py,sha256=GA1EVClwfHOImM0TEfkTlYOgZU9bzscKg_0HL-Y9TBc,2090
4
4
  weathergrabber/core.py,sha256=TiZ2utmYKf9lkIXWv8YBfSdiHZXJZXuHS8B-dBDvevw,1138
5
5
  weathergrabber/weathergrabber_application.py,sha256=2JfZAR94En3rmGrYKWRKxdRXmK_ikhJdgrGotFjtDys,3987
6
6
  weathergrabber/adapter/client/weather_api.py,sha256=9S7JmXaAVvvPY60dz6rC3lx7X68BcqyvNzvSVM1-af8,963
7
7
  weathergrabber/adapter/client/weather_search_api.py,sha256=1oy7JitHcmwkkhFlD0eIt5A7a4cGbf7LMNi26tR8z5o,1724
8
8
  weathergrabber/adapter/tty/console_tty.py,sha256=7Ih0DwRyEfaYM4tfCMHhyHo-ti97u-OmZ36ANwEAu5I,5953
9
- weathergrabber/adapter/tty/json_tty.py,sha256=ZRZSfVhFYsb8AXD1qjb1GBpaRMxHGNaMXW5VN8AvdaU,647
10
- weathergrabber/adapter/tty/waybar_tty.py,sha256=wRvqT63ubb8Zz0YxeJM_mhOiJE0USK4Y5NquFf_V1qw,6136
11
- weathergrabber/domain/air_quality_index.py,sha256=eH1glUdedtbRCZMcw5_zB5ybJQUcyY-EBMxL8vVJ6zA,2791
12
- weathergrabber/domain/city_location.py,sha256=GHzFMvOhks7iQkWgR0mS-L21uAb_tlNycvfrdY-itzc,2362
13
- weathergrabber/domain/color.py,sha256=dkWZnQ2BjXUV8qTfZnmNGng_cqKtKuDdekYw4R2NJ9U,1080
9
+ weathergrabber/adapter/tty/json_tty.py,sha256=GyUc13w_5cjFLJda65Xt4e87gYGJow1dYnkY3ZmcmP8,637
10
+ weathergrabber/adapter/tty/waybar_tty.py,sha256=SJtqdvxmt2lIpjwP29x7lS32KcakYMLCaztApO8L7wM,6137
11
+ weathergrabber/domain/air_quality_index.py,sha256=8uBqxVIKCYIvrHuP-XbCHjYZhq7Z0RmqkgAAdCw6Tp4,2680
12
+ weathergrabber/domain/city_location.py,sha256=p-11c7AwA8t-nNdvTjl9-Oc5Kw4D2XP7tI4WrATVYpQ,2382
13
+ weathergrabber/domain/color.py,sha256=HfgB5CwxrSHT05TIXCqVCy0GoRpuEl99aUrUSJhaqcE,1601
14
14
  weathergrabber/domain/current_conditions.py,sha256=S14j2lL_gc2pds0wbl5M4_72PbVdubOuX8lnE_tZVTg,1477
15
15
  weathergrabber/domain/daily_predictions.py,sha256=8pzmaU-yugS2Hx_6MBWAk24wlSISe1OQIRrCK_lfvbw,1618
16
16
  weathergrabber/domain/day_night.py,sha256=7geOuVH9xDb1ecLKnZmj6eqp3H3kp9G0YY497cEyPtg,1442
17
17
  weathergrabber/domain/forecast.py,sha256=CO0VTdMI0kjuwbpwwDNRCf_82oNtw5QkN-9GgobPet8,2399
18
- weathergrabber/domain/health_activities.py,sha256=wCG82-lBfrnIwxptgnMZt-fXHJsBnIl4YLpoeFOmC48,1490
18
+ weathergrabber/domain/health_activities.py,sha256=oaIF_nI-iaN4OIHWTL-L7QZUQxCemdmNvzprDXysK-g,1481
19
19
  weathergrabber/domain/hourly_predictions.py,sha256=PFiGuo7mBoQHLR-bDirV--R126YAOzkwIHgaeRGFQSA,2179
20
20
  weathergrabber/domain/label_value.py,sha256=LvrvZbSrcEUUpxvKAmYkFnpDFWsa6LFmGA9O8i6HB84,446
21
21
  weathergrabber/domain/moon_phase.py,sha256=sEHRi8yPIHTzaEcOkz556uNYsKLq4YmYa5qDncSX44c,587
22
- weathergrabber/domain/moon_phase_enum.py,sha256=5NUySSwDr4k5n2aMrNDc9YYmKAbb6zycFO2w1z1SwxQ,2091
22
+ weathergrabber/domain/moon_phase_enum.py,sha256=unHTHWKXrYNyRrUbRqI-7KsXWUSYPnd7PeL23-cyiew,2074
23
23
  weathergrabber/domain/precipitation.py,sha256=eXrpwMOsEJWGqV4bEBhN9niWYXalgdZRLG4-I39JZ2A,466
24
24
  weathergrabber/domain/search.py,sha256=j3BzskyPl0hDWV02XTOC4tJonV5RHxr5Rop_rYMKUtA,387
25
25
  weathergrabber/domain/sunrise_sunset.py,sha256=wNTk01NIuLbQ7gN_giAFv4f3FaRx9khul-mj19g57vE,1207
26
26
  weathergrabber/domain/temperature_hight_low.py,sha256=PQOJ5uDtfMRBR5yMxXA46xuorJC08jva2C0-WAV5yxs,909
27
27
  weathergrabber/domain/timestamp.py,sha256=Bk6f8Tx0-yNitYmEKIWHnqh_ALDwxEHrhoCRSrfvYTU,1222
28
28
  weathergrabber/domain/today_details.py,sha256=EUlV7xerYw5QhEsBfvO5m6-9Ghm4nPkXJz9zCmSYTbA,2398
29
- weathergrabber/domain/uv_index.py,sha256=lNdk38Jq-A9msuzOLjIKrZIHUc2C9J8V4MA7HU3s1ZM,1420
29
+ weathergrabber/domain/uv_index.py,sha256=7XalamfjJdVSqo4x7G4JVf_HJtrPJxO1BpbvmAfBhnw,1481
30
30
  weathergrabber/domain/weather_icon_enum.py,sha256=oY-V6qIHjJjUE0baTkrECFwL0VSe4IYte-aR53zl_ew,3901
31
31
  weathergrabber/domain/wind.py,sha256=wTDz3X1rYsnw_eNoDi1miwaomxwhiJkY_q6xrdZtLak,789
32
32
  weathergrabber/domain/adapter/icon_enum.py,sha256=YxGYS5vBRV2AiAfeuPOdqaQOHixAssiMbOzQnTmdSBg,84
@@ -52,21 +52,21 @@ weathergrabber/domain/adapter/mapper/today_details_mapper.py,sha256=y9F5b3IQXIvb
52
52
  weathergrabber/domain/adapter/mapper/uv_index_mapper.py,sha256=K3AdRnAPv1Yqudc3eKcw_EBQidNPbHbLcG4lYrQvOOw,230
53
53
  weathergrabber/domain/adapter/mapper/weather_icon_enum_mapper.py,sha256=YC7juvt38Ehtb3Y-iQFM77s1EQAv4qNHd6vGOqws6HI,249
54
54
  weathergrabber/domain/adapter/mapper/wind_mapper.py,sha256=nXyYwqTvLLMyKtSey27GaGvBV8xVhB_Y3HU0sbmIe_E,149
55
- weathergrabber/service/extract_aqi_service.py,sha256=IT3S9zZmThdqPtJDBF_G8LDUNlwUYoBh49z_WUlB-tk,1285
55
+ weathergrabber/service/extract_aqi_service.py,sha256=WKxNvZoLRE-_vmTMR4efWNrowkc8cfo28fVRJ1XmQ1c,1090
56
56
  weathergrabber/service/extract_current_conditions_service.py,sha256=R4rd-_53HOQ7YqhHdfI_gKrIy27scYkNEmLgqS23f40,1838
57
57
  weathergrabber/service/extract_daily_forecast_oldstyle_service.py,sha256=DiKfaGbMnIIBnxI-IYG8HmfLHNAW5AGew5NjFSMqBk4,2190
58
58
  weathergrabber/service/extract_daily_forecast_service.py,sha256=ZN61_neEAdn7RPedLvmmFKH7Pq1EQGhG9A9Dy8o09zM,2709
59
59
  weathergrabber/service/extract_health_activities_service.py,sha256=2qJ4tEEz5uV6EwHUIFhWM8zol_BbDW3omEohf8kDg3M,1014
60
60
  weathergrabber/service/extract_hourly_forecast_oldstyle_service.py,sha256=U5O2tG9YF8wrUUoTimDr7oqMIb670j9oeVjoIpCKKLs,2100
61
- weathergrabber/service/extract_hourly_forecast_service.py,sha256=MhVweMlvJNQjPdVqw0AAvni3f8TVTq_0PD76NDUUQHM,3079
61
+ weathergrabber/service/extract_hourly_forecast_service.py,sha256=KFsl6G6FAmGtkski3lj31VIhs6guP0BxVZTGQSHk8Do,2757
62
62
  weathergrabber/service/extract_temperature_service.py,sha256=46nRO3Izj1QmG4BNTh8flsODsovHyWPzZzOnkl1Gbj4,634
63
- weathergrabber/service/extract_today_details_service.py,sha256=QAwF7EzVaL1STGNDyxve9r7oQTjvNKhYQ53d_68lbKA,4126
63
+ weathergrabber/service/extract_today_details_service.py,sha256=VBsyv-W9y52fXbutb9ETI04zCpJrxJzXBHmKoHTCb80,3743
64
64
  weathergrabber/service/read_weather_service.py,sha256=7_B8E9IN1KCwOhpuS5PfWazI1sCrDyYrZhkV2R38bhc,649
65
65
  weathergrabber/service/search_location_service.py,sha256=tZmVgO45hjwoa4cl5bKPjMBmYlGxJiH_I9Ymb5pwEwU,1422
66
- weathergrabber/usecase/use_case.py,sha256=K2RBIEi7S0ixAM6oDKmCPYkuJ9azatxle8zAqX0xM1E,4635
67
- weathergrabber-0.0.8b2.dist-info/licenses/LICENSE,sha256=X5JFljoqN43yFwpMLudQ9rtty4K_FeZfnz3v8Yhw23Q,1067
68
- weathergrabber-0.0.8b2.dist-info/METADATA,sha256=GPhdo6t_JisDkKPSjkbIa7h4iMTx9R84Zzxn8gvL5kw,5801
69
- weathergrabber-0.0.8b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
70
- weathergrabber-0.0.8b2.dist-info/entry_points.txt,sha256=m2P9a4mrJDTzuNaiTU438NA60GxCfaw7VKvruWw43N8,63
71
- weathergrabber-0.0.8b2.dist-info/top_level.txt,sha256=P3NMDJJYRIvQujf994Vb4gZrobkKWkL2gh3NF_ajQWM,15
72
- weathergrabber-0.0.8b2.dist-info/RECORD,,
66
+ weathergrabber/usecase/use_case.py,sha256=OM36GtGlzotgRJqSu_3kaM3F3WaSQsUs90DMHQdWPsk,4627
67
+ weathergrabber-0.0.8b4.dist-info/licenses/LICENSE,sha256=X5JFljoqN43yFwpMLudQ9rtty4K_FeZfnz3v8Yhw23Q,1067
68
+ weathergrabber-0.0.8b4.dist-info/METADATA,sha256=XbMK4_xR4lNo79Hy-YFiQe78KXtpkj20FXiknIQZC9A,5801
69
+ weathergrabber-0.0.8b4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
70
+ weathergrabber-0.0.8b4.dist-info/entry_points.txt,sha256=m2P9a4mrJDTzuNaiTU438NA60GxCfaw7VKvruWw43N8,63
71
+ weathergrabber-0.0.8b4.dist-info/top_level.txt,sha256=P3NMDJJYRIvQujf994Vb4gZrobkKWkL2gh3NF_ajQWM,15
72
+ weathergrabber-0.0.8b4.dist-info/RECORD,,