Skip to content

Commit 8f5b067

Browse files
committed
Implement __iter__, min, max, map methods for Sample3Phase
Signed-off-by: Sahas Subramanian <[email protected]>
1 parent 693af77 commit 8f5b067

File tree

1 file changed

+86
-1
lines changed

1 file changed

+86
-1
lines changed

src/frequenz/sdk/timeseries/_base_types.py

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33

44
"""Timeseries basic types."""
55

6+
from __future__ import annotations
7+
8+
import functools
69
from dataclasses import dataclass, field
710
from datetime import datetime
8-
from typing import Optional
11+
from typing import Callable, Iterator, Optional, overload
912

1013

1114
# Ordering by timestamp is a bit arbitrary, and it is not always what might be
@@ -48,3 +51,85 @@ class Sample3Phase:
4851

4952
value_p3: Optional[float]
5053
"""The value of the 3rd phase in this sample."""
54+
55+
def __iter__(self) -> Iterator[float | None]:
56+
"""Return an iterator that yields values from each of the phases.
57+
58+
Yields:
59+
Per-phase measurements one-by-one.
60+
"""
61+
yield self.value_p1
62+
yield self.value_p2
63+
yield self.value_p3
64+
65+
@overload
66+
def max(self, default: float) -> float:
67+
...
68+
69+
@overload
70+
def max(self, default: None = None) -> float | None:
71+
...
72+
73+
def max(self, default: float | None = None) -> float | None:
74+
"""Return the max value among all phases, or default if they are all `None`.
75+
76+
Args:
77+
default: value to return if all phases are `None`.
78+
79+
Returns:
80+
Max value among all phases, if available, default value otherwise.
81+
"""
82+
if not any(self):
83+
return default
84+
value: float = functools.reduce(
85+
lambda x, y: x if x > y else y,
86+
filter(None, self),
87+
)
88+
return value
89+
90+
@overload
91+
def min(self, default: float) -> float:
92+
...
93+
94+
@overload
95+
def min(self, default: None = None) -> float | None:
96+
...
97+
98+
def min(self, default: float | None = None) -> float | None:
99+
"""Return the min value among all phases, or default if they are all `None`.
100+
101+
Args:
102+
default: value to return if all phases are `None`.
103+
104+
Returns:
105+
Min value among all phases, if available, default value otherwise.
106+
"""
107+
if not any(self):
108+
return default
109+
value: float = functools.reduce(
110+
lambda x, y: x if x < y else y,
111+
filter(None, self),
112+
)
113+
return value
114+
115+
def map(
116+
self, function: Callable[[float], float], default: float | None = None
117+
) -> Sample3Phase:
118+
"""Apply the given function on each of the phase values and return the result.
119+
120+
If a phase value is `None`, replace it with `default` instead.
121+
122+
Args:
123+
function: The function to apply on each of the phase values.
124+
default: The value to apply if a phase value is `None`.
125+
126+
Returns:
127+
A new `Sample3Phase` instance, with the given function applied on values
128+
for each of the phases.
129+
"""
130+
return Sample3Phase(
131+
timestamp=self.timestamp,
132+
value_p1=default if self.value_p1 is None else function(self.value_p1),
133+
value_p2=default if self.value_p2 is None else function(self.value_p2),
134+
value_p3=default if self.value_p3 is None else function(self.value_p3),
135+
)

0 commit comments

Comments
 (0)