diff --git a/i3pystatus/scores/nba.py b/i3pystatus/scores/nba.py index 672d9c9..df7f131 100644 --- a/i3pystatus/scores/nba.py +++ b/i3pystatus/scores/nba.py @@ -112,15 +112,9 @@ class NBA(ScoresBackend): 'exists primarily for troubleshooting purposes.'), ('live_url', 'URL string to launch NBA Game Tracker. This value ' 'should not need to be changed.'), - ('scoreboard_url', 'Link to the NBA.com scoreboard page. Like ' - '**live_url**, this value should not need to be ' - 'changed.'), ('api_url', 'Alternate URL string from which to retrieve score data. ' 'Like, **live_url**, this value should not need to be ' 'changed.'), - ('standings_url', 'Alternate URL string from which to retrieve team ' - 'standings. Like **live_url**, this value should ' - 'not need to be changed.'), ) required = () diff --git a/i3pystatus/weather/__init__.py b/i3pystatus/weather/__init__.py index 405e4c5..5540c7c 100644 --- a/i3pystatus/weather/__init__.py +++ b/i3pystatus/weather/__init__.py @@ -24,30 +24,26 @@ class WeatherBackend(SettingsBase): @require(internet) def api_request(self, url, headers=None): - self.logger.debug('Making API request to %s', url) + self.logger.debug(f'Making API request to {url}') try: response_json = self.http_request(url, headers=headers).strip() if not response_json: - self.logger.debug('JSON response from %s was blank', url) + self.logger.debug(f'JSON response from {url} was blank') return {} try: response = json.loads(response_json) except json.decoder.JSONDecodeError as exc: - self.logger.error('Error loading JSON: %s', exc) - self.logger.debug('JSON text that failed to load: %s', - response_json) + self.logger.error(f'Error loading JSON: {exc}') + self.logger.debug(f'JSON text that failed to load: {response_json}') return {} - self.logger.log(5, 'API response: %s', response) + self.logger.log(5, f'API response: {response}') error = self.check_response(response) if error: - self.logger.error('Error in JSON response: %s', error) + self.logger.error(f'Error in JSON response: {error}') return {} return response except Exception as exc: - self.logger.error( - 'Failed to make API request to %s. Exception follows:', url, - exc_info=True - ) + self.logger.exception(f'Failed to make API request to {url}') return {} def check_response(self, response): @@ -194,9 +190,9 @@ class Weather(IntervalModule): on_leftclick = ['check_weather'] def launch_web(self): - if self.backend.forecast_url and self.backend.forecast_url != 'N/A': - self.logger.debug('Launching %s in browser', self.backend.forecast_url) - user_open(self.backend.forecast_url) + if self.backend.conditions_url and self.backend.conditions_url != 'N/A': + self.logger.debug(f'Launching {self.backend.conditions_url} in browser') + user_open(self.backend.conditions_url) def init(self): if self.online_interval is None: @@ -296,7 +292,7 @@ class Weather(IntervalModule): else self.color_icons[condition] def refresh_display(self): - self.logger.debug('Weather data: %s', self.backend.data) + self.logger.debug(f'Weather data: {self.backend.data}') self.backend.data['icon'], condition_color = \ self.get_color_data(self.backend.data['condition']) color = condition_color if self.colorize else self.color diff --git a/i3pystatus/weather/weathercom.py b/i3pystatus/weather/weathercom.py index d8c7fa9..b0b3eba 100644 --- a/i3pystatus/weather/weathercom.py +++ b/i3pystatus/weather/weathercom.py @@ -21,7 +21,7 @@ class WeathercomHTMLParser(HTMLParser): super(WeathercomHTMLParser, self).__init__() def get_weather_data(self, url): - self.logger.debug('Making request to %s to retrieve weather data', url) + self.logger.debug(f'Making request to {url} to retrieve weather data') self.weather_data = None req = Request(url, headers={'User-Agent': self.user_agent}) with urlopen(req) as content: @@ -40,12 +40,12 @@ class WeathercomHTMLParser(HTMLParser): ) def load_json(self, json_input): - self.logger.debug('Loading the following data as JSON: %s', json_input) + self.logger.debug(f'Loading the following data as JSON: {json_input}') try: return json.loads(json_input) except json.decoder.JSONDecodeError as exc: - self.logger.debug('Error loading JSON: %s', exc) - self.logger.debug('String that failed to load: %s', json_input) + self.logger.debug(f'Error loading JSON: {exc}') + self.logger.debug(f'String that failed to load: {json_input}') return None def handle_data(self, content): @@ -84,7 +84,7 @@ class WeathercomHTMLParser(HTMLParser): if weather_data is None: self.logger.debug( 'Failed to locate weather data in the ' - 'following data: %s', json_data + f'following data: {json_data}' ) else: self.weather_data = weather_data @@ -144,7 +144,7 @@ class Weathercom(WeatherBackend): url_template = 'https://weather.com/{locale}/weather/today/l/{location_code}' # This will be set in the init based on the passed location code - forecast_url = None + conditions_url = None def init(self): if self.location_code is not None: @@ -156,7 +156,7 @@ class Weathercom(WeatherBackend): # causes weather.com to return the default, which is imperial. self.locale = 'en-CA' if self.units == 'metric' else '' - self.forecast_url = self.url_template.format(**vars(self)) + self.conditions_url = self.url_template.format(**vars(self)) self.parser = WeathercomHTMLParser(self.logger) @require(internet) @@ -178,7 +178,7 @@ class Weathercom(WeatherBackend): self.data['update_error'] = '' try: - self.parser.get_weather_data(self.forecast_url) + self.parser.get_weather_data(self.conditions_url) if self.parser.weather_data is None: self.logger.error( 'Failed to read weather data from page. Run module with ' @@ -187,7 +187,7 @@ class Weathercom(WeatherBackend): self.data['update_error'] = self.update_error return - self.logger.debug('Parsed weather data: %s', self.parser.weather_data) + self.logger.debug(f'Parsed weather data: {self.parser.weather_data}') try: observed = self.parser.weather_data['getSunV3CurrentObservationsUrlConfig'] # Observation data stored under a sub-key containing the @@ -228,7 +228,7 @@ class Weathercom(WeatherBackend): except KeyError: self.logger.warning( 'Failed to get city name from API response, falling back ' - 'to location code \'%s\'', self.location_code + f'to location code {self.location_code}' ) self.city_name = self.location_code @@ -295,8 +295,5 @@ class Weathercom(WeatherBackend): self.data['uv_index'] = str(observed.get('uvIndex', '')) except Exception: # Don't let an uncaught exception kill the update thread - self.logger.error( - 'Uncaught error occurred while checking weather. ' - 'Exception follows:', exc_info=True - ) + self.logger.exception('Uncaught error occurred while checking weather') self.data['update_error'] = self.update_error diff --git a/i3pystatus/weather/wunderground.py b/i3pystatus/weather/wunderground.py index 3a36332..565f549 100644 --- a/i3pystatus/weather/wunderground.py +++ b/i3pystatus/weather/wunderground.py @@ -73,18 +73,15 @@ class Wunderground(WeatherBackend): units = 'metric' update_error = '!' - url_template = 'https://www.wunderground.com/dashboard/pws/{location_code}' + # Will be set in the init func + conditions_url = None - # This will be set in the init based on the passed location code - forecast_url = None - - summary_url = 'https://api.weather.com/v2/pws/dailysummary/1day?apiKey={api_key}&stationId={location_code}&format=json&units={units_type}' + forecast_url = 'https://api.weather.com/v3/wx/forecast/daily/7day?apiKey={api_key}&geocode={lat:.2f}%2C{lon:.2f}&language=en-US&units={units_type}&format=json' observation_url = 'https://api.weather.com/v2/pws/observations/current?apiKey={api_key}&stationId={location_code}&format=json&units={units_type}' - overview_url = 'https://api.weather.com/v3/aggcommon/v3alertsHeadlines;v3-wx-observations-current;v3-location-point?apiKey={api_key}&geocodes={lat:.2f}%2C{lon:.2f}&language=en-US&units=e&format=json' + overview_url = 'https://api.weather.com/v3/aggcommon/v3alertsHeadlines;v3-wx-observations-current;v3-location-point?apiKey={api_key}&geocodes={lat:.2f}%2C{lon:.2f}&language=en-US&units={units_type}&format=json' headers = { - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0', - 'Referer': 'https://www.wunderground.com/dashboard/pws/{location_code}', + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0', 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'en-US,en;q=0.5', 'Connection': 'keep-alive', @@ -92,7 +89,7 @@ class Wunderground(WeatherBackend): def init(self): self.units_type = 'm' if self.units == 'metric' else 'e' - self.forecast_url = self.url_template.format(**vars(self)) + self.headers['Referer'] = self.conditions_url = f'https://www.wunderground.com/weather/{self.location_code}' @require(internet) def get_api_key(self): @@ -110,7 +107,7 @@ class Wunderground(WeatherBackend): }, ) except Exception as exc: - self.logger.exception('Failed to load %s', url) + self.logger.exception(f'Failed to load {url}') else: try: return re.search(r'apiKey=([0-9a-f]+)', page_source).group(1) @@ -138,16 +135,6 @@ class Wunderground(WeatherBackend): self.data['update_error'] = '' try: - try: - summary = self.api_request(self.summary_url.format(**vars(self)))['summaries'][0] - except (IndexError, KeyError): - self.logger.error( - 'Failed to retrieve summary data from API response. ' - 'Run module with debug logging to get more information.' - ) - self.data['update_error'] = self.update_error - return - try: observation = self.api_request(self.observation_url.format(**vars(self)))['observations'][0] except (IndexError, KeyError): @@ -161,6 +148,8 @@ class Wunderground(WeatherBackend): self.lat = observation['lat'] self.lon = observation['lon'] + forecast = self.api_request(self.forecast_url.format(**vars(self))) + try: overview = self.api_request(self.overview_url.format(**vars(self)))[0] except IndexError: @@ -191,13 +180,27 @@ class Wunderground(WeatherBackend): def _find(path, data, default=''): ptr = data - try: - for item in path.split(':'): - if item == 'units': - item = self.units + for item in path.split(':'): + if item == 'units': + item = self.units + # self.logger.debug(f'item = {item}') + try: ptr = ptr[item] - except (KeyError, IndexError, TypeError): - return default + except (KeyError, TypeError): + try: + # Try list index + int_item = int(item) + except (TypeError, ValueError): + return default + else: + if len(item) == len(str(int_item)): + try: + ptr = ptr[int_item] + continue + except IndexError: + return default + else: + return default return str(ptr) pressure_tendency = _find( @@ -209,8 +212,8 @@ class Wunderground(WeatherBackend): self.data['condition'] = _find('v3-wx-observations-current:wxPhraseMedium', overview) self.data['observation_time'] = observation_time self.data['current_temp'] = _find('units:temp', observation, '0') - self.data['low_temp'] = _find('units:tempLow', summary) - self.data['high_temp'] = _find('units:tempHigh', summary) + self.data['low_temp'] = _find('calendarDayTemperatureMin:0', forecast) + self.data['high_temp'] = _find('calendarDayTemperatureMax:0', forecast) self.data['temp_unit'] = temp_unit self.data['feelslike'] = _find('units:heatIndex', observation) self.data['dewpoint'] = _find('units:dewpt', observation)