Skip to content

Services

services

Services subpackage for the results extension.

Classes

ApiResultsRepository

ApiResultsRepository(api=None)

Repository adapter built on top of ResultsAPI.

Source code in src/owi/metadatabase/results/services/core.py
def __init__(self, api: ResultsAPI | None = None) -> None:
    self.api = api or ResultsAPI(token="dummy")
Functions
list_analyses
list_analyses(name=None, **kwargs)

Retrieve analysis rows from the backend.

Source code in src/owi/metadatabase/results/services/core.py
def list_analyses(self, name: str | None = None, **kwargs: Any) -> pd.DataFrame:
    """Retrieve analysis rows from the backend."""
    return self.api.list_analyses(name=name, **kwargs)["data"]
list_results
list_results(query)

Retrieve raw result rows from the backend.

Source code in src/owi/metadatabase/results/services/core.py
def list_results(self, query: ResultQuery) -> pd.DataFrame:
    """Retrieve raw result rows from the backend."""
    return self.api.list_results(**query.to_backend_filters())["data"]
create_analysis
create_analysis(payload)

Create an analysis row.

Source code in src/owi/metadatabase/results/services/core.py
def create_analysis(self, payload: Mapping[str, Any]) -> Mapping[str, Any]:
    """Create an analysis row."""
    return self.api.create_analysis(dict(payload))
create_result
create_result(payload)

Create a single result row.

Source code in src/owi/metadatabase/results/services/core.py
def create_result(self, payload: Mapping[str, Any]) -> Mapping[str, Any]:
    """Create a single result row."""
    return self.api.create_result(dict(payload))
create_results_bulk
create_results_bulk(payloads)

Create multiple result rows.

Source code in src/owi/metadatabase/results/services/core.py
def create_results_bulk(self, payloads: Sequence[Mapping[str, Any]]) -> Mapping[str, Any]:
    """Create multiple result rows."""
    return self.api.create_results_bulk([dict(payload) for payload in payloads])
create_or_update_results_bulk
create_or_update_results_bulk(payloads)

Create missing result rows and patch existing ones.

Source code in src/owi/metadatabase/results/services/core.py
def create_or_update_results_bulk(self, payloads: Sequence[Mapping[str, Any]]) -> Mapping[str, Any]:
    """Create missing result rows and patch existing ones."""
    return self.api.create_or_update_results_bulk([dict(payload) for payload in payloads])
update_result
update_result(result_id, payload)

Patch a single result row.

Source code in src/owi/metadatabase/results/services/core.py
def update_result(self, result_id: int, payload: Mapping[str, Any]) -> Mapping[str, Any]:
    """Patch a single result row."""
    return self.api.update_result(result_id, dict(payload))
get_location_frame
get_location_frame(location_ids)

Retrieve location metadata required by map-oriented plots.

Source code in src/owi/metadatabase/results/services/core.py
def get_location_frame(self, location_ids: Sequence[int]) -> pd.DataFrame:
    """Retrieve location metadata required by map-oriented plots."""
    if not location_ids:
        return pd.DataFrame(columns=["id", "title", "northing", "easting"])
    location_api = LocationsAPI(api_root=self.api.base_api_root, **self.api._auth_kwargs())
    assetlocations = location_api.get_assetlocations()["data"]
    if assetlocations.empty or "id" not in assetlocations.columns:
        return pd.DataFrame(columns=["id", "title", "northing", "easting"])
    location_frame = assetlocations[assetlocations["id"].isin(location_ids)].copy()
    columns = [column for column in ["id", "title", "northing", "easting"] if column in location_frame.columns]
    return location_frame.loc[:, columns]

ResultsService

ResultsService(repository=None, registry=None)

Facade for validated retrieval and plotting.

Parameters:

Name Type Description Default
repository ResultsRepositoryProtocol

Repository instance to use for data access. This means that the service does not depend on a specific repository implementation, and the default is a simple API adapter. Custom repositories can be injected for testing or to support alternative backends.

None
registry AnalysisRegistryProtocol

Analysis registry to use for looking up analysis definitions. The default is a simple in-memory registry pre-populated with the built-in analyses. Different registries can be injected to support custom analyses or alternative lookup mechanisms.

None

Examples:

>>> service = ResultsService()
>>> service.get_results("WindSpeedHistogram", filters={"location_id": 123})
pd.DataFrame(...)
>>> service.plot_results("WindSpeedHistogram",
...                      filters={"location_id": 123},
...                      plot_type="histogram")
PlotResponse(...)
Source code in src/owi/metadatabase/results/services/core.py
def __init__(
    self,
    repository: ResultsRepositoryProtocol | None = None,
    registry: AnalysisRegistryProtocol | None = None,
) -> None:
    self.repository = repository or ApiResultsRepository()
    self.registry = registry or default_registry
    self.serializer = DjangoResultSerializer()
Functions
deserialize_result_series
deserialize_result_series(raw_data)

Deserialize raw backend rows into typed result series.

Source code in src/owi/metadatabase/results/services/core.py
def deserialize_result_series(
    self,
    raw_data: Sequence[Mapping[str, Any]] | pd.DataFrame,
) -> list[ResultSeries]:
    """Deserialize raw backend rows into typed result series."""
    if isinstance(raw_data, pd.DataFrame):
        raw_records = cast(list[dict[str, Any]], raw_data.to_dict(orient="records"))
    else:
        raw_records = [dict(row) for row in raw_data]
    return [self.serializer.from_mapping(row) for row in raw_records]
get_result_series
get_result_series(analysis_name, filters=None)

Fetch and deserialize typed result series for the given analysis.

Source code in src/owi/metadatabase/results/services/core.py
def get_result_series(
    self,
    analysis_name: str,
    filters: ResultQuery | Mapping[str, Any] | None = None,
) -> list[ResultSeries]:
    """Fetch and deserialize typed result series for the given analysis."""
    query = self._coerce_query(analysis_name, filters)
    frame = self.repository.list_results(query)
    return self.deserialize_result_series(frame)
get_location_frame
get_location_frame(location_ids)

Return location metadata for the given backend location identifiers.

Source code in src/owi/metadatabase/results/services/core.py
def get_location_frame(self, location_ids: Sequence[int]) -> pd.DataFrame:
    """Return location metadata for the given backend location identifiers."""
    columns = ["id", "title", "northing", "easting"]
    if not location_ids:
        return pd.DataFrame(columns=columns)
    get_location_frame = getattr(self.repository, "get_location_frame", None)
    if not callable(get_location_frame):
        return pd.DataFrame(columns=columns)
    location_frame = get_location_frame(location_ids)
    if not isinstance(location_frame, pd.DataFrame):
        return pd.DataFrame(columns=columns)
    if location_frame.empty:
        return pd.DataFrame(columns=columns)
    available_columns = [column for column in columns if column in location_frame.columns]
    selected_frame = cast(pd.DataFrame, location_frame.loc[:, available_columns])
    return selected_frame.copy()
get_results
get_results(analysis_name, filters=None)

Return normalized analysis data for the given analysis.

Source code in src/owi/metadatabase/results/services/core.py
def get_results(self, analysis_name: str, filters: ResultQuery | Mapping[str, Any] | None = None) -> pd.DataFrame:
    """Return normalized analysis data for the given analysis."""
    records = self.get_result_series(analysis_name, filters)
    analysis = self.registry.get(analysis_name)
    return analysis.from_results(records)
plot_results
plot_results(
    analysis_name, filters=None, *, plot_type=None
)

Render a chart for the requested analysis.

Source code in src/owi/metadatabase/results/services/core.py
def plot_results(
    self,
    analysis_name: str,
    filters: ResultQuery | Mapping[str, Any] | None = None,
    *,
    plot_type: str | None = None,
) -> PlotResponse:
    """Render a chart for the requested analysis."""
    query = self._coerce_query(analysis_name, filters)
    records = self.get_result_series(analysis_name, query)
    analysis = self.registry.get(analysis_name)
    plot_request = PlotRequest(
        analysis_name=analysis_name,
        filters=query.model_dump(),
        plot_type=plot_type,
        context=self._plot_context(records, plot_type=plot_type),
    )
    return analysis.plot(records, request=plot_request)

Functions

get_results

get_results(analysis_name, filters=None, service=None)

Return normalized analysis data using the default service.

Source code in src/owi/metadatabase/results/services/core.py
def get_results(
    analysis_name: str,
    filters: ResultQuery | Mapping[str, Any] | None = None,
    service: ResultsService | None = None,
) -> pd.DataFrame:
    """Return normalized analysis data using the default service."""
    return (service or ResultsService()).get_results(analysis_name, filters)

plot_results

plot_results(
    analysis_name,
    filters=None,
    *,
    plot_type=None,
    service=None,
)

Return a plotted chart using the default service.

Source code in src/owi/metadatabase/results/services/core.py
def plot_results(
    analysis_name: str,
    filters: ResultQuery | Mapping[str, Any] | None = None,
    *,
    plot_type: str | None = None,
    service: ResultsService | None = None,
) -> PlotResponse:
    """Return a plotted chart using the default service."""
    return (service or ResultsService()).plot_results(analysis_name, filters, plot_type=plot_type)