Skip to content

Commit 2e7210d

Browse files
committed
Add count_covered for coverage between valid samples
This returns the count of samples that are covered between the oldest and newest valid samples. Signed-off-by: cwasicki <[email protected]>
1 parent 41ba5ea commit 2e7210d

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

src/frequenz/sdk/timeseries/_ringbuffer/buffer.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,34 @@ def __getitem__(self, index_or_slice: SupportsIndex | slice) -> float | FloatArr
562562
"""
563563
return self._buffer.__getitem__(index_or_slice)
564564

565+
def _covered_time_range(self) -> timedelta:
566+
"""Return the time range that is covered by the oldest and newest valid samples.
567+
568+
Returns:
569+
The time range between the oldest and newest valid samples or 0 if
570+
there are is no time range covered.
571+
"""
572+
if not self.oldest_timestamp:
573+
return timedelta(0)
574+
575+
assert (
576+
self.newest_timestamp is not None
577+
), "Newest timestamp cannot be None here."
578+
return self.newest_timestamp - self.oldest_timestamp + self._sampling_period
579+
580+
@property
581+
def count_covered(self) -> int:
582+
"""Return the count of samples that are covered by the oldest and newest valid samples.
583+
584+
Returns:
585+
The count of samples between the oldest and newest (inclusive) valid samples
586+
or 0 if there are is no time range covered.
587+
"""
588+
return int(
589+
self._covered_time_range().total_seconds()
590+
// self._sampling_period.total_seconds()
591+
)
592+
565593
def count_valid(self) -> int:
566594
"""Count the number of valid items that this buffer currently holds.
567595

tests/timeseries/test_ringbuffer.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,21 @@ def test_gaps() -> None: # pylint: disable=too-many-statements
210210
assert buffer.oldest_timestamp is None
211211
assert buffer.newest_timestamp is None
212212
assert buffer.count_valid() == 0
213+
assert buffer.count_covered == 0
213214
assert len(buffer.gaps) == 0
214215

215216
buffer.update(Sample(dt(0), Quantity(0)))
216217
assert buffer.oldest_timestamp == dt(0)
217218
assert buffer.newest_timestamp == dt(0)
218219
assert buffer.count_valid() == 1
220+
assert buffer.count_covered == 1
219221
assert len(buffer.gaps) == 1
220222

221223
buffer.update(Sample(dt(6), Quantity(0)))
222224
assert buffer.oldest_timestamp == dt(6)
223225
assert buffer.newest_timestamp == dt(6)
224226
assert buffer.count_valid() == 1
227+
assert buffer.count_covered == 1
225228
assert len(buffer.gaps) == 1
226229

227230
buffer.update(Sample(dt(2), Quantity(2)))
@@ -230,48 +233,57 @@ def test_gaps() -> None: # pylint: disable=too-many-statements
230233
assert buffer.oldest_timestamp == dt(2)
231234
assert buffer.newest_timestamp == dt(6)
232235
assert buffer.count_valid() == 4
236+
assert buffer.count_covered == 5
233237
assert len(buffer.gaps) == 1
234238

235239
buffer.update(Sample(dt(3), None))
236240
assert buffer.oldest_timestamp == dt(2)
237241
assert buffer.newest_timestamp == dt(6)
238242
assert buffer.count_valid() == 3
243+
assert buffer.count_covered == 5
239244
assert len(buffer.gaps) == 2
240245

241246
buffer.update(Sample(dt(3), Quantity(np.nan)))
242247
assert buffer.oldest_timestamp == dt(2)
243248
assert buffer.newest_timestamp == dt(6)
244249
assert buffer.count_valid() == 3
250+
assert buffer.count_covered == 5
245251
assert len(buffer.gaps) == 2
246252

247253
buffer.update(Sample(dt(2), Quantity(np.nan)))
248254
assert buffer.oldest_timestamp == dt(4)
249255
assert buffer.newest_timestamp == dt(6)
250256
assert buffer.count_valid() == 2
257+
assert buffer.count_covered == 3
251258
assert len(buffer.gaps) == 2
252259

253260
buffer.update(Sample(dt(3), Quantity(3)))
254261
assert buffer.oldest_timestamp == dt(3)
255262
assert buffer.newest_timestamp == dt(6)
256263
assert buffer.count_valid() == 3
264+
assert buffer.count_covered == 4
257265
assert len(buffer.gaps) == 2
258266

259267
buffer.update(Sample(dt(2), Quantity(2)))
260268
assert buffer.oldest_timestamp == dt(2)
261269
assert buffer.newest_timestamp == dt(6)
262270
assert buffer.count_valid() == 4
271+
assert buffer.count_covered == 5
263272
assert len(buffer.gaps) == 1
264273

265274
buffer.update(Sample(dt(5), Quantity(5)))
266275
assert buffer.oldest_timestamp == dt(2)
267276
assert buffer.newest_timestamp == dt(6)
268277
assert buffer.count_valid() == 5
278+
assert buffer.count_covered == 5
269279
assert len(buffer.gaps) == 0
270280

281+
# whole range gap suffers from sdk#646
271282
buffer.update(Sample(dt(99), None))
272283
assert buffer.oldest_timestamp == dt(95) # bug: should be None
273284
assert buffer.newest_timestamp == dt(99) # bug: should be None
274285
assert buffer.count_valid() == 4 # bug: should be 0 (whole range gap)
286+
assert buffer.count_covered == 5 # bug: should be 0
275287
assert len(buffer.gaps) == 1
276288

277289

0 commit comments

Comments
 (0)