Skip to content

Commit

Permalink
Merge pull request #1 from AdamFinkle/add_greenbutton
Browse files Browse the repository at this point in the history
Added greenbutton_objects to parse XML versions of natural gas and el…
  • Loading branch information
AdamFinkle authored Jul 3, 2024
2 parents 9033c6f + 31d6e9a commit a52ebab
Show file tree
Hide file tree
Showing 9 changed files with 2,842 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ node_modules
# Easy way to create temporary files/folders that won't accidentally be added to git
*.local.*

# PyCharm
.idea/

#local temporary folders
heat-app
venv
Expand Down
3 changes: 2 additions & 1 deletion rules-engine/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ name="rules-engine"
version="0.0.1"
requires-python=">=3.11.3"
dependencies = [
"pydantic"
"pydantic",
"greenbutton_objects"
]

[project.optional-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions rules-engine/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#
annotated-types==0.5.0
# via pydantic
greenbutton-objects==2024.7.3
# via rules-engine (pyproject.toml)
pydantic==2.3.0
# via rules-engine (pyproject.toml)
pydantic-core==2.6.3
Expand Down
56 changes: 55 additions & 1 deletion rules-engine/src/rules_engine/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
from datetime import datetime, timedelta
from enum import StrEnum

from .pydantic_models import NaturalGasBillingInput, NaturalGasBillingRecordInput
from greenbutton_objects import parse
from .pydantic_models import (
NaturalGasBillingInput,
NaturalGasBillingRecordInput,
ElectricBillingInput,
ElectricBillingRecordInput
)


class NaturalGasCompany(StrEnum):
Expand Down Expand Up @@ -177,3 +183,51 @@ def _parse_gas_bill_national_grid(data: str) -> NaturalGasBillingInput:
records.append(record)

return NaturalGasBillingInput(records=records)


def _parse_gas_bill_xml(path: str) -> NaturalGasBillingInput:
"""
Return a list of gas bill data parsed from a Green Button XML
received as a string.
"""
ups = parse.parse_feed(path)
records = []
for up in ups:
for mr in up.meterReadings:
for ir in mr.intervalReadings:
records.append(
NaturalGasBillingRecordInput(
period_start_date=ir.timePeriod.start.date(),
period_end_date=(
ir.timePeriod.start + ir.timePeriod.duration
).date(),
usage_therms=ir.value,
inclusion_override=None,
)
)

return NaturalGasBillingInput(records=records)


def _parse_electric_bill_xml(path: str) -> NaturalGasBillingInput:
"""
Return a list of gas bill data parsed from a Green Button XML
received as a string.
"""
ups = parse.parse_feed(path)
records = []
for up in ups:
for mr in up.meterReadings:
for ir in mr.intervalReadings:
records.append(
ElectricBillingRecordInput(
period_start_date=ir.timePeriod.start.date(),
period_end_date=(
ir.timePeriod.start + ir.timePeriod.duration
).date(),
usage_watt_hours=ir.value,
inclusion_override=None,
)
)

return ElectricBillingInput(records=records)
59 changes: 53 additions & 6 deletions rules-engine/src/rules_engine/pydantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,53 @@ class NaturalGasBillingRecordInput(BaseModel):
inclusion_override: Optional[AnalysisType] = Field(description="Natural Gas!E")


class ElectricBillingRecordInput(BaseModel):
"""From Electric tab. A single row of the Billing input table."""

period_start_date: date = Field(description="Electric!A")
period_end_date: date = Field(description="Electric!B")
usage_watt_hours: float = Field(description="Electric!D")
inclusion_override: Optional[AnalysisType] = Field(description="Electric!E")


class ElectricBillingInput(BaseModel):
"""From Electric tab. Container for holding all rows of the billing input table."""

records: Sequence[ElectricBillingRecordInput]

# Suppress mypy error when computed_field is used with cached_property; see https://github.com/python/mypy/issues/1362
@computed_field # type: ignore[misc]
@cached_property
def overall_start_date(self) -> date:
if len(self.records) == 0:
raise ValueError(
"Electric billing records cannot be empty.\n"
+ "Could not calculate overall start date from empty electricicity billing records.\n"
+ "Try again with non-empty electricicity billing records.\n"
)

min_date = date.max
for record in self.records:
min_date = min(min_date, record.period_start_date)
return min_date

# Suppress mypy error when computed_field is used with cached_property; see https://github.com/python/mypy/issues/1362
@computed_field # type: ignore[misc]
@cached_property
def overall_end_date(self) -> date:
if len(self.records) == 0:
raise ValueError(
"Electric billing records cannot be empty.\n"
+ "Could not calculate overall start date from empty electricicity billing records.\n"
+ "Try again with non-empty electricicity billing records.\n"
)

max_date = date.min
for record in self.records:
max_date = max(max_date, record.period_end_date)
return max_date


class NaturalGasBillingInput(BaseModel):
"""From Natural Gas tab. Container for holding all rows of the billing input table."""

Expand All @@ -113,9 +160,9 @@ class NaturalGasBillingInput(BaseModel):
def overall_start_date(self) -> date:
if len(self.records) == 0:
raise ValueError(
"Natural gas billing records cannot be empty."
+ "Could not calculate overall start date from empty natural gas billing records."
+ "Try again with non-empty natural gas billing records."
"Natural gas billing records cannot be empty.\n"
+ "Could not calculate overall start date from empty natural gas billing records.\n"
+ "Try again with non-empty natural gas billing records.\n"
)

min_date = date.max
Expand All @@ -129,9 +176,9 @@ def overall_start_date(self) -> date:
def overall_end_date(self) -> date:
if len(self.records) == 0:
raise ValueError(
"Natural gas billing records cannot be empty."
+ "Could not calculate overall start date from empty natural gas billing records."
+ "Try again with non-empty natural gas billing records."
"Natural gas billing records cannot be empty.\n"
+ "Could not calculate overall start date from empty natural gas billing records.\n"
+ "Try again with non-empty natural gas billing records.\n"
)

max_date = date.min
Expand Down
Loading

0 comments on commit a52ebab

Please sign in to comment.