From 702ff96913ac23bba42569ff8f3e860d244e6f17 Mon Sep 17 00:00:00 2001 From: Thad Kerosky Date: Tue, 14 May 2024 19:23:29 -0400 Subject: [PATCH] Pyodide_rules_engine_test (#172) * Collaboration on CSV test of pyodide Co-authored-by: Hector Benitez Co-authored-by: Leopardfoot Co-authored-by: Clayton Schneider Co-authored-by: plocket Co-authored-by: dwindleduck Co-authored-by: Debajyoti Debnath Co-authored-by: Alan Pinkert * Test the rules engine in pyodide: the keystone. Co-authored-by: plocket Co-authored-by: thatoldplatitude Co-authored-by: eriksynn Co-authored-by: Leopardfoot Co-authored-by: Alan Pinkert Co-authored-by: Debajyoti Debnath --------- Co-authored-by: Hector Benitez Co-authored-by: Leopardfoot Co-authored-by: Clayton Schneider Co-authored-by: plocket Co-authored-by: dwindleduck Co-authored-by: Debajyoti Debnath Co-authored-by: Alan Pinkert Co-authored-by: thatoldplatitude Co-authored-by: eriksynn --- heat-stack/app/routes/_heat+/single.tsx | 6 +- heat-stack/app/utils/GeocodeUtil.js | 8 +- heat-stack/app/utils/pyodide.test.ts | 500 +++++++++++++++++++++--- 3 files changed, 443 insertions(+), 71 deletions(-) diff --git a/heat-stack/app/routes/_heat+/single.tsx b/heat-stack/app/routes/_heat+/single.tsx index cf57c949..97a850aa 100644 --- a/heat-stack/app/routes/_heat+/single.tsx +++ b/heat-stack/app/routes/_heat+/single.tsx @@ -81,7 +81,11 @@ export async function action({ request, params }: ActionFunctionArgs) { }) if (submission.status !== 'success') { - console.error("submission failed",submission) + + if( process.env.NODE_ENV === "development" ) { + // this can have personal identifying information, so only active in development. + console.error("submission failed", submission) + } return submission.reply() // submission.reply({ // // You can also pass additional error to the `reply` method diff --git a/heat-stack/app/utils/GeocodeUtil.js b/heat-stack/app/utils/GeocodeUtil.js index a7103f84..22be44e6 100644 --- a/heat-stack/app/utils/GeocodeUtil.js +++ b/heat-stack/app/utils/GeocodeUtil.js @@ -1,19 +1,17 @@ const BASE_URL = "https://geocoding.geo.census.gov"; -const ADDRESS_ENDPOINT = "/geocoder/locations/address"; +const ADDRESS_ENDPOINT = "/geocoder/locations/onelineaddress"; const params = new URLSearchParams(); class GeocodeUtil { /** * - * @param {*} street - * @param {*} city - * @param {*} state + * @param {*} address * @returns x,y {x,y} lon/lat. If the given address was valid. I've implemented 0 handling here. * This is the happiest of paths, with hardcoded values also... */ async getLL(address) { - params.append("onelineaddress",address); + params.append("address",address); params.append("format","json"); params.append("benchmark",2020); diff --git a/heat-stack/app/utils/pyodide.test.ts b/heat-stack/app/utils/pyodide.test.ts index 601101ee..d6e554ca 100644 --- a/heat-stack/app/utils/pyodide.test.ts +++ b/heat-stack/app/utils/pyodide.test.ts @@ -1,71 +1,441 @@ import * as pyodideModule from 'pyodide' -import { expect, test } from 'vitest' +import { expect, test, beforeEach } from 'vitest' +import GeocodeUtil from "#app/utils/GeocodeUtil.js"; +import WeatherUtil from "#app/utils/WeatherUtil.js"; +// import PyodideUtil from "#app/utils/pyodide.util.js"; + /* For this to pass, you must run `pushd ../rules-engine && python3 -m venv venv && source venv/bin/activate && pip install -q build && python3 -m build && popd` */ /* Referenced https://github.com/epicweb-dev/full-stack-testing/blob/main/exercises/04.unit-test/02.solution.spies/app/utils/misc.error-message.test.ts of https://www.epicweb.dev/workshops/web-application-testing*/ -test('pyodide loads', async () => { - const getPyodide = async () => { - // public folder: - return await pyodideModule.loadPyodide({ - indexURL: 'public/pyodide-env/', - }) - } - const runPythonScript = async () => { - const pyodide: any = await getPyodide() - // console.log(engine); - // await pyodide.loadPackage('numpy') - - /* NOTES for pyodide-core: - need to be a special version from the release page, no pure whl: https://github.com/pydantic/pydantic-core/pull/128 - get it from https://github.com/pydantic/pydantic-core/releases e.g. https://github.com/pydantic/pydantic-core/releases/download/v2.14.5/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl - */ - await pyodide.loadPackage( - 'public/pyodide-env/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl', - ) - - /* NOTES for pydantic, typing-extensions, annotated_types: - pyodide should match pyodide-core somewhat. - typing-extensions needs specific version per https://github.com/pyodide/pyodide/issues/4234#issuecomment-1771735148 - try getting it from - - https://pypi.org/project/pydantic/#files - - https://pypi.org/project/typing-extensions/ - - https://pypi.org/project/annotated-types/#files - */ - await pyodide.loadPackage( - 'public/pyodide-env/pydantic-2.5.2-py3-none-any.whl', - ) - await pyodide.loadPackage( - 'public/pyodide-env/typing_extensions-4.8.0-py3-none-any.whl', - ) - await pyodide.loadPackage( - 'public/pyodide-env/annotated_types-0.5.0-py3-none-any.whl', - ) - - /* NOTES FOR DEBUGGING new requirements.txt - and getting specific versions of pure whl */ - // the below works but uses the internet/pypi content delivery network rather than localhost. - // await pyodide.loadPackage('micropip') - // const micropip = await pyodide.pyimport('micropip') - // await micropip.install([ - // 'typing-extensions==4.8.0', - // 'pydantic_core==2.14.5', - // 'pydantic==2.5.2', - // ]) - // await micropip.install(['annotated-types']) - - await pyodide.loadPackage( - '../rules-engine/dist/rules_engine-0.0.1-py3-none-any.whl', - ) - return pyodide - } - // consider running https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/tests/test_rules_engine/test_engine.py - const pyodide: any = await runPythonScript() - const result = await pyodide.runPythonAsync(` - from rules_engine import engine - - out = engine.hdd(57, 60) - out`) - expect(result).toBe(3) -}, 6000) +// test('pyodide loads', async () => { +// const getPyodide = async () => { +// // public folder: +// return await pyodideModule.loadPyodide({ +// indexURL: 'public/pyodide-env/', +// }) +// } +// const runPythonScript = async () => { +// const pyodide: any = await getPyodide() +// // console.log(engine); +// // await pyodide.loadPackage('numpy') + +// /* NOTES for pyodide-core: +// need to be a special version from the release page, no pure whl: https://github.com/pydantic/pydantic-core/pull/128 +// get it from https://github.com/pydantic/pydantic-core/releases e.g. https://github.com/pydantic/pydantic-core/releases/download/v2.14.5/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl +// */ +// await pyodide.loadPackage( +// 'public/pyodide-env/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl', +// ) + +// /* NOTES for pydantic, typing-extensions, annotated_types: +// pyodide should match pyodide-core somewhat. +// typing-extensions needs specific version per https://github.com/pyodide/pyodide/issues/4234#issuecomment-1771735148 +// try getting it from +// - https://pypi.org/project/pydantic/#files +// - https://pypi.org/project/typing-extensions/ +// - https://pypi.org/project/annotated-types/#files +// */ +// await pyodide.loadPackage( +// 'public/pyodide-env/pydantic-2.5.2-py3-none-any.whl', +// ) +// await pyodide.loadPackage( +// 'public/pyodide-env/typing_extensions-4.8.0-py3-none-any.whl', +// ) +// await pyodide.loadPackage( +// 'public/pyodide-env/annotated_types-0.5.0-py3-none-any.whl', +// ) + +// /* NOTES FOR DEBUGGING new requirements.txt +// and getting specific versions of pure whl */ +// // the below works but uses the internet/pypi content delivery network rather than localhost. +// // await pyodide.loadPackage('micropip') +// // const micropip = await pyodide.pyimport('micropip') +// // await micropip.install([ +// // 'typing-extensions==4.8.0', +// // 'pydantic_core==2.14.5', +// // 'pydantic==2.5.2', +// // ]) +// // await micropip.install(['annotated-types']) + +// await pyodide.loadPackage( +// '../rules-engine/dist/rules_engine-0.0.1-py3-none-any.whl', +// ) + +// return pyodide +// } +// // consider running https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/tests/test_rules_engine/test_engine.py +// const pyodide: any = await runPythonScript() +// const result = await pyodide.runPythonAsync(` +// from rules_engine import engine + +// out = engine.hdd(57, 60) +// out`) +// expect(result).toBe(3) +// }, 9000) + +// js passes household information to the rules engine +test('pyodide solves climate change', async () => { + const getPyodide = async () => { + // public folder: + return await pyodideModule.loadPyodide({ + indexURL: 'public/pyodide-env/', + }) + } + const runPythonScript = async () => { + const pyodide: any = await getPyodide() + // console.log(engine); + // await pyodide.loadPackage('numpy') + + /* NOTES for pyodide-core: + need to be a special version from the release page, no pure whl: https://github.com/pydantic/pydantic-core/pull/128 + get it from https://github.com/pydantic/pydantic-core/releases e.g. https://github.com/pydantic/pydantic-core/releases/download/v2.14.5/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl + */ + await pyodide.loadPackage( + 'public/pyodide-env/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl', + ) + + /* NOTES for pydantic, typing-extensions, annotated_types: + pyodide should match pyodide-core somewhat. + typing-extensions needs specific version per https://github.com/pyodide/pyodide/issues/4234#issuecomment-1771735148 + try getting it from + - https://pypi.org/project/pydantic/#files + - https://pypi.org/project/typing-extensions/ + - https://pypi.org/project/annotated-types/#files + */ + await pyodide.loadPackage( + 'public/pyodide-env/pydantic-2.5.2-py3-none-any.whl', + ) + await pyodide.loadPackage( + 'public/pyodide-env/typing_extensions-4.8.0-py3-none-any.whl', + ) + await pyodide.loadPackage( + 'public/pyodide-env/annotated_types-0.5.0-py3-none-any.whl', + ) + + /* NOTES FOR DEBUGGING new requirements.txt + and getting specific versions of pure whl */ + // the below works but uses the internet/pypi content delivery network rather than localhost. + // await pyodide.loadPackage('micropip') + // const micropip = await pyodide.pyimport('micropip') + // await micropip.install([ + // 'typing-extensions==4.8.0', + // 'pydantic_core==2.14.5', + // 'pydantic==2.5.2', + // ]) + // await micropip.install(['annotated-types']) + + await pyodide.loadPackage( + '../rules-engine/dist/rules_engine-0.0.1-py3-none-any.whl', + ) + + return pyodide + } + // consider running https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/tests/test_rules_engine/test_engine.py + const pyodide: any = await runPythonScript() + + const GU = new GeocodeUtil(); + const WU = new WeatherUtil(); + + // 1) parser.parse_gas_bill(data: str, company: NaturalGasCompany) + // https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/src/rules_engine/parser.py#L60 + + /* 2) get_outputs_natural_gas( + summary_input: SummaryInput, + temperature_input: TemperatureInput, + natural_gas_billing_input: NaturalGasBillingInput, + ) -> RulesEngineResult + + https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/src/rules_engine/engine.py#L59 + */ + + /** + * TODO: + * - Match up our types with rules engine types at https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/src/rules_engine/pydantic_models.py#L57 + * - Use those to create summary_input (in the same file) + * - Use those to create temperature_input (in the same file) + * - Use those to create natural_gas_billing_input (in the same file) + */ + + + // This other API may be needed to get the lookup column composite key values for merged_structure_temps.csv for the design temp. + // https://geocoding.geo.census.gov/geocoder/geographies/onelineaddress?address=1%20Broadway%2C%20Cambridge%2C%20MA%2002139&benchmark=4&vintage=4 + const address = "1 Broadway, Cambridge, MA 02142"; + let { x, y } = await GU.getLL(address); + console.log ("address", address, x, y); + const TIWD = await WU.getThatWeathaData(x, y, "2024-03-20", "2024-04-20"); + console.log("weather data",TIWD); + + + // https://github.com/codeforboston/home-energy-analysis-tool/tree/main/rules-engine/tests/test_rules_engine/cases/examples/quateman + const exampleNationalGridCSV = `Name,FIRST LAST,,,,, + Address,"100 STREET AVE, BOSTON MA 02130",,,,, + Account Number,1111111111,,,,, + Service,Service 1,,,,, + ,,,,,, + TYPE,START DATE,END DATE,USAGE,UNITS,COST,NOTES + Natural gas billing,10/2/2020,11/4/2020,29,therms,$42.08 , + Natural gas billing,11/5/2020,12/3/2020,36,therms,$65.60 , + Natural gas billing,12/4/2020,1/7/2021,97,therms,$159.49 , + Natural gas billing,1/8/2021,2/5/2021,105,therms,$169.09 , + Natural gas billing,2/6/2021,3/5/2021,98,therms,$158.19 , + Natural gas billing,3/6/2021,4/6/2021,66,therms,$111.79 , + Natural gas billing,4/7/2021,5/5/2021,22,therms,$43.16 , + Natural gas billing,5/6/2021,6/7/2021,19,therms,$32.42 , + Natural gas billing,6/8/2021,7/6/2021,7,therms,$18.68 , + Natural gas billing,7/7/2021,8/4/2021,10,therms,$21.73 , + Natural gas billing,8/5/2021,9/8/2021,11,therms,$25.35 , + Natural gas billing,9/9/2021,10/5/2021,8,therms,$19.58 , + Natural gas billing,10/6/2021,11/3/2021,13,therms,$27.10 , + Natural gas billing,11/4/2021,12/6/2021,41,therms,$87.45 , + Natural gas billing,12/7/2021,1/5/2022,86,therms,$171.92 , + Natural gas billing,1/6/2022,2/3/2022,132,therms,$248.63 , + Natural gas billing,2/4/2022,3/7/2022,116,therms,$226.66 , + Natural gas billing,3/8/2022,4/4/2022,49,therms,$109.44 , + Natural gas billing,4/5/2022,5/5/2022,39,therms,$87.54 , + Natural gas billing,5/6/2022,6/6/2022,20,therms,$44.30 , + Natural gas billing,6/7/2022,7/5/2022,9,therms,$27.71 , + Natural gas billing,7/6/2022,8/3/2022,7,therms,$23.86 , + Natural gas billing,8/4/2022,9/3/2022,8,therms,$24.04 , + Natural gas billing,9/4/2022,10/3/2022,8,therms,$26.41 , + Natural gas billing,10/4/2022,11/3/2022,19,therms,$48.92 ,` + + // NaturalGasCompany = National Grid is 2 for now + const exampleNationalGridNaturalGasCompany = "2" + + // csv is 1st arg. 2nd arg should have a selection of whether Eversource (1) or National Grid (2). + // rules engine-returned objects can be modified and passed back to rules engine + + const summaryInput = { + living_area: 2155, + fuel_type: "GAS", + heating_system_efficiency: 0.97, + thermostat_set_point: 68.0, + setback_temperature: null, + setback_hours_per_day: null, + // TODO: https://github.com/codeforboston/home-energy-analysis-tool/issues/123 + design_temperature: 9.5, + } + + const executePy = await pyodide.runPythonAsync(` + from rules_engine import parser + from rules_engine.pydantic_models import ( + FuelType, + SummaryInput, + TemperatureInput + ) + from rules_engine import engine + + def execute(summaryInputJs, temperatureInputJs, csvDataJs): + + summaryInputFromJs = summaryInputJs.as_object_map().values()._mapping + temperatureInputFromJs =temperatureInputJs.as_object_map().values()._mapping + + naturalGasInputRecords = parser.parse_gas_bill(csvDataJs, parser.NaturalGasCompany.NATIONAL_GRID) + + + summaryInput = SummaryInput(**summaryInputFromJs) + temperatureInput = TemperatureInput(**temperatureInputFromJs) + + outputs = engine.get_outputs_natural_gas(summaryInput,temperatureInput, naturalGasInputRecords) + + return outputs.model_dump(mode="json") + execute + `) + const datesFromTIWD = TIWD.dates.map(datestring => new Date(datestring).toISOString().split('T')[0]) + const convertedDatesTIWD = {dates: datesFromTIWD, temperatures: TIWD.temperatures} + + const result = executePy(summaryInput, convertedDatesTIWD, exampleNationalGridCSV); + + // https://stackoverflow.com/a/56150320 + function replacer(key: any, value: any) { + if(value instanceof Map) { + return { + dataType: 'Map', + value: Array.from(value.entries()), // or with spread: value: [...value] + }; + } else { + return value; + } + } + + // function reviver(key: any, value: any) { + // if(typeof value === 'object' && value !== null) { + // if (value.dataType === 'Map') { + // return new Map(value.value); + // } + // } + // return value; + // } + + console.log(result.toJs()) + + // console.log(result.toJs().get('balance_point_graph')) + + /* prettify-ignore */ + const expectedRecordsToGoInTheTable = '{"dataType":"Map","value":[["summary_output",{"dataType":"Map","value":[["estimated_balance_point",59.5],["other_fuel_usage",8.666666666666666],["average_indoor_temperature",68],["difference_between_ti_and_tbp",8.5],["design_temperature",9.5],["whole_home_heat_loss_rate",47972.03453453454],["standard_deviation_of_heat_loss_rate",0.07742772585617895],["average_heat_load",2494545.795795796],["maximum_heat_load",2902308.0893393396]]}],["balance_point_graph",{"dataType":"Map","value":[["records",[{"dataType":"Map","value":[["balance_point",60],["heat_loss_rate",41034.85407876231],["change_in_heat_loss_rate",0],["percent_change_in_heat_loss_rate",0],["standard_deviation",0.3967358807600794]]},{"dataType":"Map","value":[["balance_point",60.5],["heat_loss_rate",38592.303240740745],["change_in_heat_loss_rate",-2442.550838021569],["percent_change_in_heat_loss_rate",-6.32911392405064],["standard_deviation",0.39673588076007943]]},{"dataType":"Map","value":[["balance_point",59.5],["heat_loss_rate",43807.47935435436],["change_in_heat_loss_rate",2772.625275592043],["percent_change_in_heat_loss_rate",6.329113924050622],["standard_deviation",0.3967358807600795]]},{"dataType":"Map","value":[["balance_point",60.5],["heat_loss_rate",42226.71012849585],["change_in_heat_loss_rate",-2672.576590411132],["percent_change_in_heat_loss_rate",-6.329113924050639],["standard_deviation",0.30164495413734566]]},{"dataType":"Map","value":[["balance_point",59.5],["heat_loss_rate",47933.022308022315],["change_in_heat_loss_rate",3033.7355891153347],["percent_change_in_heat_loss_rate",6.3291139240506284],["standard_deviation",0.3016449541373457]]},{"dataType":"Map","value":[["balance_point",60.5],["heat_loss_rate",46671.62698412699],["change_in_heat_loss_rate",-2953.9004420333513],["percent_change_in_heat_loss_rate",-6.329113924050628],["standard_deviation",0.15298851745396608]]},{"dataType":"Map","value":[["balance_point",59.5],["heat_loss_rate",52978.60360360361],["change_in_heat_loss_rate",3353.0761774432685],["percent_change_in_heat_loss_rate",6.329113924050636],["standard_deviation",0.1529885174539661]]},{"dataType":"Map","value":[["balance_point",60.5],["heat_loss_rate",44137.56613756614],["change_in_heat_loss_rate",-2793.516844149759],["percent_change_in_heat_loss_rate",-6.329113924050642],["standard_deviation",0.10782787516463575]]},{"dataType":"Map","value":[["balance_point",59.5],["heat_loss_rate",50102.10210210211],["change_in_heat_loss_rate",3171.0191203862123],["percent_change_in_heat_loss_rate",6.329113924050639],["standard_deviation",0.10782787516463574]]},{"dataType":"Map","value":[["balance_point",59.5],["heat_loss_rate",50102.10210210211],["change_in_heat_loss_rate",3171.0191203862123],["percent_change_in_heat_loss_rate",6.329113924050639],["standard_deviation",0.10782787516463574]]},{"dataType":"Map","value":[["balance_point",59],["heat_loss_rate",53732.689210950084],["change_in_heat_loss_rate",3630.587108847976],["percent_change_in_heat_loss_rate",6.756756756756753],["standard_deviation",0.10782787516463577]]},{"dataType":"Map","value":[["balance_point",60],["heat_loss_rate",44935.829817158934],["change_in_heat_loss_rate",-3036.2047173756073],["percent_change_in_heat_loss_rate",-6.756756756756764],["standard_deviation",0.07742772585617896]]},{"dataType":"Map","value":[["balance_point",59],["heat_loss_rate",51448.26892109501],["change_in_heat_loss_rate",3476.2343865604707],["percent_change_in_heat_loss_rate",6.756756756756752],["standard_deviation",0.07742772585617896]]}]]]}],["billing_records",[{"dataType":"Map","value":[["period_start_date","2020-10-02"],["period_end_date","2020-11-04"],["usage",29],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2020-11-05"],["period_end_date","2020-12-03"],["usage",36],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",true]]},{"dataType":"Map","value":[["period_start_date","2020-12-04"],["period_end_date","2021-01-07"],["usage",97],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-01-08"],["period_end_date","2021-02-05"],["usage",105],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-02-06"],["period_end_date","2021-03-05"],["usage",98],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-03-06"],["period_end_date","2021-04-06"],["usage",66],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-04-07"],["period_end_date","2021-05-05"],["usage",22],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-05-06"],["period_end_date","2021-06-07"],["usage",19],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-06-08"],["period_end_date","2021-07-06"],["usage",7],["analysis_type_override",null],["inclusion_override",false],["analysis_type",-1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-07-07"],["period_end_date","2021-08-04"],["usage",10],["analysis_type_override",null],["inclusion_override",false],["analysis_type",-1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-08-05"],["period_end_date","2021-09-08"],["usage",11],["analysis_type_override",null],["inclusion_override",false],["analysis_type",-1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-09-09"],["period_end_date","2021-10-05"],["usage",8],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-10-06"],["period_end_date","2021-11-03"],["usage",13],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2021-11-04"],["period_end_date","2021-12-06"],["usage",41],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",true]]},{"dataType":"Map","value":[["period_start_date","2021-12-07"],["period_end_date","2022-01-05"],["usage",86],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-01-06"],["period_end_date","2022-02-03"],["usage",132],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",true]]},{"dataType":"Map","value":[["period_start_date","2022-02-04"],["period_end_date","2022-03-07"],["usage",116],["analysis_type_override",null],["inclusion_override",false],["analysis_type",1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",true]]},{"dataType":"Map","value":[["period_start_date","2022-03-08"],["period_end_date","2022-04-04"],["usage",49],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-04-05"],["period_end_date","2022-05-05"],["usage",39],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-05-06"],["period_end_date","2022-06-06"],["usage",20],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-06-07"],["period_end_date","2022-07-05"],["usage",9],["analysis_type_override",null],["inclusion_override",false],["analysis_type",-1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-07-06"],["period_end_date","2022-08-03"],["usage",7],["analysis_type_override",null],["inclusion_override",false],["analysis_type",-1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-08-04"],["period_end_date","2022-09-03"],["usage",8],["analysis_type_override",null],["inclusion_override",false],["analysis_type",-1],["default_inclusion_by_calculation",true],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-09-04"],["period_end_date","2022-10-03"],["usage",8],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]},{"dataType":"Map","value":[["period_start_date","2022-10-04"],["period_end_date","2022-11-03"],["usage",19],["analysis_type_override",null],["inclusion_override",false],["analysis_type",0],["default_inclusion_by_calculation",false],["eliminated_as_outlier",false]]}]]]}' + + // how you do "dir()"" in python + // console.log(Object.getOwnPropertyNames(result.toJs())); + expect(JSON.stringify(result.toJs(), replacer)).toBe(expectedRecordsToGoInTheTable) + +}, 20000) + +// test('pyodide gets the weather', async () => { +// const getPyodide = async () => { +// // public folder: +// return await pyodideModule.loadPyodide({ +// indexURL: 'public/pyodide-env/', +// }) +// } +// const runPythonScript = async () => { +// const pyodide: any = await getPyodide() +// // console.log(engine); +// // await pyodide.loadPackage('numpy') + +// /* NOTES for pyodide-core: +// need to be a special version from the release page, no pure whl: https://github.com/pydantic/pydantic-core/pull/128 +// get it from https://github.com/pydantic/pydantic-core/releases e.g. https://github.com/pydantic/pydantic-core/releases/download/v2.14.5/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl +// */ +// await pyodide.loadPackage( +// 'public/pyodide-env/pydantic_core-2.14.5-cp311-cp311-emscripten_3_1_32_wasm32.whl', +// ) + +// /* NOTES for pydantic, typing-extensions, annotated_types: +// pyodide should match pyodide-core somewhat. +// typing-extensions needs specific version per https://github.com/pyodide/pyodide/issues/4234#issuecomment-1771735148 +// try getting it from +// - https://pypi.org/project/pydantic/#files +// - https://pypi.org/project/typing-extensions/ +// - https://pypi.org/project/annotated-types/#files +// */ +// await pyodide.loadPackage( +// 'public/pyodide-env/pydantic-2.5.2-py3-none-any.whl', +// ) +// await pyodide.loadPackage( +// 'public/pyodide-env/typing_extensions-4.8.0-py3-none-any.whl', +// ) +// await pyodide.loadPackage( +// 'public/pyodide-env/annotated_types-0.5.0-py3-none-any.whl', +// ) + +// /* NOTES FOR DEBUGGING new requirements.txt +// and getting specific versions of pure whl */ +// // the below works but uses the internet/pypi content delivery network rather than localhost. +// // await pyodide.loadPackage('micropip') +// // const micropip = await pyodide.pyimport('micropip') +// // await micropip.install([ +// // 'typing-extensions==4.8.0', +// // 'pydantic_core==2.14.5', +// // 'pydantic==2.5.2', +// // ]) +// // await micropip.install(['annotated-types']) + +// await pyodide.loadPackage( +// '../rules-engine/dist/rules_engine-0.0.1-py3-none-any.whl', +// ) + +// return pyodide +// } +// // consider running https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/tests/test_rules_engine/test_engine.py +// const pyodide: any = await runPythonScript() + +// const GU = new GeocodeUtil(); +// const WU = new WeatherUtil(); + +// // 1) parser.parse_gas_bill(data: str, company: NaturalGasCompany) +// // https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/src/rules_engine/parser.py#L60 + +// /* 2) get_outputs_natural_gas( +// summary_input: SummaryInput, +// temperature_input: TemperatureInput, +// natural_gas_billing_input: NaturalGasBillingInput, +// ) -> RulesEngineResult + +// https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/src/rules_engine/engine.py#L59 +// */ + +// /** +// * TODO: +// * - Match up our types with rules engine types at https://github.com/codeforboston/home-energy-analysis-tool/blob/main/rules-engine/src/rules_engine/pydantic_models.py#L57 +// * - Use those to create summary_input (in the same file) +// * - Use those to create temperature_input (in the same file) +// * - Use those to create natural_gas_billing_input (in the same file) +// */ + + +// // This other API may be needed to get the lookup column composite key values for merged_structure_temps.csv for the design temp. +// // https://geocoding.geo.census.gov/geocoder/geographies/onelineaddress?address=1%20Broadway%2C%20Cambridge%2C%20MA%2002139&benchmark=4&vintage=4 +// const address = "1 Broadway, Cambridge, MA 02142"; +// let { x, y } = await GU.getLL(address); +// console.log ("address", address, x, y); +// const TIWD = await WU.getThatWeathaData(x, y, "2024-03-20", "2024-04-20"); +// console.log("weather data",TIWD); + +// // https://github.com/codeforboston/home-energy-analysis-tool/tree/main/rules-engine/tests/test_rules_engine/cases/examples/quateman +// const exampleNationalGridCSV = `Name,FIRST LAST,,,,, +// Address,"100 STREET AVE, BOSTON MA 02130",,,,, +// Account Number,1111111111,,,,, +// Service,Service 1,,,,, +// ,,,,,, +// TYPE,START DATE,END DATE,USAGE,UNITS,COST,NOTES +// Natural gas billing,10/2/2020,11/4/2020,29,therms,$42.08 , +// Natural gas billing,11/5/2020,12/3/2020,36,therms,$65.60 , +// Natural gas billing,12/4/2020,1/7/2021,97,therms,$159.49 , +// Natural gas billing,1/8/2021,2/5/2021,105,therms,$169.09 , +// Natural gas billing,2/6/2021,3/5/2021,98,therms,$158.19 , +// Natural gas billing,3/6/2021,4/6/2021,66,therms,$111.79 , +// Natural gas billing,4/7/2021,5/5/2021,22,therms,$43.16 , +// Natural gas billing,5/6/2021,6/7/2021,19,therms,$32.42 , +// Natural gas billing,6/8/2021,7/6/2021,7,therms,$18.68 , +// Natural gas billing,7/7/2021,8/4/2021,10,therms,$21.73 , +// Natural gas billing,8/5/2021,9/8/2021,11,therms,$25.35 , +// Natural gas billing,9/9/2021,10/5/2021,8,therms,$19.58 , +// Natural gas billing,10/6/2021,11/3/2021,13,therms,$27.10 , +// Natural gas billing,11/4/2021,12/6/2021,41,therms,$87.45 , +// Natural gas billing,12/7/2021,1/5/2022,86,therms,$171.92 , +// Natural gas billing,1/6/2022,2/3/2022,132,therms,$248.63 , +// Natural gas billing,2/4/2022,3/7/2022,116,therms,$226.66 , +// Natural gas billing,3/8/2022,4/4/2022,49,therms,$109.44 , +// Natural gas billing,4/5/2022,5/5/2022,39,therms,$87.54 , +// Natural gas billing,5/6/2022,6/6/2022,20,therms,$44.30 , +// Natural gas billing,6/7/2022,7/5/2022,9,therms,$27.71 , +// Natural gas billing,7/6/2022,8/3/2022,7,therms,$23.86 , +// Natural gas billing,8/4/2022,9/3/2022,8,therms,$24.04 , +// Natural gas billing,9/4/2022,10/3/2022,8,therms,$26.41 , +// Natural gas billing,10/4/2022,11/3/2022,19,therms,$48.92 ,` + +// // NaturalGasCompany = National Grid is 2 for now +// const exampleNationalGridNaturalGasCompany = "2" + +// // csv is 1st arg. 2nd arg should have a selection of whether Eversource (1) or National Grid (2). +// // rules engine-returned objects can be modified and passed back to rules engine + +// const summaryInput = `` // Type? Dict? +// const temperatureInput = `` // Type? Dict? +// const naturalGasBillingInput = `` // Type? Dict? + + +// const result = await pyodide.runPythonAsync(` +// from rules_engine import parser + +// exampleNationalGridCSV = """${exampleNationalGridCSV}""" +// naturalGasInputRecords = parser.parse_gas_bill(exampleNationalGridCSV, parser.NaturalGasCompany.NATIONAL_GRID) + +// naturalGasInputRecords +// `) + +// /* double quotes inside the template are not an accident +// and are important for the fixture to match +// */ + /* prettify-ignore */ +// const expectedFixtureResultForQuateman = `"NaturalGasBillingInput(records=[NaturalGasBillingRecordInput(period_start_date=datetime.date(2020, 10, 2), period_end_date=datetime.date(2020, 11, 4), usage_therms=29.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2020, 11, 5), period_end_date=datetime.date(2020, 12, 3), usage_therms=36.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2020, 12, 4), period_end_date=datetime.date(2021, 1, 7), usage_therms=97.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 1, 8), period_end_date=datetime.date(2021, 2, 5), usage_therms=105.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 2, 6), period_end_date=datetime.date(2021, 3, 5), usage_therms=98.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 3, 6), period_end_date=datetime.date(2021, 4, 6), usage_therms=66.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 4, 7), period_end_date=datetime.date(2021, 5, 5), usage_therms=22.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 5, 6), period_end_date=datetime.date(2021, 6, 7), usage_therms=19.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 6, 8), period_end_date=datetime.date(2021, 7, 6), usage_therms=7.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 7, 7), period_end_date=datetime.date(2021, 8, 4), usage_therms=10.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 8, 5), period_end_date=datetime.date(2021, 9, 8), usage_therms=11.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 9, 9), period_end_date=datetime.date(2021, 10, 5), usage_therms=8.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 10, 6), period_end_date=datetime.date(2021, 11, 3), usage_therms=13.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 11, 4), period_end_date=datetime.date(2021, 12, 6), usage_therms=41.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2021, 12, 7), period_end_date=datetime.date(2022, 1, 5), usage_therms=86.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 1, 6), period_end_date=datetime.date(2022, 2, 3), usage_therms=132.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 2, 4), period_end_date=datetime.date(2022, 3, 7), usage_therms=116.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 3, 8), period_end_date=datetime.date(2022, 4, 4), usage_therms=49.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 4, 5), period_end_date=datetime.date(2022, 5, 5), usage_therms=39.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 5, 6), period_end_date=datetime.date(2022, 6, 6), usage_therms=20.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 6, 7), period_end_date=datetime.date(2022, 7, 5), usage_therms=9.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 7, 6), period_end_date=datetime.date(2022, 8, 3), usage_therms=7.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 8, 4), period_end_date=datetime.date(2022, 9, 3), usage_therms=8.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 9, 4), period_end_date=datetime.date(2022, 10, 3), usage_therms=8.0, inclusion_override=None), NaturalGasBillingRecordInput(period_start_date=datetime.date(2022, 10, 4), period_end_date=datetime.date(2022, 11, 3), usage_therms=19.0, inclusion_override=None)])"` + +// /* toString() is calling on the pyodide PyProxy to extract it. +// Do we want to use .toJS()? Can we do a real fixture using that output in vitest? */ +// const resultInJsonString = JSON.stringify(result.toString()) + +// expect(resultInJsonString).toBe(expectedFixtureResultForQuateman) +// }, 20000) +