From 31d6e9ab0a339322a2a84217734da7a61261744c Mon Sep 17 00:00:00 2001 From: Vinyl Date: Wed, 3 Jul 2024 15:42:01 -0400 Subject: [PATCH 1/5] Added greenbutton_objects to parse XML versions of natural gas and electric bills, alongside pydantic models and tests for the same. --- .gitignore | 3 + rules-engine/pyproject.toml | 3 +- rules-engine/requirements.txt | 2 + rules-engine/src/rules_engine/parser.py | 56 +- .../src/rules_engine/pydantic_models.py | 59 +- .../TestGBDataHourlyNineDaysBinnedDaily.xml | 2263 +++++++++++++++++ ...a_Service 1_1_2021-05-26_to_2024-04-25.xml | 347 +++ .../tests/test_rules_engine/test_parser.py | 65 +- .../test_rules_engine/test_pydantic_models.py | 59 +- 9 files changed, 2842 insertions(+), 15 deletions(-) create mode 100644 rules-engine/tests/test_rules_engine/cases/parsing/xml/electricicity/TestGBDataHourlyNineDaysBinnedDaily.xml create mode 100644 rules-engine/tests/test_rules_engine/cases/parsing/xml/natural_gas/ngma_natural_gas_billing_billing_data_Service 1_1_2021-05-26_to_2024-04-25.xml diff --git a/.gitignore b/.gitignore index ee899313..2d32b8e5 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/rules-engine/pyproject.toml b/rules-engine/pyproject.toml index e9626739..8e08a77e 100644 --- a/rules-engine/pyproject.toml +++ b/rules-engine/pyproject.toml @@ -7,7 +7,8 @@ name="rules-engine" version="0.0.1" requires-python=">=3.11.3" dependencies = [ - "pydantic" + "pydantic", + "greenbutton_objects" ] [project.optional-dependencies] diff --git a/rules-engine/requirements.txt b/rules-engine/requirements.txt index 2afeaaa2..c13d6bce 100644 --- a/rules-engine/requirements.txt +++ b/rules-engine/requirements.txt @@ -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 diff --git a/rules-engine/src/rules_engine/parser.py b/rules-engine/src/rules_engine/parser.py index ca966e65..525b6c24 100644 --- a/rules-engine/src/rules_engine/parser.py +++ b/rules-engine/src/rules_engine/parser.py @@ -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): @@ -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) \ No newline at end of file diff --git a/rules-engine/src/rules_engine/pydantic_models.py b/rules-engine/src/rules_engine/pydantic_models.py index dfb5a081..d4c9cc6c 100644 --- a/rules-engine/src/rules_engine/pydantic_models.py +++ b/rules-engine/src/rules_engine/pydantic_models.py @@ -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.""" @@ -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 @@ -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 diff --git a/rules-engine/tests/test_rules_engine/cases/parsing/xml/electricicity/TestGBDataHourlyNineDaysBinnedDaily.xml b/rules-engine/tests/test_rules_engine/cases/parsing/xml/electricicity/TestGBDataHourlyNineDaysBinnedDaily.xml new file mode 100644 index 00000000..db4ec6f2 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/parsing/xml/electricicity/TestGBDataHourlyNineDaysBinnedDaily.xml @@ -0,0 +1,2263 @@ + + + + + + + + urn:uuid:6D8BE83A-4B44-414C-B828-20809ACF30D1 + Green Button Subscription Feed + 2013-09-19T04:00:00Z + + + urn:uuid:E2DCF5F0-810B-443F-9A2E-805BFA52D897 + + + + + + Green Button Sample Data File + + + + 0 + + + sample tariff showing block and tier pricing + ./TariffSample.xml + + + + 2013-09-19T04:00:00Z + 2013-09-19T04:00:00Z + + + urn:uuid:D5CB5FAC-EC43-4A23-B85D-8294A5A6DE0C + + + DST For North America + + + B40E2000 + 3600 + 360E2000 + -18000 + + + 2013-09-19T04:00:00Z + 2013-09-19T04:00:00Z + + + urn:uuid:AE1F66F3-C635-4748-8FB7-AFF918B9D9A8 + + + + + Monthly Electricity Consumption + + + + 2013-09-19T04:00:00Z + 2013-09-19T04:00:00Z + + + urn:uuid:C0E9C7A7-4942-4EB3-BBA5-D31CE921812E + + + Type of Meter Reading Data + + + 4 + 1 + 840 + 12 + 1 + 3600 + 12 + 769 + 0 + 0 + 72 + + + 2013-09-19T04:00:00Z + 2013-09-19T04:00:00Z + + + urn:uuid:0F3403E5-AFC3-4A86-B8A3-CA07334D67A9 + + + + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1388552400</start> + <!-- start date: 1/1/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388552400</start> + <!-- 1/1/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388556000</start> + <!-- 1/1/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388559600</start> + <!-- 1/1/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388563200</start> + <!-- 1/1/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388566800</start> + <!-- 1/1/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1388570400</start> + <!-- 1/1/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388574000</start> + <!-- 1/1/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388577600</start> + <!-- 1/1/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388581200</start> + <!-- 1/1/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388584800</start> + <!-- 1/1/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388588400</start> + <!-- 1/1/2014 3:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388592000</start> + <!-- 1/1/2014 4:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388595600</start> + <!-- 1/1/2014 5:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388599200</start> + <!-- 1/1/2014 6:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>9828</cost> + <timePeriod> + <duration>3600</duration> + <start>1388602800</start> + <!-- 1/1/2014 7:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>14742</cost> + <timePeriod> + <duration>3600</duration> + <start>1388606400</start> + <!-- 1/1/2014 8:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>19656</cost> + <timePeriod> + <duration>3600</duration> + <start>1388610000</start> + <!-- 1/1/2014 9:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1388613600</start> + <!-- 1/1/2014 10:00:00 PM --> + </timePeriod> + <value>1092</value> +</IntervalReading> +<IntervalReading> + <cost>40950</cost> + <timePeriod> + <duration>3600</duration> + <start>1388617200</start> + <!-- 1/1/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1388620800</start> + <!-- 1/2/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>24570</cost> + <timePeriod> + <duration>3600</duration> + <start>1388624400</start> + <!-- 1/2/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1388628000</start> + <!-- 1/2/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388631600</start> + <!-- 1/2/2014 3:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388635200</start> + <!-- 1/2/2014 4:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-02T05:00:00Z</published> + <updated>2014-01-02T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:2DFA4F2A-3A9A-4C1D-84E8-9EA0CD29310C</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/178"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1388638800</start> + <!-- start date: 1/2/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388638800</start> + <!-- 1/2/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388642400</start> + <!-- 1/2/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388646000</start> + <!-- 1/2/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388649600</start> + <!-- 1/2/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388653200</start> + <!-- 1/2/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1388656800</start> + <!-- 1/2/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388660400</start> + <!-- 1/2/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388664000</start> + <!-- 1/2/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388667600</start> + <!-- 1/2/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388671200</start> + <!-- 1/2/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388674800</start> + <!-- 1/2/2014 3:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388678400</start> + <!-- 1/2/2014 4:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388682000</start> + <!-- 1/2/2014 5:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388685600</start> + <!-- 1/2/2014 6:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>9828</cost> + <timePeriod> + <duration>3600</duration> + <start>1388689200</start> + <!-- 1/2/2014 7:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>14742</cost> + <timePeriod> + <duration>3600</duration> + <start>1388692800</start> + <!-- 1/2/2014 8:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>19656</cost> + <timePeriod> + <duration>3600</duration> + <start>1388696400</start> + <!-- 1/2/2014 9:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1388700000</start> + <!-- 1/2/2014 10:00:00 PM --> + </timePeriod> + <value>1092</value> +</IntervalReading> +<IntervalReading> + <cost>40950</cost> + <timePeriod> + <duration>3600</duration> + <start>1388703600</start> + <!-- 1/2/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1388707200</start> + <!-- 1/3/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>24570</cost> + <timePeriod> + <duration>3600</duration> + <start>1388710800</start> + <!-- 1/3/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1388714400</start> + <!-- 1/3/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388718000</start> + <!-- 1/3/2014 3:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388721600</start> + <!-- 1/3/2014 4:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-03T05:00:00Z</published> + <updated>2014-01-03T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:6ECF6DB5-771F-4782-8130-CE4B4C3697D4</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/179"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1388725200</start> + <!-- start date: 1/3/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388725200</start> + <!-- 1/3/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388728800</start> + <!-- 1/3/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388732400</start> + <!-- 1/3/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388736000</start> + <!-- 1/3/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388739600</start> + <!-- 1/3/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1388743200</start> + <!-- 1/3/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388746800</start> + <!-- 1/3/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388750400</start> + <!-- 1/3/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388754000</start> + <!-- 1/3/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388757600</start> + <!-- 1/3/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388761200</start> + <!-- 1/3/2014 3:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388764800</start> + <!-- 1/3/2014 4:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388768400</start> + <!-- 1/3/2014 5:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388772000</start> + <!-- 1/3/2014 6:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>9828</cost> + <timePeriod> + <duration>3600</duration> + <start>1388775600</start> + <!-- 1/3/2014 7:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>14742</cost> + <timePeriod> + <duration>3600</duration> + <start>1388779200</start> + <!-- 1/3/2014 8:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>19656</cost> + <timePeriod> + <duration>3600</duration> + <start>1388782800</start> + <!-- 1/3/2014 9:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1388786400</start> + <!-- 1/3/2014 10:00:00 PM --> + </timePeriod> + <value>1092</value> +</IntervalReading> +<IntervalReading> + <cost>40950</cost> + <timePeriod> + <duration>3600</duration> + <start>1388790000</start> + <!-- 1/3/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1388793600</start> + <!-- 1/4/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>12285</cost> + <timePeriod> + <duration>3600</duration> + <start>1388797200</start> + <!-- 1/4/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388800800</start> + <!-- 1/4/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4095</cost> + <timePeriod> + <duration>3600</duration> + <start>1388804400</start> + <!-- 1/4/2014 3:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1388808000</start> + <!-- 1/4/2014 4:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-04T05:00:00Z</published> + <updated>2014-01-04T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:4D8CEE3E-2DA9-45F3-AE00-12AE29DC2CB9</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/17A"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1388811600</start> + <!-- start date: 1/4/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388811600</start> + <!-- 1/4/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388815200</start> + <!-- 1/4/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388818800</start> + <!-- 1/4/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388822400</start> + <!-- 1/4/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388826000</start> + <!-- 1/4/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1388829600</start> + <!-- 1/4/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388833200</start> + <!-- 1/4/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388836800</start> + <!-- 1/4/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388840400</start> + <!-- 1/4/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388844000</start> + <!-- 1/4/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388847600</start> + <!-- 1/4/2014 3:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388851200</start> + <!-- 1/4/2014 4:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388854800</start> + <!-- 1/4/2014 5:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388858400</start> + <!-- 1/4/2014 6:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>12285</cost> + <timePeriod> + <duration>3600</duration> + <start>1388862000</start> + <!-- 1/4/2014 7:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1388865600</start> + <!-- 1/4/2014 8:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>20475</cost> + <timePeriod> + <duration>3600</duration> + <start>1388869200</start> + <!-- 1/4/2014 9:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>20475</cost> + <timePeriod> + <duration>3600</duration> + <start>1388872800</start> + <!-- 1/4/2014 10:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>20475</cost> + <timePeriod> + <duration>3600</duration> + <start>1388876400</start> + <!-- 1/4/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1388880000</start> + <!-- 1/5/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>12285</cost> + <timePeriod> + <duration>3600</duration> + <start>1388883600</start> + <!-- 1/5/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388887200</start> + <!-- 1/5/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4095</cost> + <timePeriod> + <duration>3600</duration> + <start>1388890800</start> + <!-- 1/5/2014 3:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1388894400</start> + <!-- 1/5/2014 4:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-05T05:00:00Z</published> + <updated>2014-01-05T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:CB28BDD7-01CD-4972-A7C5-9D69400172C9</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/17B"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1388898000</start> + <!-- start date: 1/5/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388898000</start> + <!-- 1/5/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388901600</start> + <!-- 1/5/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388905200</start> + <!-- 1/5/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388908800</start> + <!-- 1/5/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388912400</start> + <!-- 1/5/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1388916000</start> + <!-- 1/5/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388919600</start> + <!-- 1/5/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388923200</start> + <!-- 1/5/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388926800</start> + <!-- 1/5/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388930400</start> + <!-- 1/5/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388934000</start> + <!-- 1/5/2014 3:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388937600</start> + <!-- 1/5/2014 4:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388941200</start> + <!-- 1/5/2014 5:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1388944800</start> + <!-- 1/5/2014 6:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>12285</cost> + <timePeriod> + <duration>3600</duration> + <start>1388948400</start> + <!-- 1/5/2014 7:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1388952000</start> + <!-- 1/5/2014 8:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>20475</cost> + <timePeriod> + <duration>3600</duration> + <start>1388955600</start> + <!-- 1/5/2014 9:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>20475</cost> + <timePeriod> + <duration>3600</duration> + <start>1388959200</start> + <!-- 1/5/2014 10:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>20475</cost> + <timePeriod> + <duration>3600</duration> + <start>1388962800</start> + <!-- 1/5/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1388966400</start> + <!-- 1/6/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>24570</cost> + <timePeriod> + <duration>3600</duration> + <start>1388970000</start> + <!-- 1/6/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1388973600</start> + <!-- 1/6/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1388977200</start> + <!-- 1/6/2014 3:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388980800</start> + <!-- 1/6/2014 4:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-06T05:00:00Z</published> + <updated>2014-01-06T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:406E744C-206D-4A77-9335-799BBE69366E</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/17C"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1388984400</start> + <!-- start date: 1/6/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388984400</start> + <!-- 1/6/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388988000</start> + <!-- 1/6/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388991600</start> + <!-- 1/6/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388995200</start> + <!-- 1/6/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1388998800</start> + <!-- 1/6/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1389002400</start> + <!-- 1/6/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389006000</start> + <!-- 1/6/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389009600</start> + <!-- 1/6/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389013200</start> + <!-- 1/6/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389016800</start> + <!-- 1/6/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389020400</start> + <!-- 1/6/2014 3:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389024000</start> + <!-- 1/6/2014 4:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389027600</start> + <!-- 1/6/2014 5:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389031200</start> + <!-- 1/6/2014 6:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>9828</cost> + <timePeriod> + <duration>3600</duration> + <start>1389034800</start> + <!-- 1/6/2014 7:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>14742</cost> + <timePeriod> + <duration>3600</duration> + <start>1389038400</start> + <!-- 1/6/2014 8:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>19656</cost> + <timePeriod> + <duration>3600</duration> + <start>1389042000</start> + <!-- 1/6/2014 9:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389045600</start> + <!-- 1/6/2014 10:00:00 PM --> + </timePeriod> + <value>1092</value> +</IntervalReading> +<IntervalReading> + <cost>40950</cost> + <timePeriod> + <duration>3600</duration> + <start>1389049200</start> + <!-- 1/6/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389052800</start> + <!-- 1/7/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>24570</cost> + <timePeriod> + <duration>3600</duration> + <start>1389056400</start> + <!-- 1/7/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1389060000</start> + <!-- 1/7/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389063600</start> + <!-- 1/7/2014 3:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389067200</start> + <!-- 1/7/2014 4:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-07T05:00:00Z</published> + <updated>2014-01-07T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:909B5CB8-A5F6-4BF3-B643-0E1194812F0B</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/17D"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1389070800</start> + <!-- start date: 1/7/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389070800</start> + <!-- 1/7/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389074400</start> + <!-- 1/7/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389078000</start> + <!-- 1/7/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389081600</start> + <!-- 1/7/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389085200</start> + <!-- 1/7/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1389088800</start> + <!-- 1/7/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389092400</start> + <!-- 1/7/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389096000</start> + <!-- 1/7/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389099600</start> + <!-- 1/7/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389103200</start> + <!-- 1/7/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389106800</start> + <!-- 1/7/2014 3:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389110400</start> + <!-- 1/7/2014 4:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389114000</start> + <!-- 1/7/2014 5:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389117600</start> + <!-- 1/7/2014 6:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>9828</cost> + <timePeriod> + <duration>3600</duration> + <start>1389121200</start> + <!-- 1/7/2014 7:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>14742</cost> + <timePeriod> + <duration>3600</duration> + <start>1389124800</start> + <!-- 1/7/2014 8:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>19656</cost> + <timePeriod> + <duration>3600</duration> + <start>1389128400</start> + <!-- 1/7/2014 9:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389132000</start> + <!-- 1/7/2014 10:00:00 PM --> + </timePeriod> + <value>1092</value> +</IntervalReading> +<IntervalReading> + <cost>40950</cost> + <timePeriod> + <duration>3600</duration> + <start>1389135600</start> + <!-- 1/7/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389139200</start> + <!-- 1/8/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>24570</cost> + <timePeriod> + <duration>3600</duration> + <start>1389142800</start> + <!-- 1/8/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1389146400</start> + <!-- 1/8/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389150000</start> + <!-- 1/8/2014 3:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389153600</start> + <!-- 1/8/2014 4:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-08T05:00:00Z</published> + <updated>2014-01-08T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:10E830C7-F829-4D11-8141-5FE26C93CF11</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/17E"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1389157200</start> + <!-- start date: 1/8/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389157200</start> + <!-- 1/8/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389160800</start> + <!-- 1/8/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389164400</start> + <!-- 1/8/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389168000</start> + <!-- 1/8/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389171600</start> + <!-- 1/8/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1389175200</start> + <!-- 1/8/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389178800</start> + <!-- 1/8/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389182400</start> + <!-- 1/8/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389186000</start> + <!-- 1/8/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389189600</start> + <!-- 1/8/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389193200</start> + <!-- 1/8/2014 3:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389196800</start> + <!-- 1/8/2014 4:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389200400</start> + <!-- 1/8/2014 5:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389204000</start> + <!-- 1/8/2014 6:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>9828</cost> + <timePeriod> + <duration>3600</duration> + <start>1389207600</start> + <!-- 1/8/2014 7:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>14742</cost> + <timePeriod> + <duration>3600</duration> + <start>1389211200</start> + <!-- 1/8/2014 8:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>19656</cost> + <timePeriod> + <duration>3600</duration> + <start>1389214800</start> + <!-- 1/8/2014 9:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389218400</start> + <!-- 1/8/2014 10:00:00 PM --> + </timePeriod> + <value>1092</value> +</IntervalReading> +<IntervalReading> + <cost>40950</cost> + <timePeriod> + <duration>3600</duration> + <start>1389222000</start> + <!-- 1/8/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389225600</start> + <!-- 1/9/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>24570</cost> + <timePeriod> + <duration>3600</duration> + <start>1389229200</start> + <!-- 1/9/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1389232800</start> + <!-- 1/9/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389236400</start> + <!-- 1/9/2014 3:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389240000</start> + <!-- 1/9/2014 4:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-09T05:00:00Z</published> + <updated>2014-01-09T05:00:00Z</updated> +</entry> +<entry> + <id>urn:uuid:C45E83E0-96FC-43DA-AED9-B9B3F2A5DFA7</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock/17F"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/MeterReading/01/IntervalBlock"/> + <title/> + <content> +<IntervalBlock xmlns="http://naesb.org/espi"> + <interval> + <duration>86400</duration> + <start>1389243600</start> + <!-- start date: 1/9/2014 5:00:00 AM --> + </interval> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389243600</start> + <!-- 1/9/2014 5:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389247200</start> + <!-- 1/9/2014 6:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389250800</start> + <!-- 1/9/2014 7:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389254400</start> + <!-- 1/9/2014 8:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389258000</start> + <!-- 1/9/2014 9:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +<IntervalReading> + <cost>2457</cost> + <timePeriod> + <duration>3600</duration> + <start>1389261600</start> + <!-- 1/9/2014 10:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389265200</start> + <!-- 1/9/2014 11:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389268800</start> + <!-- 1/9/2014 12:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389272400</start> + <!-- 1/9/2014 1:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>8190</cost> + <timePeriod> + <duration>3600</duration> + <start>1389276000</start> + <!-- 1/9/2014 2:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389279600</start> + <!-- 1/9/2014 3:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389283200</start> + <!-- 1/9/2014 4:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389286800</start> + <!-- 1/9/2014 5:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389290400</start> + <!-- 1/9/2014 6:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>9828</cost> + <timePeriod> + <duration>3600</duration> + <start>1389294000</start> + <!-- 1/9/2014 7:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>14742</cost> + <timePeriod> + <duration>3600</duration> + <start>1389297600</start> + <!-- 1/9/2014 8:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>19656</cost> + <timePeriod> + <duration>3600</duration> + <start>1389301200</start> + <!-- 1/9/2014 9:00:00 PM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389304800</start> + <!-- 1/9/2014 10:00:00 PM --> + </timePeriod> + <value>1092</value> +</IntervalReading> +<IntervalReading> + <cost>40950</cost> + <timePeriod> + <duration>3600</duration> + <start>1389308400</start> + <!-- 1/9/2014 11:00:00 PM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>32760</cost> + <timePeriod> + <duration>3600</duration> + <start>1389312000</start> + <!-- 1/10/2014 --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>24570</cost> + <timePeriod> + <duration>3600</duration> + <start>1389315600</start> + <!-- 1/10/2014 1:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>16380</cost> + <timePeriod> + <duration>3600</duration> + <start>1389319200</start> + <!-- 1/10/2014 2:00:00 AM --> + </timePeriod> + <value>1365</value> +</IntervalReading> +<IntervalReading> + <cost>4914</cost> + <timePeriod> + <duration>3600</duration> + <start>1389322800</start> + <!-- 1/10/2014 3:00:00 AM --> + </timePeriod> + <value>819</value> +</IntervalReading> +<IntervalReading> + <cost>819</cost> + <timePeriod> + <duration>3600</duration> + <start>1389326400</start> + <!-- 1/10/2014 4:00:00 AM --> + </timePeriod> + <value>273</value> +</IntervalReading> +</IntervalBlock> + </content> + <published>2014-01-10T05:00:00Z</published> + <updated>2014-01-10T05:00:00Z</updated> +</entry> + <entry> + <id>urn:uuid:E63113F5-0999-470F-AFB6-7722AEE2FB1D</id> + <link rel="self" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/ElectricPowerUsageSummary/01"/> + <link rel="up" href="https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/RetailCustomer/2/UsagePoint/2/ElectricPowerUsageSummary"/> + <title>Usage Summary + + + + 2419200 + 1388552400 + + 2208000 + 0 + 0 + 840 + + 0 + 72 + 199563 + + + 0 + 1389330000 + 72 + 0 + + 14 + 1389330000 + + + 2014-01-29T05:00:00Z + 2014-01-29T05:00:00Z + + diff --git a/rules-engine/tests/test_rules_engine/cases/parsing/xml/natural_gas/ngma_natural_gas_billing_billing_data_Service 1_1_2021-05-26_to_2024-04-25.xml b/rules-engine/tests/test_rules_engine/cases/parsing/xml/natural_gas/ngma_natural_gas_billing_billing_data_Service 1_1_2021-05-26_to_2024-04-25.xml new file mode 100644 index 00000000..153b0e2e --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/parsing/xml/natural_gas/ngma_natural_gas_billing_billing_data_Service 1_1_2021-05-26_to_2024-04-25.xml @@ -0,0 +1,347 @@ + + + urn:uuid:5762c9e8-4e65-3b0c-83b3-7874683f3dbe + + + Opower ESPI Third Party Batch Feed v1 + 2024-06-21 + + urn:uuid:db678d4d-d9fe-3c0c-96f9-c13142c295e1 + + + + + 1 MAIN ST, ANYTOWN ME 12345 + 2024-06-21 + 2011-11-30T12:00:00.000Z + + + + 1 + + + + + + urn:uuid:cb36cc47-2cfb-3e47-abb9-7866f42cc194 + + + + + + + 2024-06-21 + 2011-11-30T12:00:00.000Z + + + + + + + urn:uuid:e7c7cd10-5e47-3d9c-8684-bd9e7eb401d3 + + + 2024-06-21 + 2011-11-30T12:00:00.000Z + + + 840 + -3 + 169 + + + + + urn:uuid:1efd2ba9-8c1a-3937-b7c0-ae55733e9e5e + + + + + + 92102400 + 1622005200 + + + 5100000 + + 3024000 + 1621987200 + + 37000 + + + 2493000 + + 2419200 + 1625011200 + + 14000 + + + 3281000 + + 2592000 + 1627430400 + + 21000 + + + 4207000 + + 2851200 + 1630022400 + + 27000 + + + 5543000 + + 2332800 + 1632873600 + + 41000 + + + 23042000 + + 2595600 + 1635206400 + + 131000 + + + 41220000 + + 2851200 + 1637798400 + + 215000 + + + 42806000 + + 2505600 + 1640649600 + + 231000 + + + 43142000 + + 2505600 + 1643155200 + + 234000 + + + 35127000 + + 2588400 + 1645660800 + + 173000 + + + 24299000 + + 2764800 + 1648252800 + + 115000 + + + 10578000 + + 2592000 + 1651017600 + + 58000 + + + 5452000 + + 2764800 + 1653609600 + + 24000 + + + 4880000 + + 2505600 + 1656374400 + + 21000 + + + 3680000 + + 2592000 + 1658880000 + + 17000 + + + 4039000 + + 2505600 + 1661472000 + + 17000 + + + 17322000 + + 2764800 + 1663977600 + + 86000 + + + 32887000 + + 2941200 + 1666742400 + + 140000 + + + 42535000 + + 2419200 + 1669680000 + + 184000 + + + 44808000 + + 2592000 + 1672099200 + + 194000 + + + 43253000 + + 2505600 + 1674691200 + + 194000 + + + 40932000 + + 2761200 + 1677196800 + + 199000 + + + 22680000 + + 2678400 + 1679961600 + + 110000 + + + 9525000 + + 2419200 + 1682640000 + + 57000 + + + 4806000 + + 2764800 + 1685059200 + + 28000 + + + 3548000 + + 2592000 + 1687824000 + + 20000 + + + 3533000 + + 2592000 + 1690416000 + + 20000 + + + 3639000 + + 2764800 + 1693008000 + + 21000 + + + 7841000 + + 2505600 + 1695772800 + + 58000 + + + 31108000 + + 2941200 + 1698278400 + + 137000 + + + 28456000 + + 2505600 + 1701216000 + + 122000 + + + 39210000 + + 2505600 + 1703721600 + + 170000 + + + 33385000 + + 2505600 + 1706227200 + + 144000 + + + 31040000 + + 2761200 + 1708732800 + + 133000 + + + 21314000 + + 2592000 + 1711497600 + + 91000 + + + + + \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/test_parser.py b/rules-engine/tests/test_rules_engine/test_parser.py index 43fa3401..8ea72db5 100644 --- a/rules-engine/tests/test_rules_engine/test_parser.py +++ b/rules-engine/tests/test_rules_engine/test_parser.py @@ -4,7 +4,10 @@ import pytest from rules_engine import parser -from rules_engine.pydantic_models import NaturalGasBillingRecordInput +from rules_engine.pydantic_models import ( + NaturalGasBillingRecordInput, + ElectricBillingRecordInput +) ROOT_DIR = pathlib.Path(__file__).parent / "cases" / "examples" @@ -24,6 +27,26 @@ def _read_gas_bill_national_grid() -> str: return f.read() +def _get_gas_bill_xml_path() -> pathlib.Path: + """Return the path of a test natural gas XML bill""" + return (pathlib.Path(__file__).parent + / "cases" + / "parsing" + / "xml" + / "natural_gas" + / "ngma_natural_gas_billing_billing_data_Service 1_1_2021-05-26_to_2024-04-25.xml") + + +def _get_electric_bill_xml_path() -> pathlib.Path: + """Return the path of a test natural gas XML bill""" + return (pathlib.Path(__file__).parent + / "cases" + / "parsing" + / "xml" + / "electricicity" + / "TestGBDataHourlyNineDaysBinnedDaily.xml") + + def _validate_eversource(result): """Validate the result of reading an Eversource CSV.""" assert len(result.records) == 36 @@ -58,6 +81,34 @@ def _validate_national_grid(result): assert second_row.inclusion_override == None +def _validate_gas_bill_xml(result): + """Validate the result of reading a National Grid CSV.""" + assert len(result.records) == 35 + for row in result.records: + assert isinstance(row, NaturalGasBillingRecordInput) + + second_row = result.records[1] + assert second_row.period_start_date == date(2021, 6, 29) + assert second_row.period_end_date == date(2021, 7, 27) + assert isinstance(second_row.usage_therms, float) + assert second_row.usage_therms == 14 + assert second_row.inclusion_override == None + + +def _validate_electric_bill_xml(result): + """Validate the result of reading a National Grid CSV.""" + assert len(result.records) == 216 + for row in result.records: + assert isinstance(row, ElectricBillingRecordInput) + + second_row = result.records[1] + assert second_row.period_start_date == date(2014, 1, 1) + assert second_row.period_end_date == date(2014, 1, 1) + assert isinstance(second_row.usage_watt_hours, float) + assert second_row.usage_watt_hours == 273 + assert second_row.inclusion_override == None + + def test_parse_gas_bill(): """ Tests the logic of parse_gas_bill. @@ -88,6 +139,18 @@ def test_parse_gas_bill_national_grid(): ) +def test_parse_gas_bill_xml(): + """Tests parsing a natural gas bill from a Green Button XML.""" + _validate_gas_bill_xml( + parser._parse_gas_bill_xml(_get_gas_bill_xml_path())) + + +def test_parse_electric_bill_xml(): + """Tests parsing an electricicity bill from a Green Button XML.""" + _validate_electric_bill_xml( + parser._parse_electric_bill_xml(_get_electric_bill_xml_path())) + + def test_detect_natural_gas_company(): """Tests if the natural gas company is correctly detected from the parsed csv.""" read_eversource = _read_gas_bill_eversource() diff --git a/rules-engine/tests/test_rules_engine/test_pydantic_models.py b/rules-engine/tests/test_rules_engine/test_pydantic_models.py index 1ca88ce9..35108a75 100644 --- a/rules-engine/tests/test_rules_engine/test_pydantic_models.py +++ b/rules-engine/tests/test_rules_engine/test_pydantic_models.py @@ -5,9 +5,11 @@ from rules_engine.pydantic_models import ( NaturalGasBillingInput, NaturalGasBillingRecordInput, + ElectricBillingInput, + ElectricBillingRecordInput ) -_EXAMPLE_VALID_RECORDS = NaturalGasBillingInput( +_EXAMPLE_VALID_RECORDS_NATURAL_GAS = NaturalGasBillingInput( records=[ NaturalGasBillingRecordInput( period_start_date=date(2020, 1, 1), @@ -24,30 +26,75 @@ ] ) -_EXAMPLE_INVALID_RECORDS = NaturalGasBillingInput( +_EXAMPLE_VALID_RECORDS_ELECTRIC = ElectricBillingInput( + records=[ + ElectricBillingRecordInput( + period_start_date=date(2020, 1, 1), + period_end_date=date(2020, 1, 31), + usage_watt_hours=10, + inclusion_override=None, + ), + ElectricBillingRecordInput( + period_start_date=date(2020, 2, 1), + period_end_date=date(2020, 2, 28), + usage_watt_hours=10, + inclusion_override=None, + ), + ] +) + +_EXAMPLE_INVALID_RECORDS_NATURAL_GAS = NaturalGasBillingInput( + records=[] # create billing input with no records +) + +_EXAMPLE_INVALID_RECORDS_ELECTRIC = ElectricBillingInput( records=[] # create billing input with no records ) def test_natural_gas_billing_input_overall_start_date(): expected_overall_start_date = date(2020, 1, 1) - actual_overall_start_date = _EXAMPLE_VALID_RECORDS.overall_start_date + actual_overall_start_date = _EXAMPLE_VALID_RECORDS_NATURAL_GAS.overall_start_date assert expected_overall_start_date == actual_overall_start_date def test_natural_gas_billing_input_overall_end_date(): expected_overall_end_date = date(2020, 2, 28) - actual_overall_end_date = _EXAMPLE_VALID_RECORDS.overall_end_date + actual_overall_end_date = _EXAMPLE_VALID_RECORDS_NATURAL_GAS.overall_end_date + + assert expected_overall_end_date == actual_overall_end_date + + +def test_electric_billing_input_overall_start_date(): + expected_overall_start_date = date(2020, 1, 1) + actual_overall_start_date = _EXAMPLE_VALID_RECORDS_ELECTRIC.overall_start_date + + assert expected_overall_start_date == actual_overall_start_date + + +def test_electric_billing_input_overall_end_date(): + expected_overall_end_date = date(2020, 2, 28) + actual_overall_end_date = _EXAMPLE_VALID_RECORDS_ELECTRIC.overall_end_date assert expected_overall_end_date == actual_overall_end_date def test_natural_gas_billing_input_overall_start_date_invalid(): with pytest.raises(ValueError): - _EXAMPLE_INVALID_RECORDS.overall_start_date + _EXAMPLE_INVALID_RECORDS_NATURAL_GAS.overall_start_date def test_natural_gas_billing_input_overall_end_date_invalid(): with pytest.raises(ValueError): - _EXAMPLE_INVALID_RECORDS.overall_end_date + _EXAMPLE_INVALID_RECORDS_NATURAL_GAS.overall_end_date + + +def test_electric_billing_input_overall_start_date_invalid(): + with pytest.raises(ValueError): + _EXAMPLE_INVALID_RECORDS_ELECTRIC.overall_start_date + + +def test_electric_billing_input_overall_end_date_invalid(): + with pytest.raises(ValueError): + _EXAMPLE_INVALID_RECORDS_ELECTRIC.overall_end_date From 999aab30fed8731449db0326b2121e687cba1a1e Mon Sep 17 00:00:00 2001 From: AdamFinkle <77808710+AdamFinkle@users.noreply.github.com> Date: Wed, 3 Jul 2024 18:32:14 -0400 Subject: [PATCH 2/5] Ran linter and corrected typing. --- rules-engine/src/rules_engine/parser.py | 6 ++-- .../tests/test_rules_engine/test_parser.py | 36 ++++++++++--------- .../test_rules_engine/test_pydantic_models.py | 2 +- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/rules-engine/src/rules_engine/parser.py b/rules-engine/src/rules_engine/parser.py index 525b6c24..e781febf 100644 --- a/rules-engine/src/rules_engine/parser.py +++ b/rules-engine/src/rules_engine/parser.py @@ -13,7 +13,7 @@ NaturalGasBillingInput, NaturalGasBillingRecordInput, ElectricBillingInput, - ElectricBillingRecordInput + ElectricBillingRecordInput, ) @@ -209,7 +209,7 @@ def _parse_gas_bill_xml(path: str) -> NaturalGasBillingInput: return NaturalGasBillingInput(records=records) -def _parse_electric_bill_xml(path: str) -> NaturalGasBillingInput: +def _parse_electric_bill_xml(path: str) -> ElectricBillingInput: """ Return a list of gas bill data parsed from a Green Button XML received as a string. @@ -230,4 +230,4 @@ def _parse_electric_bill_xml(path: str) -> NaturalGasBillingInput: ) ) - return ElectricBillingInput(records=records) \ No newline at end of file + return ElectricBillingInput(records=records) diff --git a/rules-engine/tests/test_rules_engine/test_parser.py b/rules-engine/tests/test_rules_engine/test_parser.py index 8ea72db5..5c90dc36 100644 --- a/rules-engine/tests/test_rules_engine/test_parser.py +++ b/rules-engine/tests/test_rules_engine/test_parser.py @@ -6,7 +6,7 @@ from rules_engine import parser from rules_engine.pydantic_models import ( NaturalGasBillingRecordInput, - ElectricBillingRecordInput + ElectricBillingRecordInput, ) ROOT_DIR = pathlib.Path(__file__).parent / "cases" / "examples" @@ -29,22 +29,26 @@ def _read_gas_bill_national_grid() -> str: def _get_gas_bill_xml_path() -> pathlib.Path: """Return the path of a test natural gas XML bill""" - return (pathlib.Path(__file__).parent - / "cases" - / "parsing" - / "xml" - / "natural_gas" - / "ngma_natural_gas_billing_billing_data_Service 1_1_2021-05-26_to_2024-04-25.xml") + return ( + pathlib.Path(__file__).parent + / "cases" + / "parsing" + / "xml" + / "natural_gas" + / "ngma_natural_gas_billing_billing_data_Service 1_1_2021-05-26_to_2024-04-25.xml" + ) def _get_electric_bill_xml_path() -> pathlib.Path: """Return the path of a test natural gas XML bill""" - return (pathlib.Path(__file__).parent - / "cases" - / "parsing" - / "xml" - / "electricicity" - / "TestGBDataHourlyNineDaysBinnedDaily.xml") + return ( + pathlib.Path(__file__).parent + / "cases" + / "parsing" + / "xml" + / "electricicity" + / "TestGBDataHourlyNineDaysBinnedDaily.xml" + ) def _validate_eversource(result): @@ -141,14 +145,14 @@ def test_parse_gas_bill_national_grid(): def test_parse_gas_bill_xml(): """Tests parsing a natural gas bill from a Green Button XML.""" - _validate_gas_bill_xml( - parser._parse_gas_bill_xml(_get_gas_bill_xml_path())) + _validate_gas_bill_xml(parser._parse_gas_bill_xml(_get_gas_bill_xml_path())) def test_parse_electric_bill_xml(): """Tests parsing an electricicity bill from a Green Button XML.""" _validate_electric_bill_xml( - parser._parse_electric_bill_xml(_get_electric_bill_xml_path())) + parser._parse_electric_bill_xml(_get_electric_bill_xml_path()) + ) def test_detect_natural_gas_company(): diff --git a/rules-engine/tests/test_rules_engine/test_pydantic_models.py b/rules-engine/tests/test_rules_engine/test_pydantic_models.py index 35108a75..412aa6f8 100644 --- a/rules-engine/tests/test_rules_engine/test_pydantic_models.py +++ b/rules-engine/tests/test_rules_engine/test_pydantic_models.py @@ -6,7 +6,7 @@ NaturalGasBillingInput, NaturalGasBillingRecordInput, ElectricBillingInput, - ElectricBillingRecordInput + ElectricBillingRecordInput, ) _EXAMPLE_VALID_RECORDS_NATURAL_GAS = NaturalGasBillingInput( From 889a060f3ebe1b2f90e272bcb38bb42d949bb983 Mon Sep 17 00:00:00 2001 From: AdamFinkle <77808710+AdamFinkle@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:35:35 -0400 Subject: [PATCH 3/5] Ran isort to correct linting. --- rules-engine/src/rules_engine/parser.py | 5 +++-- rules-engine/tests/test_rules_engine/test_parser.py | 2 +- rules-engine/tests/test_rules_engine/test_pydantic_models.py | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rules-engine/src/rules_engine/parser.py b/rules-engine/src/rules_engine/parser.py index e781febf..977dbedc 100644 --- a/rules-engine/src/rules_engine/parser.py +++ b/rules-engine/src/rules_engine/parser.py @@ -9,11 +9,12 @@ from enum import StrEnum from greenbutton_objects import parse + from .pydantic_models import ( - NaturalGasBillingInput, - NaturalGasBillingRecordInput, ElectricBillingInput, ElectricBillingRecordInput, + NaturalGasBillingInput, + NaturalGasBillingRecordInput, ) diff --git a/rules-engine/tests/test_rules_engine/test_parser.py b/rules-engine/tests/test_rules_engine/test_parser.py index 5c90dc36..7ca0d0f3 100644 --- a/rules-engine/tests/test_rules_engine/test_parser.py +++ b/rules-engine/tests/test_rules_engine/test_parser.py @@ -5,8 +5,8 @@ from rules_engine import parser from rules_engine.pydantic_models import ( - NaturalGasBillingRecordInput, ElectricBillingRecordInput, + NaturalGasBillingRecordInput, ) ROOT_DIR = pathlib.Path(__file__).parent / "cases" / "examples" diff --git a/rules-engine/tests/test_rules_engine/test_pydantic_models.py b/rules-engine/tests/test_rules_engine/test_pydantic_models.py index 412aa6f8..47d844f7 100644 --- a/rules-engine/tests/test_rules_engine/test_pydantic_models.py +++ b/rules-engine/tests/test_rules_engine/test_pydantic_models.py @@ -3,10 +3,10 @@ import pytest from rules_engine.pydantic_models import ( - NaturalGasBillingInput, - NaturalGasBillingRecordInput, ElectricBillingInput, ElectricBillingRecordInput, + NaturalGasBillingInput, + NaturalGasBillingRecordInput, ) _EXAMPLE_VALID_RECORDS_NATURAL_GAS = NaturalGasBillingInput( From f9e4ed36540bf3e1c8e57bd0cff7e4798be83a0c Mon Sep 17 00:00:00 2001 From: AdamFinkle <77808710+AdamFinkle@users.noreply.github.com> Date: Fri, 5 Jul 2024 03:12:13 -0400 Subject: [PATCH 4/5] Ignored type hint requirement for this old library. --- rules-engine/src/rules_engine/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules-engine/src/rules_engine/parser.py b/rules-engine/src/rules_engine/parser.py index 977dbedc..5336f8a9 100644 --- a/rules-engine/src/rules_engine/parser.py +++ b/rules-engine/src/rules_engine/parser.py @@ -8,7 +8,7 @@ from datetime import datetime, timedelta from enum import StrEnum -from greenbutton_objects import parse +from greenbutton_objects import parse # type: ignore from .pydantic_models import ( ElectricBillingInput, From 410e674ae6544fcd7d6e6b4f19cb11eb95a3db27 Mon Sep 17 00:00:00 2001 From: AdamFinkle <77808710+AdamFinkle@users.noreply.github.com> Date: Fri, 5 Jul 2024 03:18:03 -0400 Subject: [PATCH 5/5] Ran linter. --- rules-engine/src/rules_engine/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules-engine/src/rules_engine/parser.py b/rules-engine/src/rules_engine/parser.py index 5336f8a9..359c07a5 100644 --- a/rules-engine/src/rules_engine/parser.py +++ b/rules-engine/src/rules_engine/parser.py @@ -8,7 +8,7 @@ from datetime import datetime, timedelta from enum import StrEnum -from greenbutton_objects import parse # type: ignore +from greenbutton_objects import parse # type: ignore from .pydantic_models import ( ElectricBillingInput,