Skip to content

Commit

Permalink
import Pyodide in single.tsx
Browse files Browse the repository at this point in the history
Co-authored-by: Camden Blatchly <[email protected]>
Co-authored-by: plocket <[email protected]>
  • Loading branch information
3 people committed May 14, 2024
1 parent 702ff96 commit 5a2fd7d
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 79 deletions.
232 changes: 155 additions & 77 deletions heat-stack/app/routes/_heat+/single.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { invariantResponse } from '@epic-web/invariant'
import { json, ActionFunctionArgs } from '@remix-run/node'
import { Form, redirect, useActionData } from '@remix-run/react'
import { z } from 'zod'
import GeocodeUtil from "#app/utils/GeocodeUtil.js";
import WeatherUtil from "#app/utils/WeatherUtil.js";
import PyodideUtil from "#app/utils/pyodide.util.js";
import GeocodeUtil from '#app/utils/GeocodeUtil.js'
import WeatherUtil from '#app/utils/WeatherUtil.js'
import PyodideUtil from '#app/utils/pyodide.util.js'
import * as pyodideModule from 'pyodide'

// TODO NEXT WEEK
// - [x] Server side error checking/handling
Expand All @@ -23,8 +24,14 @@ import PyodideUtil from "#app/utils/pyodide.util.js";
// - parseMultipartFormData
// - avoid dealing with the server for now
// - pass the data to the rules engine/pyodide either in the component or the action (probably the action for validation, etc.)
// - [ ] (On hold for data format from rules engine) Build table form
// - [ ] Form errors (if we think of a use case - 2 fields conflicting...)
// - [x] import pyodide into single.tsx and run it with genny
// don't forget `npm run buildpy` to build rules engine into `public/pyodide-env` if you start a new codingspace or on local.
// - [ ] figure out how to set field defaults with Conform to speed up trials (defaultValue prop on input doesn't work) https://conform.guide/api/react/useForm
// - [ ] Issue?: How about a dropdown? census geocoder address form picker component to choose which address from several, if ambigous or bad.
// - [ ] How do we stop the geocoder helper from glomming everyone's past submitted addresses onto querystring in single.tsx action?
// - [ ] Display Conform's form-wide errors, currently thrown away (if we think of a use case - 2 fields conflicting...)
// - [ ] (use data passing function API from #172 from rules engine) to Build table component form
// - [ ] Pass modified table back to rules engine for full cycle revalidation

// Ours
import { ErrorList } from '#app/components/ui/heat/CaseSummaryComponents/ErrorList.tsx'
Expand All @@ -35,7 +42,6 @@ import { HomeInformation } from '../../components/ui/heat/CaseSummaryComponents/
import HeatLoadAnalysis from './heatloadanalysis.tsx'
import { Button } from '#/app/components/ui/button.tsx'


const nameMaxLength = 50
const addressMaxLength = 100

Expand All @@ -62,7 +68,6 @@ const CurrentHeatingSystemSchema = Home.pick({
thermostatSetPoint: true,
setbackTemperature: true,
setbackHoursPerDay: true,

})

const Schema = HomeFormSchema.and(CurrentHeatingSystemSchema)
Expand All @@ -73,18 +78,17 @@ export async function action({ request, params }: ActionFunctionArgs) {
// Checks if url has a homeId parameter, throws 400 if not there
// invariantResponse(params.homeId, 'homeId param is required')

console.log("action started")
console.log('action started')

const formData = await request.formData()
const submission = parseWithZod(formData, {
schema: Schema,
})

if (submission.status !== 'success') {

if( process.env.NODE_ENV === "development" ) {
if (process.env.NODE_ENV === 'development') {
// this can have personal identifying information, so only active in development.
console.error("submission failed", submission)
console.error('submission failed', submission)
}
return submission.reply()
// submission.reply({
Expand All @@ -100,22 +104,27 @@ export async function action({ request, params }: ActionFunctionArgs) {
// {status: submission.status === "error" ? 400 : 200}
}

const { name, address, livingArea, fuelType,
const {
name,
address,
livingArea,
fuelType,
heatingSystemEfficiency,
thermostatSetPoint,
setbackTemperature,
setbackHoursPerDay,
designTemperatureOverride } = submission.value
designTemperatureOverride,
} = submission.value

// await updateNote({ id: params.noteId, title, content })
//code snippet from - https://github.com/epicweb-dev/web-forms/blob/2c10993e4acffe3dd9ad7b9cb0cdf89ce8d46ecf/exercises/04.file-upload/01.solution.multi-part/app/routes/users%2B/%24username_%2B/notes.%24noteId_.edit.tsx#L180
//code snippet from - https://github.com/epicweb-dev/web-forms/blob/2c10993e4acffe3dd9ad7b9cb0cdf89ce8d46ecf/exercises/04.file-upload/01.solution.multi-part/app/routes/users%2B/%24username_%2B/notes.%24noteId_.edit.tsx#L180

// const formData = await parseMultipartFormData(
// request,
// createMemoryUploadHandler({ maxPartSize: MAX_UPLOAD_SIZE }),
// )

console.log("loading PU/PM/GU/WU");
console.log('loading PU/PM/GU/WU')

// CONSOLE: loading PU/PM/GU/WU
// Error: No known package with name 'pydantic_core'
Expand All @@ -127,70 +136,141 @@ export async function action({ request, params }: ActionFunctionArgs) {

// const PU = PyodideUtil.getInstance();
// const PM = await PU.getPyodideModule();
const GU = new GeocodeUtil();
const WU = new WeatherUtil();
const GU = new GeocodeUtil()
const WU = new WeatherUtil()
// console.log("loaded PU/PM/GU/WU");
////////////////////////
const getPyodide = async () => {
return await pyodideModule.loadPyodide({
// This path is actually `public/pyodide-env`, but the browser knows where `public` is. Note that remix server needs `public/`
// TODO: figure out how to determine if we're in browser or remix server and use ternary.
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()
//////////////////////

/**
*
* @param longitude
* @param latitude
* @param start_date
* @param end_date
* @returns {SI,TIWD,BI} Summary input: hardcoded data.TIWD: TemperatureInput: WeatherData from calling open meto API
* Billing input: hardcoded data
*
* Function just to generate test data. inputs come from the values entered in from HomeInformation component
*/
async function genny(
longitude: number,
latitude: number,
start_date: string,
end_date: string,
) {
// SI = new SummaryInput(6666,"GAS",80,67,null,null,60);
// was living_area: number, fuel_type: FuelType, heating_system_efficiency: number, thermostat_set_point: number, setback_temperature: number | null, setback_hours_per_day: number | null, design_temperature: number

type SchemaZodFromFormType = z.infer<typeof Schema>

const oldSummaryInput = {
living_area: 6666,
fuel_type: 'GAS',
heating_system_efficiency: 80,
thermostat_set_point: 67,
setback_temperature: null,
setback_hours_per_day: null,
design_temperature: 60,
}

/**
*
* @param longitude
* @param latitude
* @param start_date
* @param end_date
* @returns {SI,TIWD,BI} Summary input: hardcoded data.TIWD: TemperatureInput: WeatherData from calling open meto API
* Billing input: hardcoded data
*
* Function just to generate test data. inputs come from the values entered in from HomeInformation component
*/
async function genny(longitude: number, latitude: number, start_date: string, end_date: string) {
// SI = new SummaryInput(6666,"GAS",80,67,null,null,60);
// was living_area: number, fuel_type: FuelType, heating_system_efficiency: number, thermostat_set_point: number, setback_temperature: number | null, setback_hours_per_day: number | null, design_temperature: number

type SchemaZodFromFormType = z.infer<typeof Schema>;



const oldSummaryInput = {
living_area: 6666,
fuel_type: "GAS",
heating_system_efficiency: 80,
thermostat_set_point: 67,
setback_temperature: null,
setback_hours_per_day: null,
design_temperature: 60,
};

const SI: SchemaZodFromFormType = Schema.parse({
livingArea: oldSummaryInput.living_area,
address: '123 Main St', // Provide a valid address
name: 'My Home', // Provide a valid name
fuelType: oldSummaryInput.fuel_type === 'GAS' ? 'Natural Gas' : oldSummaryInput.fuel_type,
heatingSystemEfficiency: oldSummaryInput.heating_system_efficiency,
thermostatSetPoint: oldSummaryInput.thermostat_set_point,
setbackTemperature: oldSummaryInput.setback_temperature,
setbackHoursPerDay: oldSummaryInput.setback_hours_per_day,
designTemperatureOverride: oldSummaryInput.design_temperature,
});

console.log("SI", SI)


// const TIWD: TemperatureInput = await WU.getThatWeathaData(longitude, latitude, start_date, end_date);
const TIWD = await WU.getThatWeathaData(longitude, latitude, start_date, end_date);
const BI = [{
period_start_date: new Date("2023-12-30"),//new Date("2023-12-30"),
period_end_date: new Date("2024-01-06"),
usage:100,
inclusion_override: null
}];
return {SI, TIWD, BI};
}
const SI: SchemaZodFromFormType = Schema.parse({
livingArea: oldSummaryInput.living_area,
address: '123 Main St', // Provide a valid address
name: 'My Home', // Provide a valid name
fuelType:
oldSummaryInput.fuel_type === 'GAS'
? 'Natural Gas'
: oldSummaryInput.fuel_type,
heatingSystemEfficiency: oldSummaryInput.heating_system_efficiency,
thermostatSetPoint: oldSummaryInput.thermostat_set_point,
setbackTemperature: oldSummaryInput.setback_temperature,
setbackHoursPerDay: oldSummaryInput.setback_hours_per_day,
designTemperatureOverride: oldSummaryInput.design_temperature,
})

console.log('SI', SI)

// const TIWD: TemperatureInput = await WU.getThatWeathaData(longitude, latitude, start_date, end_date);
const TIWD = await WU.getThatWeathaData(
longitude,
latitude,
start_date,
end_date,
)
const BI = [
{
period_start_date: new Date('2023-12-30'), //new Date("2023-12-30"),
period_end_date: new Date('2024-01-06'),
usage: 100,
inclusion_override: null,
},
]
return { SI, TIWD, BI }
}


let { x, y } = await GU.getLL(address);
console.log("geocoded", x,y)
let { x, y } = await GU.getLL(address)
console.log('geocoded', x, y)

let { SI, TIWD, BI } = await genny(x,y,"2024-01-01","2024-01-03")
let { SI, TIWD, BI } = await genny(x, y, '2024-01-01', '2024-01-03')

// PU.runit(SI,null,TIWD,JSON.stringify(BI));
// CSV entrypoint parse_gas_bill(data: str, company: NaturalGasCompany)
Expand All @@ -199,8 +279,6 @@ async function genny(longitude: number, latitude: number, start_date: string, en
return redirect(`/single`)
}



export default function Inputs() {
const lastResult = useActionData<typeof action>()
const [form, fields] = useForm({
Expand Down
Loading

0 comments on commit 5a2fd7d

Please sign in to comment.