Analysis: read a rarely-read meter as coarse, not absent; newest rows first
ci / build-test (push) Successful in 2m41s
ci / build-test (push) Successful in 2m41s
Three things a reported Heizoel page got wrong at once. Its tank is dipped a few times a year and its burner read every few months, which is exactly the shape the coverage rules had not been walked through. "No data" for data that exists. A tank books nothing until the next dipstick closes the interval, so the stretch after the last dipstick is covered by no run at all, and a bucket no run covers was reported missing. The burner, whose run reaches into the window, said "only coarser data" -- the honest answer -- so one card claimed there was nothing while the coverage panel beside it listed years of data. A bucket that no run covers, no gap overlaps and no opening balance explains now reports the meter's resolution when its preceding coverage is within one interval of its own class: it is not silent, it is read rarely. A meter that does book its own buckets and stops -- a dead hourly source, a sheet asked about a later month -- still reads missing. Auto answering twelve months with one bar. Coarse only means "longer than a month", so a dipstick taken each autumn straddles a New Year as surely as a month start: coarsening the chart to years bought nothing and cost every point. The planning resolution now caps coarse at month when a run crosses a local year edge, and a series that cannot resolve the natural size no longer coarsens the whole chart -- it is drawn at that size with its buckets marked, which the chart and table already explain. A page contradicting itself. The comparison line above the ranking was fed the leading measure's matched coverage but worded as if it spoke for the page, directly above a burner row that did compare. It now names the figure it is about. Alongside: the "largest changes" ranking no longer drops a meter whose change is not comparable. It ranks what can be ranked, then lists the rest with their values and the reason -- the tank had been vanishing from its own energy type. And every dated table now reads newest first, as lists are read; charts stay chronological left to right, and the CSV export stays ascending for spreadsheets. A-41 to A-43 in the note record the three rules.
This commit is contained in:
@@ -278,4 +278,35 @@ public sealed class MeterPageLogicTests
|
||||
Assert.NotNull(MeterProjection.For(MonthSeries(1800, ResolutionClass.Month, start, Now.AddDays(-10)), ytd));
|
||||
Assert.Null(MeterProjection.For(MonthSeries(1800, ResolutionClass.Month, start, Now.AddDays(-100)), ytd));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- markers
|
||||
|
||||
[Fact]
|
||||
public void The_period_markers_are_one_dated_list_newest_first() => In("en", () =>
|
||||
{
|
||||
// A-42: events and price changes are drawn as one list, so they interleave by date instead of running down
|
||||
// twice. On one day the event comes first, then the price that starts with it.
|
||||
DateTimeOffset At(int month, int day) =>
|
||||
new DateTimeOffset(2026, month, day, 12, 0, 0, TimeSpan.Zero).ToUniversalTime();
|
||||
|
||||
var markers = new MeterMarkers(
|
||||
[
|
||||
new EventRow(3, At(5, 20), MeterEventType.Delivery, 2000, null, null, "L", null, null),
|
||||
new EventRow(2, At(3, 4), MeterEventType.Note, null, null, null, null, "new tenant", null),
|
||||
],
|
||||
MoreEvents: false,
|
||||
[
|
||||
new TariffRow(1, TariffScope.EnergyType, 3, TariffComponent.UnitPrice, 0.9, "EUR/L", D(2026, 3, 4), null),
|
||||
new TariffRow(2, TariffScope.Meter, 7, TariffComponent.UnitPrice, 1.1, "EUR/L", D(2026, 1, 1), null),
|
||||
]);
|
||||
|
||||
var list = MeterMarkerList.Of(markers, Berlin, "Heizöl");
|
||||
|
||||
Assert.Equal([D(2026, 5, 20), D(2026, 3, 4), D(2026, 3, 4), D(2026, 1, 1)], list.Select(m => m.Day));
|
||||
Assert.Equal("Delivery: 2,000.0 L", list[0].Text);
|
||||
Assert.Equal("Note: new tenant", list[1].Text); // the event of 4 March …
|
||||
Assert.Contains("Heizöl", list[2].Text, StringComparison.Ordinal); // … then the type price starting that day
|
||||
Assert.Contains("This meter", list[3].Text, StringComparison.Ordinal);
|
||||
Assert.Empty(MeterMarkerList.Of(MeterMarkers.None, Berlin, "Heizöl"));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user