Source code for kappa_sdk.vector

from __future__ import annotations
import uuid
from datetime import datetime, timedelta, timezone
from typing import List, Optional, Union
from .datetime_utils import datetime_to_utc


[docs] class VectorElement: def __init__(self, date: datetime, value: float, elapsed_time: float): self.date = date self.value = value self.elapsed_time = elapsed_time
[docs] class Vector: """ Vector object. Container of the data that supports: * Conversion of X values between the absolute and elapsed times. * Conversion of Y values from the internal units to the given ones. .. important:: When initializing the :class:`Vector`, Y values must be given in ``internal units``. This conversion can be done with the help of the :class:`UnitConverter`. Parameters ---------- dates: List of X (date) values. values: List of Y (data) values. first_x: first_x of a step data """ def __init__(self, dates: List[datetime], values: List[float], first_x: Optional[datetime] = None, vector_id: Optional[str] = None): self.__first_x: Optional[datetime] = first_x if first_x is not None else dates[0] if len(dates) > 0 else None self.__dates: List[datetime] = dates self.__elapsed_times: Optional[List[float]] = None self.__values: List[float] = values self.__id: str = vector_id if vector_id is not None else str(uuid.uuid4()) @property def id(self) -> str: """ Gets the :class:`Vector` id. """ return self.__id @property def first_x(self) -> Optional[datetime]: """Gets the first_x/ start time of the elapsed times of this :class:`Vector` """ return self.__first_x @property def dates(self) -> List[datetime]: """ List of X (date) values in this :class:`Vector`. """ return self.__dates @property def elapsed_times(self) -> List[float]: """ List of X (date) values as elapsed time (in hours), counted from the first date in the :class:`Vector`. """ if self.__elapsed_times is None: self.__elapsed_times = [(Vector.__to_elapsed_time(date) - Vector.__to_elapsed_time(self.__first_x)) for date in self.dates] return self.__elapsed_times @property def values(self) -> List[float]: """ List of Y (data) values in this :class:`Vector`. """ return self.__values def set_first_x(self, first_x: datetime) -> None: self.__elapsed_times = None self.__first_x = first_x def set_elapsed_times(self, elapsed_times: List[float], reference_date: Optional[datetime] = None) -> None: """ Sets the elapsed times. Sets the elapsed times and recalculates absolute times based on the provided reference date. Parameters ---------- elapsed_times: List of X (date) values as elapsed time (in hours) reference_date: Reference date used to calculate absolute time values. Default value is ``datetime.now()``. """ reference_date = datetime_to_utc(reference_date) if reference_date is None: reference_date = datetime.now().replace(tzinfo=timezone.utc) self.__first_x = reference_date self.__elapsed_times = elapsed_times self.__dates = [reference_date + timedelta(hours=h) for h in self.__elapsed_times] @staticmethod def __to_elapsed_time(date: Optional[datetime]) -> float: date = datetime_to_utc(date) if date is None: return 0 else: return ((date - datetime(1970, 1, 1).replace(tzinfo=timezone.utc)).total_seconds()) / 3600 def __iter__(self) -> Vector: self._index = 0 return self def __next__(self) -> VectorElement: if self._index < len(self.__dates): element = VectorElement(self.__dates[self._index], self.__values[self._index], self.elapsed_times[self._index]) self._index += 1 return element else: raise StopIteration def __getitem__(self, index: Union[int, slice]) -> Union[VectorElement, List[VectorElement]]: if isinstance(index, slice): return [VectorElement(self.__dates[i], self.__values[i], self.elapsed_times[i]) for i in range(*index.indices(len(self.__dates)))] else: return VectorElement(self.__dates[index], self.__values[index], self.elapsed_times[index]) def __len__(self) -> int: return len(self.__values)