Skip to content

Commit

Permalink
Added related coverages to time series API endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardogsilva committed May 23, 2024
1 parent 7d64e18 commit 358c81f
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 85 deletions.
37 changes: 22 additions & 15 deletions arpav_ppcv/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,17 +840,23 @@ def create_coverage_configuration(
observation_variable_id=coverage_configuration_create.observation_variable_id,
observation_variable_aggregation_type=coverage_configuration_create.observation_variable_aggregation_type,
uncertainty_lower_bounds_coverage_configuration_id=(
coverage_configuration_create.uncertainty_lower_bounds_coverage_configuration_id),
coverage_configuration_create.uncertainty_lower_bounds_coverage_configuration_id
),
uncertainty_upper_bounds_coverage_configuration_id=(
coverage_configuration_create.uncertainty_upper_bounds_coverage_configuration_id),
coverage_configuration_create.uncertainty_upper_bounds_coverage_configuration_id
),
)
session.add(db_coverage_configuration)
to_refresh.append(db_coverage_configuration)
for secondary_cov_conf_id in coverage_configuration_create.secondary_coverage_configurations_ids:
db_secondary_cov_conf = get_coverage_configuration(session, secondary_cov_conf_id)
for (
secondary_cov_conf_id
) in coverage_configuration_create.secondary_coverage_configurations_ids:
db_secondary_cov_conf = get_coverage_configuration(
session, secondary_cov_conf_id
)
db_related = coverages.RelatedCoverageConfiguration(
main_coverage_configuration=db_coverage_configuration,
secondary_coverage_configuration=db_secondary_cov_conf
secondary_coverage_configuration=db_secondary_cov_conf,
)
session.add(db_related)
to_refresh.append(db_related)
Expand Down Expand Up @@ -903,15 +909,17 @@ def update_coverage_configuration(
# account for related cov confs being added/deleted
for existing_related in db_coverage_configuration.secondary_coverage_configurations:
has_been_requested_to_remove = (
existing_related.secondary_coverage_configuration_id
not in [
i.secondary_coverage_configuration_id
for i in coverage_configuration_update.secondary_coverage_configurations_ids
]
existing_related.secondary_coverage_configuration_id
not in [
i
for i in coverage_configuration_update.secondary_coverage_configurations_ids
]
)
if has_been_requested_to_remove:
session.delete(existing_related)
for secondary_id in coverage_configuration_update.secondary_coverage_configurations_ids:
for (
secondary_id
) in coverage_configuration_update.secondary_coverage_configurations_ids:
already_related = secondary_id in [
i.secondary_coverage_configuration_id
for i in db_coverage_configuration.secondary_coverage_configurations
Expand All @@ -920,19 +928,18 @@ def update_coverage_configuration(
db_secondary_cov_conf = get_coverage_configuration(session, secondary_id)
db_related = coverages.RelatedCoverageConfiguration(
main_coverage_configuration=db_coverage_configuration,
secondary_coverage_configuration=db_secondary_cov_conf
secondary_coverage_configuration=db_secondary_cov_conf,
)
session.add(db_related)
to_refresh.append(db_related)


data_ = coverage_configuration_update.model_dump(
exclude={
"possible_values",
"secondary_coverage_configuration_ids",
"secondary_coverage_configurations_ids",
},
exclude_unset=True,
exclude_none=True
exclude_none=True,
)
for key, value in data_.items():
setattr(db_coverage_configuration, key, value)
Expand Down
63 changes: 61 additions & 2 deletions arpav_ppcv/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,22 @@ def get_coverage_time_series(
)
measurements.update(**uncertainty_data)
if include_coverage_related_data:
# TODO: how to map to related data?
...
for (
related_cov_conf
) in coverage.configuration.secondary_coverage_configurations:
related_data = _get_related_coverage_time_series(
settings,
http_client,
point_geom,
start,
end,
coverage_smoothing_strategies,
related_cov_conf.secondary_coverage_configuration,
coverage,
)
measurements[
f"{base.RELATED_TIME_SERIES_PATTERN}_{related_cov_conf.secondary_coverage_configuration.name}"
] = related_data
if include_observation_data:
if coverage.configuration.related_observation_variable is not None:
station_data = _get_station_data(
Expand Down Expand Up @@ -386,6 +400,51 @@ def _get_coverage_uncertainty_time_series(
return result


def _get_related_coverage_time_series(
settings: ArpavPpcvSettings,
http_client: httpx.Client,
point_geom: shapely.Point,
time_start: Optional[dt.datetime],
time_end: Optional[dt.datetime],
smoothing_strategies: list[base.CoverageDataSmoothingStrategy],
related_coverage_configuration: coverages.CoverageConfiguration,
coverage: coverages.CoverageInternal,
) -> pd.DataFrame:
used_possible_values = coverage.configuration.retrieve_used_values(
coverage.identifier
)
used_values = [pv.configuration_parameter_value for pv in used_possible_values]
related_cov_identifier = related_coverage_configuration.build_coverage_identifier(
used_values
)
ncss_url = "/".join(
(
settings.thredds_server.base_url,
settings.thredds_server.netcdf_subset_service_url_fragment,
related_coverage_configuration.get_thredds_url_fragment(
related_cov_identifier
),
)
)
raw_coverage_data = ncss.query_dataset(
http_client,
thredds_ncss_url=ncss_url,
variable_name=related_coverage_configuration.netcdf_main_dataset_name,
longitude=point_geom.x,
latitude=point_geom.y,
time_start=time_start,
time_end=time_end,
)
return _process_coverage_data(
raw_coverage_data,
related_coverage_configuration.netcdf_main_dataset_name,
smoothing_strategies,
time_start,
time_end,
base_column_name=related_cov_identifier,
)


def _parse_temporal_range(
raw_temporal_range: str,
) -> tuple[dt.datetime | None, dt.datetime | None]:
Expand Down
1 change: 1 addition & 0 deletions arpav_ppcv/schemas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ObservationDataSmoothingStrategy(enum.Enum):


UNCERTAINTY_TIME_SERIES_PATTERN = "**UNCERTAINTY**"
RELATED_TIME_SERIES_PATTERN = "**RELATED**"


class ObservationAggregationType(enum.Enum):
Expand Down
19 changes: 9 additions & 10 deletions arpav_ppcv/schemas/coverages.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,19 @@ class CoverageConfiguration(sqlmodel.SQLModel, table=True):
back_populates="main_coverage_configuration",
sa_relationship_kwargs={
"foreign_keys": (
"RelatedCoverageConfiguration.main_coverage_configuration_id")
}
"RelatedCoverageConfiguration.main_coverage_configuration_id"
)
},
)
primary_coverage_configurations: list[
"RelatedCoverageConfiguration"
] = sqlmodel.Relationship(
back_populates="secondary_coverage_configuration",
sa_relationship_kwargs={
"foreign_keys": (
"RelatedCoverageConfiguration.secondary_coverage_configuration_id")
}
"RelatedCoverageConfiguration.secondary_coverage_configuration_id"
)
},
)

related_observation_variable: "observations.Variable" = sqlmodel.Relationship(
Expand Down Expand Up @@ -381,9 +383,7 @@ class RelatedCoverageConfiguration(sqlmodel.SQLModel, table=True):
"""

main_coverage_configuration_id: Optional[uuid.UUID] = sqlmodel.Field(
default=None,
primary_key=True,
foreign_key="coverageconfiguration.id"
default=None, primary_key=True, foreign_key="coverageconfiguration.id"
)
secondary_coverage_configuration_id: Optional[uuid.UUID] = sqlmodel.Field(
default=None,
Expand All @@ -395,17 +395,16 @@ class RelatedCoverageConfiguration(sqlmodel.SQLModel, table=True):
back_populates="secondary_coverage_configurations",
sa_relationship_kwargs={
"foreign_keys": "RelatedCoverageConfiguration.main_coverage_configuration_id",
}
},
)
secondary_coverage_configuration: CoverageConfiguration = sqlmodel.Relationship(
back_populates="primary_coverage_configurations",
sa_relationship_kwargs={
"foreign_keys": "RelatedCoverageConfiguration.secondary_coverage_configuration_id",
}
},
)



class ConfigurationParameterPossibleValue(sqlmodel.SQLModel, table=True):
"""Possible values for a parameter of a coverage configuration.
Expand Down
6 changes: 6 additions & 0 deletions arpav_ppcv/webapp/admin/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class ConfigurationParameterPossibleValueRead(sqlmodel.SQLModel):
configuration_parameter_value_name: str


class RelatedCoverageConfigurationRead(sqlmodel.SQLModel):
id: uuid.UUID
name: str


class CoverageConfigurationRead(sqlmodel.SQLModel):
id: uuid.UUID
name: str
Expand All @@ -43,6 +48,7 @@ class CoverageConfigurationRead(sqlmodel.SQLModel):
uncertainty_upper_bounds_coverage_configuration: Optional[
"CoverageConfigurationReadListItem"
]
related_coverages: list[RelatedCoverageConfigurationRead]


class ObservationVariableRead(sqlmodel.SQLModel):
Expand Down
53 changes: 44 additions & 9 deletions arpav_ppcv/webapp/admin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import functools
import logging
import uuid
from typing import Dict, Any, Union, Optional, List, Sequence

import anyio.to_thread
Expand Down Expand Up @@ -59,9 +60,10 @@ def related_observation_variable_choices_loader(
def coverage_configurations_choices_loader(
request: Request,
) -> Sequence[tuple[str, str]]:
main_cov_conf_id = uuid.UUID(request.path_params["pk"])
all_cov_confs = database.collect_all_coverage_configurations(request.state.session)
result = []
for cov_conf in all_cov_confs:
for cov_conf in [cc for cc in all_cov_confs if cc.id != main_cov_conf_id]:
result.append((cov_conf.name, cov_conf.name))
return result

Expand Down Expand Up @@ -241,14 +243,14 @@ class CoverageConfigurationView(ModelView):
pk_attr = "id"
fields = (
fields.UuidField("id"),
starlette_admin.StringField("name"),
starlette_admin.StringField("netcdf_main_dataset_name"),
starlette_admin.StringField("thredds_url_pattern"),
starlette_admin.StringField("name", required=True),
starlette_admin.StringField("netcdf_main_dataset_name", required=True),
starlette_admin.StringField("thredds_url_pattern", required=True),
starlette_admin.StringField("coverage_id_pattern", disabled=True),
starlette_admin.StringField("unit"),
starlette_admin.StringField("palette"),
starlette_admin.FloatField("color_scale_min"),
starlette_admin.FloatField("color_scale_max"),
starlette_admin.StringField("unit", required=True),
starlette_admin.StringField("palette", required=True),
starlette_admin.FloatField("color_scale_min", required=True),
starlette_admin.FloatField("color_scale_max", required=True),
fields.RelatedObservationsVariableField(
"observation_variable",
help_text="Related observation variable",
Expand Down Expand Up @@ -279,6 +281,16 @@ class CoverageConfigurationView(ModelView):
"which have the upper uncertainty bounds values"
),
),
starlette_admin.ListField(
field=fields.RelatedCoverageconfigurationsField(
"related_coverages",
choices_loader=coverage_configurations_choices_loader,
help_text=(
"Additional coverages which are related to this one and which "
"can be used when requesting time series"
),
)
),
)

exclude_fields_from_list = (
Expand All @@ -294,6 +306,7 @@ class CoverageConfigurationView(ModelView):
"observation_variable_aggregation_type",
"uncertainty_lower_bounds_coverage_configuration",
"uncertainty_upper_bounds_coverage_configuration",
"related_coverages",
)
exclude_fields_from_edit = ("coverage_id_pattern",)

Expand All @@ -305,7 +318,8 @@ async def get_pk_value(self, request: Request, obj: Any) -> Any:
result = await super().get_pk_value(request, obj)
return str(result)

def _serialize_instance(self, instance: coverages.CoverageConfiguration):
@staticmethod
def _serialize_instance(instance: coverages.CoverageConfiguration):
obs_variable = instance.related_observation_variable
if obs_variable is not None:
observation_variable = read_schemas.ObservationVariableRead(
Expand Down Expand Up @@ -353,6 +367,13 @@ def _serialize_instance(self, instance: coverages.CoverageConfiguration):
],
uncertainty_lower_bounds_coverage_configuration=uncertainty_lower_bounds_coverage_configuration,
uncertainty_upper_bounds_coverage_configuration=uncertainty_upper_bounds_coverage_configuration,
related_coverages=[
read_schemas.RelatedCoverageConfigurationRead(
id=rcc.secondary_coverage_configuration_id,
name=rcc.secondary_coverage_configuration.name,
)
for rcc in instance.secondary_coverage_configurations
],
)

async def find_by_pk(
Expand Down Expand Up @@ -430,6 +451,12 @@ async def create(self, request: Request, data: Dict[str, Any]) -> Any:
uncertainty_upper_id = db_uncertainty_upper.id
else:
uncertainty_upper_id = None
related_cov_conf_ids = []
for related_cov_conf_name in data.get("related_coverages", []):
db_related_cov_conf = database.get_coverage_configuration_by_name(
session, related_cov_conf_name
)
related_cov_conf_ids.append(db_related_cov_conf.id)
cov_conf_create = coverages.CoverageConfigurationCreate(
name=data["name"],
netcdf_main_dataset_name=data["netcdf_main_dataset_name"],
Expand All @@ -447,6 +474,7 @@ async def create(self, request: Request, data: Dict[str, Any]) -> Any:
),
uncertainty_lower_bounds_coverage_configuration_id=uncertainty_lower_id,
uncertainty_upper_bounds_coverage_configuration_id=uncertainty_upper_id,
secondary_coverage_configurations_ids=related_cov_conf_ids,
)
db_cov_conf = await anyio.to_thread.run_sync(
database.create_coverage_configuration, session, cov_conf_create
Expand Down Expand Up @@ -500,6 +528,12 @@ async def edit(self, request: Request, pk: Any, data: Dict[str, Any]) -> Any:
uncertainty_upper_id = db_uncertainty_upper.id
else:
uncertainty_upper_id = None
related_cov_conf_ids = []
for related_cov_conf_name in data.get("related_coverages", []):
db_related_cov_conf = database.get_coverage_configuration_by_name(
session, related_cov_conf_name
)
related_cov_conf_ids.append(db_related_cov_conf.id)
cov_conv_update = coverages.CoverageConfigurationUpdate(
name=data.get("name"),
netcdf_main_dataset_name=data.get("netcdf_main_dataset_name"),
Expand All @@ -517,6 +551,7 @@ async def edit(self, request: Request, pk: Any, data: Dict[str, Any]) -> Any:
),
uncertainty_lower_bounds_coverage_configuration_id=uncertainty_lower_id,
uncertainty_upper_bounds_coverage_configuration_id=uncertainty_upper_id,
secondary_coverage_configurations_ids=related_cov_conf_ids,
)
db_coverage_configuration = await anyio.to_thread.run_sync(
database.get_coverage_configuration, session, pk
Expand Down
Loading

0 comments on commit 358c81f

Please sign in to comment.