Skip to content

Commit

Permalink
Merge pull request #86 from ricardogsilva/81-add-related-coverages-to…
Browse files Browse the repository at this point in the history
…-coverage-time-series-endpoint

add related coverages to coverage time series endpoint
  • Loading branch information
francbartoli authored May 23, 2024
2 parents 636b396 + 358c81f commit 42ad3c9
Show file tree
Hide file tree
Showing 12 changed files with 648 additions and 62 deletions.
52 changes: 51 additions & 1 deletion arpav_ppcv/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,9 +839,27 @@ def create_coverage_configuration(
color_scale_max=coverage_configuration_create.color_scale_max,
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
),
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
)
db_related = coverages.RelatedCoverageConfiguration(
main_coverage_configuration=db_coverage_configuration,
secondary_coverage_configuration=db_secondary_cov_conf,
)
session.add(db_related)
to_refresh.append(db_related)
for possible in coverage_configuration_create.possible_values:
db_conf_param_value = get_configuration_parameter_value(
session, possible.configuration_parameter_value_id
Expand Down Expand Up @@ -888,8 +906,40 @@ def update_coverage_configuration(
)
session.add(db_possible_value)
to_refresh.append(db_possible_value)
# 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
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:
already_related = secondary_id in [
i.secondary_coverage_configuration_id
for i in db_coverage_configuration.secondary_coverage_configurations
]
if not already_related:
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,
)
session.add(db_related)
to_refresh.append(db_related)

data_ = coverage_configuration_update.model_dump(
exclude={"possible_values"}, exclude_unset=True, exclude_none=True
exclude={
"possible_values",
"secondary_coverage_configurations_ids",
},
exclude_unset=True,
exclude_none=True,
)
for key, value in data_.items():
setattr(db_coverage_configuration, key, value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""add-related-cov-confs
Revision ID: 74ee5bc68b7e
Revises: e8bc68ec327b
Create Date: 2024-05-21 16:29:25.257549
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
import sqlmodel


# revision identifiers, used by Alembic.
revision: str = '74ee5bc68b7e'
down_revision: Union[str, None] = 'e8bc68ec327b'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('relatedcoverageconfiguration',
sa.Column('main_coverage_configuration_id', sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.Column('secondary_coverage_configuration_id', sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.ForeignKeyConstraint(['main_coverage_configuration_id'], ['coverageconfiguration.id'], ),
sa.ForeignKeyConstraint(['secondary_coverage_configuration_id'], ['coverageconfiguration.id'], ),
sa.PrimaryKeyConstraint('main_coverage_configuration_id', 'secondary_coverage_configuration_id')
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('relatedcoverageconfiguration')
# ### end Alembic commands ###
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
53 changes: 53 additions & 0 deletions arpav_ppcv/schemas/coverages.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,27 @@ class CoverageConfiguration(sqlmodel.SQLModel, table=True):
"passive_deletes": True,
},
)
secondary_coverage_configurations: list[
"RelatedCoverageConfiguration"
] = sqlmodel.Relationship(
back_populates="main_coverage_configuration",
sa_relationship_kwargs={
"foreign_keys": (
"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"
)
},
)

related_observation_variable: "observations.Variable" = sqlmodel.Relationship(
back_populates="related_coverage_configurations"
)
Expand Down Expand Up @@ -315,6 +336,7 @@ class CoverageConfigurationCreate(sqlmodel.SQLModel):
] = None
uncertainty_lower_bounds_coverage_configuration_id: Optional[uuid.UUID] = None
uncertainty_upper_bounds_coverage_configuration_id: Optional[uuid.UUID] = None
secondary_coverage_configurations_ids: Optional[list[uuid.UUID]] = None

@pydantic.field_validator("thredds_url_pattern")
@classmethod
Expand All @@ -341,6 +363,7 @@ class CoverageConfigurationUpdate(sqlmodel.SQLModel):
possible_values: list["ConfigurationParameterPossibleValueUpdate"]
uncertainty_lower_bounds_coverage_configuration_id: Optional[uuid.UUID] = None
uncertainty_upper_bounds_coverage_configuration_id: Optional[uuid.UUID] = None
secondary_coverage_configurations_ids: Optional[list[uuid.UUID]] = None

@pydantic.field_validator("thredds_url_pattern")
@classmethod
Expand All @@ -352,6 +375,36 @@ def validate_thredds_url_pattern(cls, v: str) -> str:
return v.strip()


class RelatedCoverageConfiguration(sqlmodel.SQLModel, table=True):
"""Relates coverage configurations with each other.
This model mediates an association table that governs a many-to-many relationship
between a main coverage configuration and other coverage configurations.
"""

main_coverage_configuration_id: Optional[uuid.UUID] = sqlmodel.Field(
default=None, primary_key=True, foreign_key="coverageconfiguration.id"
)
secondary_coverage_configuration_id: Optional[uuid.UUID] = sqlmodel.Field(
default=None,
primary_key=True,
foreign_key="coverageconfiguration.id",
)

main_coverage_configuration: CoverageConfiguration = sqlmodel.Relationship(
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
Loading

0 comments on commit 42ad3c9

Please sign in to comment.