Skip to content

Models

models

Pydantic models for the results extension.

Classes

AnalysisKind

Bases: str, Enum

Supported analysis categories.

ResultScope

Bases: str, Enum

Supported result scopes.

AnalysisDefinition

Bases: BaseModel

Validated analysis metadata.

Functions
to_payload
to_payload()

Serialize this definition to the fixed Django schema.

Source code in src/owi/metadatabase/results/models.py
def to_payload(self) -> dict[str, Any]:
    """Serialize this definition to the fixed Django schema."""
    payload: dict[str, Any] = {
        "name": self.name,
        "model_definition_id": self.model_definition_id,
        "location_id": self.location_id,
        "source_type": self.source_type,
        "source": self.source,
        "description": self.description,
        "user": self.user,
        "timestamp": self.timestamp.isoformat() if self.timestamp else None,
        "additional_data": self.additional_data,
    }
    return payload

ResultVector

Bases: BaseModel

Validated numeric vector metadata.

Functions
validate_values classmethod
validate_values(values)

Require non-empty numeric vectors.

Source code in src/owi/metadatabase/results/models.py
@field_validator("values")
@classmethod
def validate_values(cls, values: list[float]) -> list[float]:
    """Require non-empty numeric vectors."""
    if not values:
        raise ValueError("Result vectors must contain at least one value.")
    return values

RelatedObject

Bases: BaseModel

Reference to an arbitrary Django object related to a result.

ResultSeries

Bases: BaseModel

One logical result series compatible with a single Django row.

Functions
validate_scope_and_vectors
validate_scope_and_vectors()

Validate vector alignment and scope requirements.

Source code in src/owi/metadatabase/results/models.py
@model_validator(mode="after")
def validate_scope_and_vectors(self) -> ResultSeries:
    """Validate vector alignment and scope requirements."""
    vector_lengths = {len(vector.values) for vector in self.vectors}
    if len(vector_lengths) != 1:
        raise ValueError("All result vectors must have identical lengths.")
    if self.result_scope == ResultScope.SITE and self.site_id is None:
        raise ValueError("Site scoped results require site_id.")
    if self.result_scope == ResultScope.LOCATION and self.location_id is None:
        raise ValueError("Location scoped results require location_id.")
    return self
to_record_payload
to_record_payload(analysis_id)

Serialize this series to the fixed Django schema.

Source code in src/owi/metadatabase/results/models.py
def to_record_payload(self, analysis_id: int) -> dict[str, Any]:
    """Serialize this series to the fixed Django schema."""
    payload: dict[str, Any] = {
        "analysis": analysis_id,
        "site": self.site_id,
        "location": self.location_id,
        "short_description": self.short_description,
        "description": self.description,
        "additional_data": {
            "analysis_kind": self.analysis_kind.value,
            "result_scope": self.result_scope.value,
            **self.data_additional,
        },
    }
    if self.related_object is not None:
        payload["related_object"] = self.related_object.model_dump()
    for index, vector in enumerate(self.vectors, start=1):
        payload[f"name_col{index}"] = vector.name
        payload[f"units_col{index}"] = vector.unit
        payload[f"value_col{index}"] = vector.values
    for index in range(len(self.vectors) + 1, 4):
        payload[f"name_col{index}"] = None
        payload[f"units_col{index}"] = None
        payload[f"value_col{index}"] = None
    return payload

AnalysisRecordPayload

Bases: BaseModel

Validated payload sent to the Django Analysis endpoint.

ResultRecordPayload

Bases: BaseModel

Validated payload sent to the Django Result endpoint.

Functions
validate_lengths
validate_lengths()

Require aligned persisted vectors.

Source code in src/owi/metadatabase/results/models.py
@model_validator(mode="after")
def validate_lengths(self) -> ResultRecordPayload:
    """Require aligned persisted vectors."""
    lengths = {len(self.value_col1), len(self.value_col2)}
    if self.value_col3 is not None:
        lengths.add(len(self.value_col3))
    if len(lengths) != 1:
        raise ValueError("Persisted value columns must have identical lengths.")
    return self

ResultQuery

Bases: BaseModel

Validated high-level query filters.

Functions
validate_timezone classmethod
validate_timezone(value)

Require timezone-aware datetimes.

Source code in src/owi/metadatabase/results/models.py
@field_validator("timestamp_from", "timestamp_to")
@classmethod
def validate_timezone(cls, value: datetime | None) -> datetime | None:
    """Require timezone-aware datetimes."""
    if value is not None and value.tzinfo is None:
        raise ValueError("Datetime filters must be timezone-aware.")
    return value
to_backend_filters
to_backend_filters()

Translate friendly query fields to backend filters.

Source code in src/owi/metadatabase/results/models.py
def to_backend_filters(self) -> dict[str, Any]:
    """Translate friendly query fields to backend filters."""
    filters = dict(self.backend_filters)
    if self.analysis_id is not None:
        filters["analysis__id"] = self.analysis_id
    elif self.analysis_name is not None:
        filters["analysis__name"] = self.analysis_name
    if self.site_id is not None:
        filters["site"] = self.site_id
    if self.location_id is not None:
        filters["location"] = self.location_id
    if self.short_description is not None:
        filters["short_description"] = self.short_description
    if self.turbine is not None:
        filters["additional_data__turbine"] = self.turbine
    if self.timestamp_from is not None:
        filters["additional_data__timestamp_from"] = self.timestamp_from.isoformat()
    if self.timestamp_to is not None:
        filters["additional_data__timestamp_to"] = self.timestamp_to.isoformat()
    return filters

PlotRequest

Bases: BaseModel

Validated plot request.

PlotResponse

Bases: BaseModel

Structured chart response.