Skip to content

Commit

Permalink
Cleanup and auto-scale the values
Browse files Browse the repository at this point in the history
Note that we are taking perfomance hit by doing some extra
pre-processing for the convinience to the user
  • Loading branch information
vladistan committed Aug 8, 2024
1 parent 9009d12 commit f00c6d4
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 47 deletions.
44 changes: 22 additions & 22 deletions src/greenbutton_objects/feed/feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,24 @@ def build(self, entry_forest: ObjectForest) -> "ObjectFeed":
uri=interval_block_node.uri, interval=interval_block.interval
)

readings = self.get_readings(block, interval_block)
self.process_readings(block, interval_block)
interval_blocks.append(block)
combined_readings.extend(readings)

meter_readings.append(
ob.MeterReading(
title=mr_node.title,
uri=mr_node.uri,
reading_type=reading_type if reading_type else espi.ReadingType(),
intervalBlock=tuple(interval_blocks),
readings=tuple(combined_readings),
)
combined_readings.extend(block.readings)

reading = ob.MeterReading(
title=mr_node.title,
uri=mr_node.uri,
reading_type=reading_type if reading_type else espi.ReadingType(),
intervalBlock=tuple(interval_blocks),
readings=tuple(combined_readings),
)

for ib in reading.intervalBlock:
ib.compute_multiplier(reading.reading_type)
reading.patch()

meter_readings.append(reading)

service_kind = up.service_category.kind
if service_kind == "" or service_kind is None:
# TODO: We are forcing natural gas here. But we should either use
Expand All @@ -61,6 +65,9 @@ def build(self, entry_forest: ObjectForest) -> "ObjectFeed":
for mr in meter_readings:
mr.reading_type.uom = ob.UnitSymbol.THERM.value
mr.reading_type.power_of_ten_multiplier = espi.UnitMultiplierKindValue.VALUE_MINUS_3
for ib in mr.intervalBlock:
ib.compute_multiplier(mr.reading_type)

else:
service_kind = ob.ServiceKind(service_kind.value)

Expand All @@ -77,9 +84,7 @@ def build(self, entry_forest: ObjectForest) -> "ObjectFeed":

return self

def get_readings(
self, block: ob.IntervalBlock, interval_block: espi.IntervalBlock
) -> List[ob.IntervalReading]:
def process_readings(self, block: ob.IntervalBlock, interval_block: espi.IntervalBlock) -> None:
# TODO: If quality of reading is not in readings it might be in the
# top level description
readings = []
Expand All @@ -94,16 +99,11 @@ def get_readings(
dest_type=ob.QualityOfReading,
)
reading_values["quality_of_reading"] = quality_of_reading
readings.append(
ob.IntervalReading(
**reading_values,
parent=block,
)
)
reading_values["raw_value"] = interval_reading.value
readings.append(ob.IntervalReading(**reading_values, parent=block))
block.readings = readings
return readings

allowed_reading_keys = ("consumption_tier", "cost", "cpp", "time_period", "tou", "value")
allowed_reading_keys = ("consumption_tier", "cost", "cpp", "time_period", "tou")

def filter_allowed(self, interval_reading: espi.IntervalReading) -> dict[str, Any]:
reading_values = {}
Expand Down
33 changes: 31 additions & 2 deletions src/greenbutton_objects/orig_objects/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import greenbutton_objects.data.espi as espi
from greenbutton_objects.orig_objects import ServiceKind
from greenbutton_objects.orig_objects.enums import QualityOfReading
from greenbutton_objects.util import get_value


@dataclass
Expand All @@ -13,7 +14,7 @@ class DateTimeInterval:
duration: timedelta


@dataclass(order=True, frozen=True)
@dataclass(order=True)
class IntervalReading:
"""Specific value measured by a meter or other asset.
Expand All @@ -39,7 +40,7 @@ class IntervalReading:
"""

time_period: DateTimeInterval
value: float
raw_value: float

consumption_tier: Optional[int] = None
tou: Optional[int] = None
Expand All @@ -48,6 +49,17 @@ class IntervalReading:
parent: Optional["IntervalBlock"] = None
cost: float = float("NaN")
quality_of_reading: QualityOfReading = QualityOfReading.MISSING
reading_type: Optional[espi.ReadingType] = None

__cached_value: Optional[float] = None

@property
def value(self) -> float:
if self.__cached_value is None and self.reading_type is not None and self.parent is not None:
self.__cached_value = self.raw_value * self.parent.multiplier
else:
raise ValueError("Cannot auto scale raw_value. Not enough data")
return self.__cached_value


@dataclass
Expand All @@ -56,6 +68,19 @@ class IntervalBlock:
interval: DateTimeInterval
readings: List[IntervalReading] = field(default_factory=list)

multiplier: Optional[float] = None
reading_power_of_ten: Optional[float] = None

def compute_multiplier(self, reading_type: espi.ReadingType) -> None:
reading_power_ten = get_value(
reading_type.power_of_ten_multiplier,
src_type=espi.UnitMultiplierKindValue,
dest_type=espi.UnitMultiplierKindValue,
missing_val=espi.UnitMultiplierKindValue.VALUE_0,
)
self.reading_power_of_ten = reading_power_ten.value
self.multiplier = 10.0**self.reading_power_of_ten


@dataclass
class MeterReading:
Expand All @@ -65,6 +90,10 @@ class MeterReading:
readings: Tuple[IntervalReading, ...] = field(default_factory=tuple)
intervalBlock: Tuple[IntervalBlock, ...] = field(default_factory=tuple)

def patch(self):
for r in self.readings:
r.reading_type = self.reading_type


@dataclass
class UsagePoint:
Expand Down
22 changes: 1 addition & 21 deletions src/greenbutton_objects/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from greenbutton_objects.atom.object_tree import ObjectForest
from greenbutton_objects.data.espi import (
ReadingType,
UnitMultiplierKindValue,
UnitSymbolKindValue,
)
from greenbutton_objects.feed.feed import ObjectFeed
Expand Down Expand Up @@ -57,14 +56,6 @@ def parse_feed_representation(feed: ObjectFeed) -> str:
reading_type = mr.reading_type

uom_description, uom_symbol = get_uom_symbol(reading_type)
multiplier = compute_multiplier(reading_type)

# # EXCEPTION: Some natural gas providers in NorthEast do not add
# # proper metadata to their greenbutton file.
# # We'll fill it up for them here
# if not reading_type.uom and not reading_type.power_of_ten_multiplier:
# multiplier = 0.001
# uom_symbol = " therm"

result.append("Meter Reading (%s) %s:" % (mr.title, uom_description))
result.append("\n")
Expand All @@ -79,7 +70,7 @@ def parse_feed_representation(feed: ObjectFeed) -> str:
% (
time_start.strftime("%Y-%m-%d %H:%M:%S+00:00"),
str(timedelta(seconds=io.time_period.duration)), # type: ignore
io.value * multiplier,
io.value,
uom_symbol,
)
)
Expand Down Expand Up @@ -122,14 +113,3 @@ def get_uom_symbol(reading_type: ReadingType) -> tuple[str, str]:
uom_symbol = uom_strs[-1]

return uom_description, uom_symbol


def compute_multiplier(reading_type: ReadingType) -> float:
reading_power_ten = get_value(
reading_type.power_of_ten_multiplier,
src_type=UnitMultiplierKindValue,
dest_type=UnitMultiplierKindValue,
missing_val=UnitMultiplierKindValue.VALUE_0,
)

return 10.0**reading_power_ten.value
4 changes: 2 additions & 2 deletions tests/test_digested.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_gas_containerized(data_dir):
assert len(mr.readings) == 3

# Not this is not normalized
assert mr.readings[0].value == 12000
assert mr.readings[0].value == 12.000
assert mr.readings[0].cost == 2806000
assert mr.readings[0].quality_of_reading == QualityOfReading.VALIDATED

Expand Down Expand Up @@ -113,7 +113,7 @@ def test_gas_direct(data_dir):
assert len(mr.readings) == 5

# Not this is not normalized
assert mr.readings[0].value == 37000
assert mr.readings[0].value == 37.000
assert mr.readings[0].cost == 5100000
assert mr.readings[0].quality_of_reading == QualityOfReading.MISSING

Expand Down

0 comments on commit f00c6d4

Please sign in to comment.