Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #66

Merged
merged 2 commits into from
Aug 12, 2024
Merged

Dev #66

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 50 additions & 29 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
"""
from __future__ import annotations

from typing import Any

import matplotlib.pyplot as plt
from flasgger import Swagger, swag_from
from flask import Flask, jsonify, request, Response, send_file
from flask import Flask, request, Response, send_file

import src.api.swagger.parameters_specs as param
from src.api.schemas import (
Expand All @@ -27,7 +29,7 @@
standard_deviation_specs,
)
from src.api.swagger.year import below_normal_specs, above_normal_specs
from src.api.utils import parse_args, manage_time_mode_errors
from src.api.utils import parse_args, return_time_mode_error_or_fill_response_dict
from src.config import Config
from src.core.models.all_rainfall import AllRainfall
from src.core.utils.enums.time_modes import TimeMode
Expand All @@ -54,21 +56,29 @@ def average_rainfall() -> Response:
param.season,
)

to_return = manage_time_mode_errors({}, params[0], params[3], params[4])
if isinstance(to_return, Response):
return to_return
to_return: dict[str, Any] = {}
if error := return_time_mode_error_or_fill_response_dict(
to_return, params[0], params[3], params[4]
):
return error

to_return.update(
{
"name": "average rainfall (mm)",
"value": all_rainfall.get_average_rainfall(*params),
"value": all_rainfall.get_average_rainfall(
params[0],
begin_year=params[1],
end_year=params[2],
month=params[3],
season=params[4],
),
"begin_year": params[1],
"end_year": params[2] or all_rainfall.get_last_year(),
"time_mode": TimeMode[params[0]],
}
)

return jsonify(RainfallSchema().dump(to_return))
return RainfallSchema().dump(to_return)


@app.route(f"{base_path}/rainfall/normal")
Expand All @@ -78,9 +88,11 @@ def normal_rainfall() -> Response:
request.args, param.time_mode, param.begin_year, param.month, param.season
)

to_return = manage_time_mode_errors({}, params[0], params[2], params[3])
if isinstance(to_return, Response):
return to_return
to_return: dict[str, Any] = {}
if error := return_time_mode_error_or_fill_response_dict(
to_return, params[0], params[2], params[3]
):
return error

to_return.update(
{
Expand All @@ -92,7 +104,7 @@ def normal_rainfall() -> Response:
}
)

return jsonify(RainfallSchema().dump(to_return))
return RainfallSchema().dump(to_return)


@app.route(f"{base_path}/rainfall/relative_distance_to_normal")
Expand All @@ -108,9 +120,11 @@ def rainfall_relative_distance_to_normal() -> Response:
param.season,
)

to_return = manage_time_mode_errors({}, params[0], params[4], params[5])
if isinstance(to_return, Response):
return to_return
to_return: dict[str, Any] = {}
if error := return_time_mode_error_or_fill_response_dict(
to_return, params[0], params[4], params[5]
):
return error

to_return.update(
{
Expand All @@ -123,7 +137,7 @@ def rainfall_relative_distance_to_normal() -> Response:
}
)

return jsonify(RelativeDistanceToRainfallNormalSchema().dump(to_return))
return RelativeDistanceToRainfallNormalSchema().dump(to_return)


@app.route(f"{base_path}/rainfall/standard_deviation")
Expand All @@ -138,9 +152,11 @@ def rainfall_standard_deviation() -> Response:
param.season,
)

to_return = manage_time_mode_errors({}, params[0], params[3], params[4])
if isinstance(to_return, Response):
return to_return
to_return: dict[str, Any] = {}
if error := return_time_mode_error_or_fill_response_dict(
to_return, params[0], params[3], params[4]
):
return error

to_return.update(
{
Expand All @@ -152,7 +168,7 @@ def rainfall_standard_deviation() -> Response:
}
)

return jsonify(RainfallSchema().dump(to_return))
return RainfallSchema().dump(to_return)


@app.route(f"{base_path}/year/below_normal")
Expand All @@ -168,9 +184,11 @@ def years_below_normal() -> Response:
param.season,
)

to_return = manage_time_mode_errors({}, params[0], params[4], params[5])
if isinstance(to_return, Response):
return to_return
to_return: dict[str, Any] = {}
if error := return_time_mode_error_or_fill_response_dict(
to_return, params[0], params[4], params[5]
):
return error

to_return.update(
{
Expand All @@ -183,7 +201,7 @@ def years_below_normal() -> Response:
}
)

return jsonify(YearsAboveOrBelowNormalSchema().dump(to_return))
return YearsAboveOrBelowNormalSchema().dump(to_return)


@app.route(f"{base_path}/year/above_normal")
Expand All @@ -199,9 +217,11 @@ def years_above_normal() -> Response:
param.season,
)

to_return = manage_time_mode_errors({}, params[0], params[4], params[5])
if isinstance(to_return, Response):
return to_return
to_return: dict[str, Any] = {}
if error := return_time_mode_error_or_fill_response_dict(
to_return, params[0], params[4], params[5]
):
return error

to_return.update(
{
Expand All @@ -214,7 +234,7 @@ def years_above_normal() -> Response:
}
)

return jsonify(YearsAboveOrBelowNormalSchema().dump(to_return))
return YearsAboveOrBelowNormalSchema().dump(to_return)


@app.route(f"{base_path}/csv/minimal_csv")
Expand All @@ -224,8 +244,9 @@ def minimal_csv() -> Response:
request.args, param.time_mode, param.month, param.season, param.file_name
)

error = manage_time_mode_errors({}, params[0], params[1], params[2])
if isinstance(error, Response):
if error := return_time_mode_error_or_fill_response_dict(
{}, params[0], params[1], params[2]
):
return error

all_rainfall.export_as_csv(*params)
Expand Down
5 changes: 3 additions & 2 deletions src/api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ class RainfallSchema(BaseRainfallSchema):
value: float = fields.Float()


class RelativeDistanceToRainfallNormalSchema(RainfallSchema):
class RelativeDistanceToRainfallNormalSchema(BaseRainfallSchema):
"""
Schema for depicting a relative distance to rainfall normal.
"""

normal_year: int = fields.Int(load_default=param.normal_year["default"])
value: float = fields.Float()


class YearsAboveOrBelowNormalSchema(BaseRainfallSchema):
Expand All @@ -51,7 +52,7 @@ class YearsAboveOrBelowNormalSchema(BaseRainfallSchema):
"""

normal_year: int = fields.Int(load_default=param.normal_year["default"])
value: float = fields.Int()
value: int = fields.Int()


class ApiError(Schema):
Expand Down
8 changes: 4 additions & 4 deletions src/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ def swagger_type_to_python_type(swagger_type: str) -> type | None:
return python_type


def manage_time_mode_errors(
def return_time_mode_error_or_fill_response_dict(
response_dict: dict,
time_mode: str,
month: str | None = None,
season: str | None = None,
) -> Response | dict:
) -> Response | None:
"""
Manage errors related to time mode issues.
If time mode is set to monthly and month is None.
Expand All @@ -67,7 +67,7 @@ def manage_time_mode_errors(
:param season: A string corresponding to the season name.
Possible values are within ['WINTER', 'SPRING', 'SUMMER', 'FALL'].
Set if time_mode is 'SEASONAL' (optional)
:return: Either a Flask Response if there is an error or the updated dictionary.
:return: Either a Flask Response if there is an error or None.
"""
if time_mode == TimeMode.MONTHLY.name:
if month is None:
Expand All @@ -81,4 +81,4 @@ def manage_time_mode_errors(

response_dict["season"] = Season[season]

return response_dict
return None
33 changes: 22 additions & 11 deletions src/core/models/all_rainfall.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ def export_as_csv(
def get_average_rainfall(
self,
time_mode: str,
begin_year: int | None = None,
*,
begin_year: int,
end_year: int | None = None,
month: str | None = None,
season: str | None = None,
Expand All @@ -131,7 +132,7 @@ def get_average_rainfall(

:param time_mode: A string setting the time period ['YEARLY', 'MONTHLY', 'SEASONAL']
:param begin_year: An integer representing the year
to start getting our rainfall values (optional).
to start getting our rainfall values.
:param end_year: An integer representing the year
to end getting our rainfall values (optional).
:param month: A string corresponding to the month name.
Expand Down Expand Up @@ -322,29 +323,35 @@ def get_last_year(self) -> int:

def bar_rainfall_averages(
self,
monthly=True,
begin_year: int | None = None,
begin_year: int,
end_year: int | None = None,
monthly=True,
) -> 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. Defaults to True (optional).
:param begin_year: An integer representing the year
to start getting our rainfall values. (optional).
to start getting our rainfall values.
:param end_year: An integer representing the year
to end getting our rainfall values (optional).
:param monthly: If True, plots monthly rainfall averages.
If False, plots seasonal rainfall averages. Defaults to True (optional).
:return: A list of the Rainfall averages for each month or season.
"""
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
list(self.monthly_rainfalls.values()),
begin_year=begin_year,
end_year=end_year,
label=label,
)

return plotting.bar_seasonal_rainfall_averages(
self.seasonal_rainfalls, label, begin_year, end_year
list(self.seasonal_rainfalls.values()),
begin_year=begin_year,
end_year=end_year,
label=label,
)

def bar_rainfall_linreg_slopes(self, monthly=True) -> list:
Expand All @@ -356,9 +363,13 @@ def bar_rainfall_linreg_slopes(self, monthly=True) -> list:
:return: A list of the Rainfall LinReg slopes for each month or season.
"""
if monthly:
return plotting.bar_monthly_rainfall_linreg_slopes(self.monthly_rainfalls)
return plotting.bar_monthly_rainfall_linreg_slopes(
list(self.monthly_rainfalls.values())
)

return plotting.bar_seasonal_rainfall_linreg_slopes(self.seasonal_rainfalls)
return plotting.bar_seasonal_rainfall_linreg_slopes(
list(self.seasonal_rainfalls.values())
)

def get_entity_for_time_mode(
self, time_mode: str, month: str | None = None, season: str | None = None
Expand Down
12 changes: 7 additions & 5 deletions src/core/models/yearly_rainfall.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,22 @@ def load_rainfall(
)

def get_yearly_rainfall(
self, begin_year: int | None = None, end_year: int | None = None
self, begin_year: int, end_year: int | None = None
) -> pd.DataFrame:
"""
Retrieves Yearly Rainfall within a specific year range.

:param begin_year: An integer representing the year
to start getting our rainfall values (optional).
to start getting our rainfall values.
:param end_year: An integer representing the year
to end getting our rainfall values (optional).
:return: A pandas DataFrame displaying rainfall data (in mm)
for instance month according to year.
"""

return df_opr.get_rainfall_within_year_interval(self.data, begin_year, end_year)
return df_opr.get_rainfall_within_year_interval(
self.data, begin_year=begin_year, end_year=end_year
)

def export_as_csv(self, path: str | Path | None = None) -> str | None:
"""
Expand All @@ -107,13 +109,13 @@ def export_as_csv(self, path: str | Path | None = None) -> str | None:
return self.data.to_csv(path_or_buf=path, index=False)

def get_average_yearly_rainfall(
self, begin_year: int | None = None, end_year: int | None = None
self, begin_year: int, end_year: int | None = None
) -> float:
"""
Computes Rainfall average for a specific year range.

:param begin_year: An integer representing the year
to start getting our rainfall values (optional).
to start getting our rainfall values.
:param end_year: An integer representing the year
to end getting our rainfall values (optional).
:return: A float representing the average Rainfall.
Expand Down
12 changes: 4 additions & 8 deletions src/core/utils/functions/dataframe_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,25 @@

def get_rainfall_within_year_interval(
yearly_rainfall: pd.DataFrame,
begin_year: int | None = None,
*,
begin_year: int,
end_year: int | None = None,
) -> pd.DataFrame:
"""
Retrieves Yearly Rainfall within a specific year range.

:param yearly_rainfall: A pandas DataFrame displaying rainfall data (in mm) according to year.
:param begin_year: An integer representing the year
to start getting our rainfall values (optional).
to start getting our rainfall values.
:param end_year: An integer representing the year
to end getting our rainfall values (optional).
:return: A pandas DataFrame displaying rainfall data (in mm)
according to year.
"""
if begin_year is not None:
yearly_rainfall = yearly_rainfall[
yearly_rainfall[Label.YEAR.value] >= begin_year
]

if end_year is not None:
yearly_rainfall = yearly_rainfall[yearly_rainfall[Label.YEAR.value] <= end_year]

return yearly_rainfall
return yearly_rainfall[yearly_rainfall[Label.YEAR.value] >= begin_year]


def remove_column(yearly_rainfall: pd.DataFrame, label: Label) -> bool:
Expand Down
Loading
Loading