diff --git a/src/api.py b/src/api.py index 289d654..eac34bb 100644 --- a/src/api.py +++ b/src/api.py @@ -188,9 +188,9 @@ def ocean_information( "latitude": lat, "longitude": long, "current": [ - "wave_height", - "wave_direction", - "wave_period", + "swell_wave_height", + "swell_wave_direction", + "swell_wave_period", "sea_surface_temperature", ], "length_unit": unit, @@ -210,15 +210,15 @@ def ocean_information( current = response.Current() if current is None: return [0, 0, 0, 0] - current_wave_height = round(current.Variables(0).Value(), decimal) - current_wave_direction = round(current.Variables(1).Value(), decimal) - current_wave_period = round(current.Variables(2).Value(), decimal) + current_swell_wave_height = round(current.Variables(0).Value(), decimal) + current_swell_wave_direction = round(current.Variables(1).Value(), decimal) + current_swell_wave_period = round(current.Variables(2).Value(), decimal) current_sea_surface_temperature = current.Variables(3).Value() return [ - current_wave_height, - current_wave_direction, - current_wave_period, + current_swell_wave_height, + current_swell_wave_direction, + current_swell_wave_period, current_sea_surface_temperature, ] @@ -261,7 +261,11 @@ def ocean_information_history( params = { "latitude": lat, "longitude": long, - "hourly": ["wave_height", "wave_direction", "wave_period"], + "hourly": [ + "swell_wave_height", + "swell_wave_direction", + "swell_wave_period", + ], "length_unit": unit, "timezone": "auto", "start_date": formatted_date_one_year_ago, @@ -280,15 +284,15 @@ def ocean_information_history( # Extract hourly values for the specified metrics hourly = response.Hourly() - hourly_wave_height = hourly.Variables(0).ValuesAsNumpy() - hourly_wave_direction = hourly.Variables(1).ValuesAsNumpy() - hourly_wave_period = hourly.Variables(2).ValuesAsNumpy() + hourly_swell_wave_height = hourly.Variables(0).ValuesAsNumpy() + hourly_swell_wave_direction = hourly.Variables(1).ValuesAsNumpy() + hourly_swell_wave_period = hourly.Variables(2).ValuesAsNumpy() # Retrieve data for the current hour from one year ago return [ - f"{hourly_wave_height[current_hour]:.{decimal}f}", - f"{hourly_wave_direction[current_hour]:.{decimal}f}", - f"{hourly_wave_period[current_hour]:.{decimal}f}", + f"{hourly_swell_wave_height[current_hour]:.{decimal}f}", + f"{hourly_swell_wave_direction[current_hour]:.{decimal}f}", + f"{hourly_swell_wave_period[current_hour]:.{decimal}f}", ] @@ -368,9 +372,9 @@ def forecast(lat: float, long: float, decimal: int, days: int = 0) -> dict: "latitude": lat, "longitude": long, "daily": [ - "wave_height_max", - "wave_direction_dominant", - "wave_period_max", + "swell_wave_height_max", + "swell_wave_direction_dominant", + "swell_wave_period_max", ], "length_unit": "imperial", "timezone": "auto", @@ -438,9 +442,9 @@ def forecast(lat: float, long: float, decimal: int, days: int = 0) -> dict: forecast_data = { "date": daily_data["date"], - "wave_height_max": marine_data[0], - "wave_direction_dominant": marine_data[1], - "wave_period_max": marine_data[2], + "swell_wave_height_max": marine_data[0], + "swell_wave_direction_dominant": marine_data[1], + "swell_wave_period_max": marine_data[2], "uv_index_max": general_data[0], "temperature_2m_max": general_data[1], "temperature_2m_min": general_data[2], diff --git a/src/art.py b/src/art.py index db33704..1309426 100644 --- a/src/art.py +++ b/src/art.py @@ -2,7 +2,9 @@ All ASCII art in this file """ +import codecs import logging +import sys logger = logging.getLogger(__name__) @@ -71,9 +73,34 @@ def print_wave(show_wave, show_large_wave, color): color = "blue" if show_large_wave: - print( - colors[color] - + """ + encoding = getattr(sys.stdout, "encoding", None) or "" + try: + is_utf8 = codecs.lookup(encoding).name == "utf-8" + except LookupError: + is_utf8 = False + + if sys.platform == "win32" and not is_utf8: + print( + colors[color] + + r""" + _ + _//_ + _///__`-. + _////__ \ + _/////__ \ + _//////__ | + _///////__ | + _////////__ / + _/////////__ .-' + _//////////__ .-' + ///////////_______/ +""" + + colors["end"] + ) + else: + print( + colors[color] + + """ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣶⠾⠿⠿⠯⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣾⠛⠁⠀⠀⠀⠀⠀⠀⠈⢻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⠿⠁⠀⠀⠀⢀⣤⣾⣟⣛⣛⣶⣬⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -84,8 +111,8 @@ def print_wave(show_wave, show_large_wave, color): ⢀⣄⣠⣶⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠓⠚⠋⠉⠀⠀⠀⠀⠀⠀⠈⠛⡛⡻⠿⠿⠙⠓⢒⣺⡿⠋⠁ ⠉⠉⠉⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠁⠀ """ - + colors["end"] - ) + + colors["end"] + ) elif show_wave: print( colors[color] diff --git a/src/cli.py b/src/cli.py index 064baa3..b182031 100644 --- a/src/cli.py +++ b/src/cli.py @@ -103,32 +103,30 @@ def _build_args_string(ns): tokens.append("metric") if ns.imperial: tokens.append("imperial") - flag_map = { - "json": "json", - "gpt": "gpt", - "hide_wave": "hide_wave", - "hide_uv": "hide_uv", - "hide_height": "hide_height", - "hide_direction": "hide_direction", - "hide_period": "hide_period", - "hide_location": "hide_location", - "hide_date": "hide_date", - "show_large_wave": "show_large_wave", - "show_past_uv": "show_past_uv", - "show_height_history": "show_height_history", - "show_direction_history": "show_direction_history", - "show_period_history": "show_period_history", - "show_air_temp": "show_air_temp", - "show_wind_speed": "show_wind_speed", - "show_wind_direction": "show_wind_direction", - "show_rain_sum": "show_rain_sum", - "show_precipitation_prob": "show_precipitation_prob", - "show_cloud_cover": "show_cloud_cover", - "show_visibility": "show_visibility", - } - for attr, token in flag_map.items(): - if getattr(ns, attr, False): - tokens.append(token) + flags = [ + "json", + "gpt", + "hide_wave", + "hide_uv", + "hide_height", + "hide_direction", + "hide_period", + "hide_location", + "hide_date", + "show_large_wave", + "show_past_uv", + "show_height_history", + "show_direction_history", + "show_period_history", + "show_air_temp", + "show_wind_speed", + "show_wind_direction", + "show_rain_sum", + "show_precipitation_prob", + "show_cloud_cover", + "show_visibility", + ] + tokens.extend([flag for flag in flags if getattr(ns, flag, False)]) return ",".join(tokens) diff --git a/src/helper.py b/src/helper.py index cf2b59c..a1d7a0b 100644 --- a/src/helper.py +++ b/src/helper.py @@ -203,6 +203,16 @@ def print_location(city, show_city): print("\n") +def _print_mapped_data(mappings, arguments_dict, data_dict): + """ + Helper function to print mapped data from a dictionary + if the argument is set. + """ + for arg_key, data_key, label in mappings: + if arguments_dict[arg_key] and data_key in data_dict: + print(f"{label}{data_dict[data_key]}") + + def print_ocean_data(arguments_dict, ocean_data_dict): """ Prints ocean data (height, wave direction, period, etc). @@ -242,9 +252,7 @@ def print_ocean_data(arguments_dict, ocean_data_dict): ("show_sea_temp", "Sea Surface Temperature", "Sea Surface Temp: "), ] - for arg_key, data_key, label in mappings: - if arguments_dict.get(arg_key) and data_key in ocean_data_dict: - print(f"{label}{ocean_data_dict[data_key]}") + _print_mapped_data(mappings, arguments_dict, ocean_data_dict) if arguments_dict.get("show_tide") and ocean_data_dict.get("Tide"): tide = ocean_data_dict["Tide"] @@ -272,9 +280,13 @@ def print_forecast(ocean, forecast): mappings = [ ("show_date", "date", "Date: "), ("show_uv", "uv_index_max", "UV Index: "), - ("show_height", "wave_height_max", "Wave Height: "), - ("show_direction", "wave_direction_dominant", "Wave Direction: "), - ("show_period", "wave_period_max", "Wave Period: "), + ("show_height", "swell_wave_height_max", "Wave Height: "), + ( + "show_direction", + "swell_wave_direction_dominant", + "Wave Direction: ", + ), + ("show_period", "swell_wave_period_max", "Wave Period: "), ("show_air_temp", "temperature_2m_max", "Air Temp Max: "), ("show_air_temp", "temperature_2m_min", "Air Temp Min: "), ("show_rain_sum", "rain_sum", "Rain Sum: "), @@ -292,14 +304,18 @@ def print_forecast(ocean, forecast): ] for day in range(ocean["forecast_days"]): - for arg_key, data_key, label in mappings: - if ocean[arg_key]: + # Extract day's data into a temporary dictionary + day_data = {} + for _, data_key, _ in mappings: + if data_key in forecast: try: - data = forecast[data_key][day] - formatted = round(float(data), ocean["decimal"]) - print(f"{label}{formatted}") + raw = forecast[data_key][day] + formatted = round(float(raw), ocean["decimal"]) except TypeError: - print(f"{label}{forecast[data_key][day]}") + formatted = forecast[data_key][day] + day_data[data_key] = formatted + + _print_mapped_data(mappings, ocean, day_data) print("\n") @@ -369,13 +385,14 @@ def forecast_to_json(forecast_data, decimal): forecast = { "date": str(date.date()), "surf height": round( - float(forecast_data["wave_height_max"][i]), decimal + float(forecast_data["swell_wave_height_max"][i]), decimal ), "swell direction": round( - float(forecast_data["wave_direction_dominant"][i]), decimal + float(forecast_data["swell_wave_direction_dominant"][i]), + decimal, ), "swell period": round( - float(forecast_data["wave_period_max"][i]), decimal + float(forecast_data["swell_wave_period_max"][i]), decimal ), "uv index": round( float(forecast_data["uv_index_max"][i]), decimal diff --git a/tests/test_api.py b/tests/test_api.py index a90a63d..dac30fa 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -178,9 +178,9 @@ def test_forecast(mock_create_client): FORECAST_LENGTH = 7 - assert len(fc["wave_height_max"]) == FORECAST_LENGTH - assert len(fc["wave_direction_dominant"]) == FORECAST_LENGTH - assert len(fc["wave_period_max"]) == FORECAST_LENGTH + assert len(fc["swell_wave_height_max"]) == FORECAST_LENGTH + assert len(fc["swell_wave_direction_dominant"]) == FORECAST_LENGTH + assert len(fc["swell_wave_period_max"]) == FORECAST_LENGTH @patch("src.api.ocean_information", return_value=[3.5, 180.0, 12.0, 20.0]) diff --git a/tests/test_cli.py b/tests/test_cli.py index c7c26e4..e16b877 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -5,7 +5,12 @@ import logging from unittest.mock import Mock -from src.cli import SurfReport, run +from src.cli import ( + SurfReport, + _build_args_string, # noqa: PLC2701 + cli_main, + run, +) from src.helper import DEFAULT_ARGUMENTS _LAT = 10.0 @@ -190,3 +195,130 @@ def test_module_run_delegates_to_surf_report(mocker): lat=1.0, long=2.0, args=["placeholder", "json"] ) assert result == {"ocean": "data"} + + +def test_build_args_string(): + + class DummyNamespace: + def __init__(self, **kwargs): + self.location = kwargs.get("location", None) + self.forecast = kwargs.get("forecast", None) + self.decimal = kwargs.get("decimal", None) + self.color = kwargs.get("color", None) + self.metric = kwargs.get("metric", False) + self.imperial = kwargs.get("imperial", False) + self.json = kwargs.get("json", False) + self.gpt = kwargs.get("gpt", False) + self.hide_wave = kwargs.get("hide_wave", False) + self.hide_uv = kwargs.get("hide_uv", False) + self.hide_height = kwargs.get("hide_height", False) + self.hide_direction = kwargs.get("hide_direction", False) + self.hide_period = kwargs.get("hide_period", False) + self.hide_location = kwargs.get("hide_location", False) + self.hide_date = kwargs.get("hide_date", False) + self.show_large_wave = kwargs.get("show_large_wave", False) + self.show_past_uv = kwargs.get("show_past_uv", False) + self.show_height_history = kwargs.get("show_height_history", False) + self.show_direction_history = kwargs.get( + "show_direction_history", False + ) + self.show_period_history = kwargs.get("show_period_history", False) + self.show_air_temp = kwargs.get("show_air_temp", False) + self.show_wind_speed = kwargs.get("show_wind_speed", False) + self.show_wind_direction = kwargs.get("show_wind_direction", False) + self.show_rain_sum = kwargs.get("show_rain_sum", False) + self.show_precipitation_prob = kwargs.get( + "show_precipitation_prob", False + ) + self.show_cloud_cover = kwargs.get("show_cloud_cover", False) + self.show_visibility = kwargs.get("show_visibility", False) + + ns = DummyNamespace( + location="San Francisco", + forecast=3, + decimal=2, + color="red", + metric=True, + json=True, + show_large_wave=True, + ) + res = _build_args_string(ns) + assert "location=San_Francisco" in res + assert "forecast=3" in res + assert "decimal=2" in res + assert "color=red" in res + assert "metric" in res + assert "json" in res + assert "show_large_wave" in res + + all_true_kwargs = { + "location": "A", + "forecast": 1, + "decimal": 0, + "color": "blue", + "metric": True, + "imperial": True, + "json": True, + "gpt": True, + "hide_wave": True, + "hide_uv": True, + "hide_height": True, + "hide_direction": True, + "hide_period": True, + "hide_location": True, + "hide_date": True, + "show_large_wave": True, + "show_past_uv": True, + "show_height_history": True, + "show_direction_history": True, + "show_period_history": True, + "show_air_temp": True, + "show_wind_speed": True, + "show_wind_direction": True, + "show_rain_sum": True, + "show_precipitation_prob": True, + "show_cloud_cover": True, + "show_visibility": True, + } + ns_all = DummyNamespace(**all_true_kwargs) + res_all = _build_args_string(ns_all) + for flag in all_true_kwargs.keys(): + if flag == "location": + assert "location=A" in res_all + elif flag in {"forecast", "decimal", "color"}: + assert f"{flag}=" in res_all + else: + assert flag in res_all + + +def test_cli_main(mocker): + mocker.patch( + "sys.argv", + [ + "surf", + "--location", + "Santa Cruz", + "--forecast", + "3", + "--decimal", + "1", + "--color", + "blue", + "--metric", + "--json", + "--show-large-wave", + ], + ) + mock_run = mocker.patch("src.cli.run") + cli_main() + mock_run.assert_called_once() + _, kwargs = mock_run.call_args + assert "args" in kwargs + args_str = kwargs["args"] + assert "location=Santa_Cruz" in args_str + assert "forecast=3" in args_str + assert "decimal=1" in args_str + assert "color=blue" in args_str + assert "metric" in args_str + assert "json" in args_str + assert "show_large_wave" in args_str diff --git a/tests/test_helper.py b/tests/test_helper.py index 9ab35bd..437df93 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -253,6 +253,7 @@ def test_print_ocean_data_prints_enabled_fields(capsys): "show_precipitation_prob": False, "show_cloud_cover": False, "show_visibility": False, + "show_sea_temp": False, } ocean_data_dict = {"UV Index": 5, "Height": 3.5} helper.print_ocean_data(arguments_dict, ocean_data_dict) @@ -282,7 +283,7 @@ def test_print_forecast_renders_float_values(capsys): "show_wind_speed": False, "show_wind_direction": False, } - forecast = {"wave_height_max": [3.567]} + forecast = {"swell_wave_height_max": [3.567]} helper.print_forecast(ocean, forecast) assert "Wave Height: 3.6" in capsys.readouterr().out diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..a5bc514 --- /dev/null +++ b/uv.lock @@ -0,0 +1,3 @@ +version = 1 +revision = 3 +requires-python = ">=3.14"