diff --git a/app.py b/app.py index 26ec962..36f7b72 100644 --- a/app.py +++ b/app.py @@ -45,7 +45,7 @@ @app.route(f"{base_path}/rainfall/average") @swag_from(average_specs.route_specs) def average_rainfall() -> Response: - params: tuple = parse_args( + params = parse_args( request.args, param.time_mode, param.begin_year, @@ -54,9 +54,7 @@ def average_rainfall() -> Response: param.season, ) - to_return: Response | dict = manage_time_mode_errors( - {}, params[0], params[3], params[4] - ) + to_return = manage_time_mode_errors({}, params[0], params[3], params[4]) if isinstance(to_return, Response): return to_return @@ -65,9 +63,7 @@ def average_rainfall() -> Response: "name": "average rainfall (mm)", "value": all_rainfall.get_average_rainfall(*params), "begin_year": params[1], - "end_year": params[2] - if params[2] is not None - else all_rainfall.get_last_year(), + "end_year": params[2] or all_rainfall.get_last_year(), "time_mode": TimeMode[params[0]], } ) @@ -78,13 +74,11 @@ def average_rainfall() -> Response: @app.route(f"{base_path}/rainfall/normal") @swag_from(normal_specs.route_specs) def normal_rainfall() -> Response: - params: tuple = parse_args( + params = parse_args( request.args, param.time_mode, param.begin_year, param.month, param.season ) - to_return: Response | dict = manage_time_mode_errors( - {}, params[0], params[2], params[3] - ) + to_return = manage_time_mode_errors({}, params[0], params[2], params[3]) if isinstance(to_return, Response): return to_return @@ -104,7 +98,7 @@ def normal_rainfall() -> Response: @app.route(f"{base_path}/rainfall/relative_distance_to_normal") @swag_from(relative_distance_to_normal_specs.route_specs) def rainfall_relative_distance_to_normal() -> Response: - params: tuple = parse_args( + params = parse_args( request.args, param.time_mode, param.normal_year, @@ -114,9 +108,7 @@ def rainfall_relative_distance_to_normal() -> Response: param.season, ) - to_return: Response | dict = manage_time_mode_errors( - {}, params[0], params[4], params[5] - ) + to_return = manage_time_mode_errors({}, params[0], params[4], params[5]) if isinstance(to_return, Response): return to_return @@ -126,9 +118,7 @@ def rainfall_relative_distance_to_normal() -> Response: "value": all_rainfall.get_relative_distance_from_normal(*params), "normal_year": params[1], "begin_year": params[2], - "end_year": params[3] - if params[3] is not None - else all_rainfall.get_last_year(), + "end_year": params[3] or all_rainfall.get_last_year(), "time_mode": TimeMode[params[0]], } ) @@ -139,7 +129,7 @@ def rainfall_relative_distance_to_normal() -> Response: @app.route(f"{base_path}/rainfall/standard_deviation") @swag_from(standard_deviation_specs.route_specs) def rainfall_standard_deviation() -> Response: - params: tuple = parse_args( + params = parse_args( request.args, param.time_mode, param.begin_year, @@ -148,9 +138,7 @@ def rainfall_standard_deviation() -> Response: param.season, ) - to_return: Response | dict = manage_time_mode_errors( - {}, params[0], params[3], params[4] - ) + to_return = manage_time_mode_errors({}, params[0], params[3], params[4]) if isinstance(to_return, Response): return to_return @@ -159,9 +147,7 @@ def rainfall_standard_deviation() -> Response: "name": "rainfall standard deviation (mm)", "value": all_rainfall.get_rainfall_standard_deviation(*params), "begin_year": params[1], - "end_year": params[2] - if params[2] is not None - else all_rainfall.get_last_year(), + "end_year": params[2] or all_rainfall.get_last_year(), "time_mode": TimeMode[params[0]], } ) @@ -172,7 +158,7 @@ def rainfall_standard_deviation() -> Response: @app.route(f"{base_path}/year/below_normal") @swag_from(below_normal_specs.route_specs) def years_below_normal() -> Response: - params: tuple = parse_args( + params = parse_args( request.args, param.time_mode, param.normal_year, @@ -182,9 +168,7 @@ def years_below_normal() -> Response: param.season, ) - to_return: Response | dict = manage_time_mode_errors( - {}, params[0], params[4], params[5] - ) + to_return = manage_time_mode_errors({}, params[0], params[4], params[5]) if isinstance(to_return, Response): return to_return @@ -194,9 +178,7 @@ def years_below_normal() -> Response: "value": all_rainfall.get_years_below_normal(*params), "normal_year": params[1], "begin_year": params[2], - "end_year": params[3] - if params[3] is not None - else all_rainfall.get_last_year(), + "end_year": params[3] or all_rainfall.get_last_year(), "time_mode": TimeMode[params[0]], } ) @@ -207,7 +189,7 @@ def years_below_normal() -> Response: @app.route(f"{base_path}/year/above_normal") @swag_from(above_normal_specs.route_specs) def years_above_normal() -> Response: - params: tuple = parse_args( + params = parse_args( request.args, param.time_mode, param.normal_year, @@ -217,9 +199,7 @@ def years_above_normal() -> Response: param.season, ) - to_return: Response | dict = manage_time_mode_errors( - {}, params[0], params[4], params[5] - ) + to_return = manage_time_mode_errors({}, params[0], params[4], params[5]) if isinstance(to_return, Response): return to_return @@ -229,9 +209,7 @@ def years_above_normal() -> Response: "value": all_rainfall.get_years_above_normal(*params), "normal_year": params[1], "begin_year": params[2], - "end_year": params[3] - if params[3] is not None - else all_rainfall.get_last_year(), + "end_year": params[3] or all_rainfall.get_last_year(), "time_mode": TimeMode[params[0]], } ) @@ -242,13 +220,11 @@ def years_above_normal() -> Response: @app.route(f"{base_path}/csv/minimal_csv") @swag_from(minimal_csv_specs.route_specs) def minimal_csv() -> Response: - params: tuple = parse_args( + params = parse_args( request.args, param.time_mode, param.month, param.season, param.file_name ) - error: Response | dict = manage_time_mode_errors( - {}, params[0], params[1], params[2] - ) + error = manage_time_mode_errors({}, params[0], params[1], params[2]) if isinstance(error, Response): return error @@ -260,9 +236,7 @@ def minimal_csv() -> Response: @app.route(f"{base_path}/graph/monthly_averages") @swag_from(monthly_averages_specs.route_specs) def monthly_averages() -> Response: - params: tuple = parse_args( - request.args, param.file_name, param.begin_year, param.end_year - ) + params = parse_args(request.args, param.file_name, param.begin_year, param.end_year) all_rainfall.bar_rainfall_averages(begin_year=params[1], end_year=params[2]) plt.savefig(params[0], format="svg") @@ -274,9 +248,7 @@ def monthly_averages() -> Response: @app.route(f"{base_path}/graph/seasonal_averages") @swag_from(seasonal_averages_specs.route_specs) def seasonal_averages() -> Response: - params: tuple = parse_args( - request.args, param.file_name, param.begin_year, param.end_year - ) + params = parse_args(request.args, param.file_name, param.begin_year, param.end_year) all_rainfall.bar_rainfall_averages( monthly=False, begin_year=params[1], end_year=params[2] diff --git a/coverage.svg b/coverage.svg index b6c4e36..ee07d4c 100644 --- a/coverage.svg +++ b/coverage.svg @@ -15,7 +15,7 @@ coverage coverage - 95% - 95% + 96% + 96% diff --git a/src/api/swagger/error_specs.py b/src/api/swagger/error_specs.py index 33048de..01c89e6 100644 --- a/src/api/swagger/error_specs.py +++ b/src/api/swagger/error_specs.py @@ -1,6 +1,8 @@ +from typing import Dict, Any + from src.api.schemas import ApiError -bad_request_specs: dict = { +bad_request_specs: Dict[str, Any] = { "description": 'Bad Request: error specific to API; check "message" field for information.', "schema": ApiError, } diff --git a/src/api/swagger/graph/monthly_averages_specs.py b/src/api/swagger/graph/monthly_averages_specs.py index 75ea077..7bbd9c3 100644 --- a/src/api/swagger/graph/monthly_averages_specs.py +++ b/src/api/swagger/graph/monthly_averages_specs.py @@ -10,6 +10,7 @@ route_specs: dict = { "operationId": "getMonthlyAverages", "summary": "Retrieve monthly averages of data as an SVG.", + "description": "If no end year is set, will default to most recent data year (usually last year).", "tags": ["Graph"], "responses": { "200": { diff --git a/src/api/swagger/graph/seasonal_averages_specs.py b/src/api/swagger/graph/seasonal_averages_specs.py index 8d2c3c4..9292b90 100644 --- a/src/api/swagger/graph/seasonal_averages_specs.py +++ b/src/api/swagger/graph/seasonal_averages_specs.py @@ -10,6 +10,7 @@ route_specs: dict = { "operationId": "getSeasonalAverages", "summary": "Retrieve seasonal averages of data as an SVG.", + "description": "If no end year is set, will default to most recent data year (usually last year).", "tags": ["Graph"], "responses": { "200": { diff --git a/src/api/swagger/parameters_specs.py b/src/api/swagger/parameters_specs.py index a9efff5..4e2f6e4 100644 --- a/src/api/swagger/parameters_specs.py +++ b/src/api/swagger/parameters_specs.py @@ -1,11 +1,13 @@ """ Swagger specifications for route parameters. """ +from typing import Dict, Any + from src.core.utils.enums.months import Month from src.core.utils.enums.seasons import Season from src.core.utils.enums.time_modes import TimeMode -normal_year: dict = { +normal_year: Dict[str, Any] = { "default": 1971, "required": True, "type": "integer", @@ -13,7 +15,7 @@ "in": "query", } -begin_year: dict = { +begin_year: Dict[str, Any] = { "default": 1991, "required": True, "type": "integer", @@ -21,7 +23,7 @@ "in": "query", } -end_year: dict = { +end_year: Dict[str, Any] = { "default": None, "required": False, "type": "integer", @@ -29,7 +31,7 @@ "in": "query", } -time_mode: dict = { +time_mode: Dict[str, Any] = { "default": TimeMode.YEARLY.name, "required": True, "type": "string", @@ -38,7 +40,7 @@ "in": "query", } -month: dict = { +month: Dict[str, Any] = { "default": None, "required": False, "type": "string", @@ -47,7 +49,7 @@ "in": "query", } -season: dict = { +season: Dict[str, Any] = { "default": None, "required": False, "type": "string", @@ -56,9 +58,9 @@ "in": "query", } -time_params: tuple = (time_mode, month, season) +time_params = (time_mode, month, season) -file_name: dict = { +file_name: Dict[str, Any] = { "default": None, "required": True, "type": "string", diff --git a/src/api/utils.py b/src/api/utils.py index 3de5f16..83f8d12 100644 --- a/src/api/utils.py +++ b/src/api/utils.py @@ -20,17 +20,15 @@ def parse_args(args: MultiDict[str, str], *params: dict) -> tuple: :param params: Desired parameters to parse as a list of swagger-conformed dicts. :return: Tuple containing retrieved values from query parameters. """ - to_return: list = [] - for param in params: - to_return.append( - args.get( - param["name"], - default=param["default"], - type=swagger_type_to_python_type(param["type"]), - ) - ) - return tuple(to_return) + return tuple( + args.get( + param["name"], + default=param["default"], + type=swagger_type_to_python_type(param["type"]), + ) + for param in params + ) def swagger_type_to_python_type(swagger_type: str) -> type | None: diff --git a/src/core/models/all_rainfall.py b/src/core/models/all_rainfall.py index 462a872..942a451 100644 --- a/src/core/models/all_rainfall.py +++ b/src/core/models/all_rainfall.py @@ -322,18 +322,22 @@ def get_last_year(self) -> int: def bar_rainfall_averages( self, - monthly: bool | None = True, + monthly=True, begin_year: int | None = None, end_year: int | None = None, ) -> list: """ Plots a bar graphic displaying average rainfall for each month or each season. - :param monthly: if True, plots monthly rainfall averages. - if False, plots seasonal rainfall averages. + :param monthly: If True, plots monthly rainfall averages. + If False, plots seasonal rainfall averages. Defaults to True (optional). + :param begin_year: An integer representing the year + to start getting our rainfall values. (optional). + :param end_year: An integer representing the year + to end getting our rainfall values (optional). :return: A list of the Rainfall averages for each month or season. """ - label: str = f"Average rainfall (mm) between {begin_year or self.starting_year} and {self.get_last_year()}" + label = f"Average rainfall (mm) between {begin_year or self.starting_year} and {self.get_last_year()}" if monthly: return plotting.bar_monthly_rainfall_averages( self.monthly_rainfalls, label, begin_year, end_year @@ -343,12 +347,12 @@ def bar_rainfall_averages( self.seasonal_rainfalls, label, begin_year, end_year ) - def bar_rainfall_linreg_slopes(self, monthly: bool | None = True) -> list: + def bar_rainfall_linreg_slopes(self, monthly=True) -> list: """ Plots a bar graphic displaying linear regression slope for each month or each season. :param monthly: if True, plots monthly rainfall LinReg slopes. - if False, plots seasonal rainfall LinReg slopes. + if False, plots seasonal rainfall LinReg slopes. Defaults to True (optional). :return: A list of the Rainfall LinReg slopes for each month or season. """ if monthly: diff --git a/src/core/models/yearly_rainfall.py b/src/core/models/yearly_rainfall.py index 3d12555..a78a9c0 100644 --- a/src/core/models/yearly_rainfall.py +++ b/src/core/models/yearly_rainfall.py @@ -205,14 +205,14 @@ def get_relative_distance_from_normal( if end_year is None: end_year = self.get_last_year() - gap: int = end_year - begin_year + 1 + gap = end_year - begin_year + 1 if gap == 0: return 0.0 - n_years_above_normal: int = self.get_years_above_normal( + n_years_above_normal = self.get_years_above_normal( normal_year, begin_year, end_year ) - n_years_below_normal: int = self.get_years_below_normal( + n_years_below_normal = self.get_years_below_normal( normal_year, begin_year, end_year ) @@ -261,7 +261,7 @@ def add_percentage_of_normal( to end getting our rainfall values (optional). :return: None """ - normal: float = self.get_average_yearly_rainfall(begin_year, end_year) + normal = self.get_average_yearly_rainfall(begin_year, end_year) if normal == 0.0: return @@ -279,7 +279,7 @@ def add_linear_regression(self) -> Tuple[float, float]: years = self.data[Label.YEAR.value].values.reshape(-1, 1) # type: ignore rainfalls = self.data[Label.RAINFALL.value].values - reg: LinearRegression = LinearRegression() + reg = LinearRegression() reg.fit(years, rainfalls) self.data[Label.LINEAR_REGRESSION.value] = reg.predict(years) self.data[Label.LINEAR_REGRESSION.value] = round( @@ -319,7 +319,7 @@ def add_kmeans(self, kmeans_clusters: int | None = 4) -> int: [Label.YEAR.value, Label.RAINFALL.value] ].values - kmeans: KMeans = KMeans(n_init=10, n_clusters=kmeans_clusters) + kmeans = KMeans(n_init=10, n_clusters=kmeans_clusters) kmeans.fit(fit_data) self.data[Label.KMEANS.value] = kmeans.predict(fit_data) @@ -344,11 +344,7 @@ def plot_rainfall(self) -> bool: :return: A boolean set to True if data has been successfully plotted, False otherwise. """ - success: bool = plotting.bar_column_according_to_year(self.data, Label.RAINFALL) - if not success: - return False - - return True + return plotting.bar_column_according_to_year(self.data, Label.RAINFALL) @plots.legend() def plot_linear_regression(self) -> bool: @@ -358,13 +354,9 @@ def plot_linear_regression(self) -> bool: :return: A boolean set to True if data has been successfully plotted, False otherwise. """ - success: bool = plotting.plot_column_according_to_year( + return plotting.plot_column_according_to_year( self.data, Label.LINEAR_REGRESSION, "red" ) - if not success: - return False - - return True @plots.legend() def plot_savgol_filter(self) -> bool: @@ -374,25 +366,22 @@ def plot_savgol_filter(self) -> bool: :return: A boolean set to True if data has been successfully plotted, False otherwise. """ - success: bool = plotting.plot_column_according_to_year( + return plotting.plot_column_according_to_year( self.data, Label.SAVITZKY_GOLAY_FILTER, "orange" ) - if not success: - return False - - return True @plots.legend(ylabel=Label.PERCENTAGE_OF_NORMAL.value) - def plot_normal(self, display_clusters: bool | None = False) -> bool: + def plot_normal(self, display_clusters=False) -> bool: """ Plot Rainfall normals data according to year. - :param display_clusters: The number of clusters to display + :param display_clusters: Whether to display clusters computed with k-means or not. + Defaults to False (optional). :return: A boolean set to True if data has been successfully plotted, False otherwise. """ plt.axhline(y=100.0, color="orange", linestyle="dashed", label="Normal") - success: bool = False + success = False if not display_clusters: success = plotting.scatter_column_according_to_year( self.data, Label.PERCENTAGE_OF_NORMAL @@ -408,7 +397,4 @@ def plot_normal(self, display_clusters: bool | None = False) -> bool: if not success: return False - if not success: - return False - - return True + return success diff --git a/src/core/utils/enums/labels.py b/src/core/utils/enums/labels.py index f2b72e2..a4a272e 100644 --- a/src/core/utils/enums/labels.py +++ b/src/core/utils/enums/labels.py @@ -10,9 +10,9 @@ class Label(str, BaseEnum): An Enum listing labels in DataFrame columns. """ - YEAR: str = "Year" - RAINFALL: str = "Rainfall" - PERCENTAGE_OF_NORMAL: str = "Percentage of normal" - LINEAR_REGRESSION: str = "Linear regression" - SAVITZKY_GOLAY_FILTER: str = "Savitzky–Golay filter" - KMEANS: str = "K-Means" + YEAR = "Year" + RAINFALL = "Rainfall" + PERCENTAGE_OF_NORMAL = "Percentage of normal" + LINEAR_REGRESSION = "Linear regression" + SAVITZKY_GOLAY_FILTER = "Savitzky–Golay filter" + KMEANS = "K-Means" diff --git a/src/core/utils/enums/months.py b/src/core/utils/enums/months.py index cfc2023..7a4a1de 100644 --- a/src/core/utils/enums/months.py +++ b/src/core/utils/enums/months.py @@ -10,15 +10,15 @@ class Month(int, BaseEnum): An Enum listing months as integers. """ - JANUARY: int = 1 - FEBRUARY: int = 2 - MARCH: int = 3 - APRIL: int = 4 - MAY: int = 5 - JUNE: int = 6 - JULY: int = 7 - AUGUST: int = 8 - SEPTEMBER: int = 9 - OCTOBER: int = 10 - NOVEMBER: int = 11 - DECEMBER: int = 12 + JANUARY = 1 + FEBRUARY = 2 + MARCH = 3 + APRIL = 4 + MAY = 5 + JUNE = 6 + JULY = 7 + AUGUST = 8 + SEPTEMBER = 9 + OCTOBER = 10 + NOVEMBER = 11 + DECEMBER = 12 diff --git a/src/core/utils/functions/metrics.py b/src/core/utils/functions/metrics.py index 7e2e2c3..555f5e2 100644 --- a/src/core/utils/functions/metrics.py +++ b/src/core/utils/functions/metrics.py @@ -17,10 +17,10 @@ def get_average_rainfall(yearly_rainfall: pd.DataFrame, round_precision=2) -> fl Computes Rainfall average. :param yearly_rainfall: A pandas DataFrame displaying rainfall data (in mm) according to year. - :param round_precision: A float representing the rainfall precision (optional). + :param round_precision: A float representing the rainfall precision (optional). Defaults to 2. :return: A float representing the average Rainfall. """ - nb_years: int = len(yearly_rainfall) + nb_years = len(yearly_rainfall) if nb_years == 0: return 0.0 @@ -68,7 +68,7 @@ def get_normal(yearly_rainfall: pd.DataFrame, begin_year, round_precision=2) -> :param yearly_rainfall: A pandas DataFrame displaying rainfall data (in mm) according to year. :param begin_year: A year to start the time frame. - :param round_precision: A float representing the rainfall precision (optional). + :param round_precision: A float representing the rainfall precision (optional). Defaults to 2. :return: A float storing the normal. """ diff --git a/src/core/utils/functions/plotting.py b/src/core/utils/functions/plotting.py index e4cd183..97511a9 100644 --- a/src/core/utils/functions/plotting.py +++ b/src/core/utils/functions/plotting.py @@ -37,7 +37,7 @@ def plot_column_according_to_year( def scatter_column_according_to_year( - yearly_rainfall: pd.DataFrame, label: Label, display_label: bool = True + yearly_rainfall: pd.DataFrame, label: Label, display_label=True ) -> bool: """ Scatter specified column data according to year. @@ -53,11 +53,10 @@ def scatter_column_according_to_year( ): return False - label_value: str | None = label.value if display_label else None plt.scatter( yearly_rainfall[Label.YEAR.value], yearly_rainfall[label.value], - label=label_value, + label=label.value if display_label else None, ) return True @@ -88,7 +87,7 @@ def bar_column_according_to_year(yearly_rainfall: pd.DataFrame, label: Label) -> def bar_monthly_rainfall_averages( monthly_rainfalls: dict, - label: str | None = "Average rainfall (mm)", + label="Average rainfall (mm)", begin_year: int | None = None, end_year: int | None = None, ) -> list: @@ -98,6 +97,7 @@ def bar_monthly_rainfall_averages( :param monthly_rainfalls: A list of instances of MonthlyRainfall. To be purposeful, all instances should have the same time frame in years. :param label: A string to use as a label for bar graphic. (optional) + Defaults to "Average rainfall (mm)". :param begin_year: An integer representing the year to start getting our rainfall values (optional). :param end_year: An integer representing the year @@ -140,7 +140,7 @@ def bar_monthly_rainfall_linreg_slopes(monthly_rainfalls: dict) -> list: def bar_seasonal_rainfall_averages( seasonal_rainfalls: dict, - label: str | None = "Average rainfall (mm)", + label="Average rainfall (mm)", begin_year: int | None = None, end_year: int | None = None, ) -> list: @@ -150,6 +150,7 @@ def bar_seasonal_rainfall_averages( :param seasonal_rainfalls: A list of instances of SeasonalRainfall. To be purposeful, all instances should have the same time frame in years. :param label: A string to use as a label for bar graphic. (optional) + Defaults to "Average rainfall (mm)". :param begin_year: An integer representing the year to start getting our rainfall values (optional). :param end_year: An integer representing the year