Skip to content

Commit

Permalink
Dynamic <EnergyUsageHistoryChart/> from Python Rules Engine (codeforb…
Browse files Browse the repository at this point in the history
…oston#249)

* Begin sending prop data to usage charts table component

Co-authored-by: plocket <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>

* Hide some components that don't have data yet, pass indoor temperature from rules engine.

Co-authored-by: plocket <[email protected]>
Co-authored-by: AbdiladifG <[email protected]>

* hydrate AnalysisHeader with rules-engine data

* hydrate EnergyUseHistoryChart with rules-engine data

* calculate num days in period, conditionally set row background, analysisType image and checkbox state

* set up frame for Override Default checkbox behavior

* linting, tsc, css->tailwind

Co-authored-by: AbdiladifG <[email protected]>
Co-authored-by: plocket <[email protected]>
Co-authored-by: dwindleduck <[email protected]>

---------

Co-authored-by: plocket <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>
Co-authored-by: AbdiladifG <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
  • Loading branch information
6 people authored Sep 11, 2024
1 parent f810eee commit 63c6f78
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 182 deletions.
Original file line number Diff line number Diff line change
@@ -1,61 +1,110 @@
import { type z } from 'zod'
import { type HeatLoadAnalysisZod} from '#types/index'
import { type HeatLoadAnalysisZod } from '#types/index'

type HeatLoadAnalysisZod = z.infer<typeof HeatLoadAnalysisZod>
export function AnalysisHeader() {
const heatLoadAnalysis: HeatLoadAnalysisZod = {
rulesEngineVersion: 'Beta 1',
estimatedBalancePoint: 60.5,
otherFuelUsage: 1.07,
averageIndoorTemperature: 68,
differenceBetweenTiAndTbp: 0,
design_temperature: 0,
wholeHomeHeatLossRate: 1112,
standardDeviationHeatLossRate: 5.52,
averageHeatLoad: 0,
maximumHeatLoad: 0,
}
export function AnalysisHeader(props: { usage_data: any }) {

// Example usage_data
// new Map([[
// "estimated_balance_point",
// 61.5
// ],[
// "other_fuel_usage",
// 0.2857142857142857
// ],[
// "average_indoor_temperature",
// 67
// ],[
// "difference_between_ti_and_tbp",
// 5.5
// ],[
// "design_temperature",
// 1
// ],[
// "whole_home_heat_loss_rate",
// 48001.81184312083
// ],[
// "standard_deviation_of_heat_loss_rate",
// 0.08066745182677547
// ],[
// "average_heat_load",
// 3048115.0520381727
// ],[
// "maximum_heat_load",
// 3312125.0171753373
// ]])

const summaryOutputs = props.usage_data?.get('summary_output')

// Calculate the number of billing periods included in Heating calculations
const heatingAnalysisTypeRecords = props.usage_data
?.get('billing_records')
?.filter((billingRecord: any) => billingRecord.get('analysis_type') == 1)

const recordsIncludedByDefault = heatingAnalysisTypeRecords?.filter(
(billingRecord: any) =>
billingRecord.get('default_inclusion_by_calculation') == true &&
billingRecord.get('inclusion_override') == false,
).length

const recordsIncludedByOverride = heatingAnalysisTypeRecords?.filter(
(billingRecord: any) =>
billingRecord.get('default_inclusion_by_calculation') == false &&
billingRecord.get('inclusion_override') == true,
).length

const numRecordsForHeatingCalculations =
recordsIncludedByDefault + recordsIncludedByOverride

return (
<div className="section-title">
<div className="item-group-title">Analysis</div>
<div className="item-group-title">Heat Load Analysis</div>
<div className="flex flex-row">
<div className="basis-1/3">
<div className="item-title-small">
Average Indoor Temperature <br />
<div className="item">
{heatLoadAnalysis.averageIndoorTemperature} °F
{summaryOutputs?.get('average_indoor_temperature')} °F
</div>{' '}
<br />
Balance Point Temperature (°F) <br />
Balance Point Temperature
<br />
<div className="item">
{heatLoadAnalysis.estimatedBalancePoint}
{summaryOutputs?.get('estimated_balance_point')} °F
</div>{' '}
<br />
</div>
</div>
<div className="basis-1/3">
<div className="item-title-small">
Number of Periods Included <br />
<div className="item">(to be calculated)</div>
<div className="item">{numRecordsForHeatingCalculations}</div>
<br />
Daily non-heating Usage <br />
<div className="item">
{heatLoadAnalysis.otherFuelUsage} therms
{/* Rounding to two decimal places */}
{summaryOutputs?.get('other_fuel_usage').toFixed(2)} therms
</div>{' '}
</div>
</div>
<div className="basis-1/3">
<div className="item-title-small">
Standard Deviation of UA <br />
<div className="item">
{heatLoadAnalysis.standardDeviationHeatLossRate} %
{/* Rounding to two decimal places */}
{(
summaryOutputs?.get('standard_deviation_of_heat_loss_rate') *
100
).toFixed(2)}{' '}
%
</div>{' '}
<br />
Whole-home UA
<br />
<div className="item">
{heatLoadAnalysis.wholeHomeHeatLossRate} BTU/h-°F
{/* Rounding to zero decimal places */}
{summaryOutputs?.get('whole_home_heat_loss_rate').toFixed(0)}{' '}
BTU/h-°F
</div>{' '}
<br />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import { EnergyUseHistoryChart } from './EnergyUseHistoryChart.tsx'


// export function EnergyUseHistory(props: EnergyUseProps) {
export function EnergyUseHistory() {
export function EnergyUseHistory(props: { usage_data: any }) {
// console.log(`EnergyUseHistory:`, props.usage_data);
const titleClass = 'text-5xl font-extrabold tracking-wide mt-10'
// const subtitleClass = 'text-2xl font-semibold text-zinc-950 mt-9'

return (

<div>
<h2 className={`${titleClass}`}>Energy Use History</h2>
<h2 className={`${titleClass} pb-6`}>Energy Use History</h2>
<div>
<Suspense fallback={'<div>Blah</div>'}>
<input
Expand All @@ -36,8 +37,8 @@ export function EnergyUseHistory() {
(<a className="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80 gap-1" href="https://github.com/codeforboston/home-energy-analysis-tool/issues/162#issuecomment-2246594484">Get example file here</a>)

</div>
<AnalysisHeader />
<EnergyUseHistoryChart />
{props.usage_data && <AnalysisHeader usage_data={ props.usage_data } /> }
{props.usage_data && <EnergyUseHistoryChart usage_data={props.usage_data} />}
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type z } from 'zod'
import { type NaturalGasBillRecord as NaturalGasBillRecordZod } from '#types/index'
import { NaturalGasUsageData, type NaturalGasBillRecord as NaturalGasBillRecordZod } from '#types/index'
import { Checkbox } from '../../../../components/ui/checkbox.tsx'

import {
Expand All @@ -11,75 +11,139 @@ import {
TableRow,
} from '../../../../components/ui/table.tsx'

type NaturalGasBillRecord = z.infer<typeof NaturalGasBillRecordZod>
const naturalGasBillRecord01: NaturalGasBillRecord = {
periodStartDate: new Date('12/08/2017'),
periodEndDate: new Date('01/07/2018'),
usageTherms: 197,
inclusionOverride: 'Include',
}
import HeatingUsage from './assets/HeatingUsage.png'
import NonHeatingUsage from './assets/NonHeatingUsage.png'
import NotAllowedInCalculations from './assets/NotAllowedInCalculations.png'

import { tr } from '@faker-js/faker'

Check warning on line 18 in heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistoryChart.tsx

View workflow job for this annotation

GitHub Actions / ⬣ Heat-Stack - ESLint

`@faker-js/faker` import should occur before import of `zod`

// type NaturalGasBillRecord = z.infer<typeof NaturalGasBillRecordZod>
// const naturalGasBillRecord01: NaturalGasBillRecord = {
// periodStartDate: new Date('12/08/2017'),
// periodEndDate: new Date('01/07/2018'),
// usageTherms: 197,
// inclusionOverride: 'Include',
// }

// const naturalGasBillRecord02: NaturalGasBillRecord = {
// periodStartDate: new Date('01/08/2018'),
// periodEndDate: new Date('02/07/2018'),
// usageTherms: 205,
// inclusionOverride: 'Include',
// }

// const naturalGasBillRecord03: NaturalGasBillRecord = {
// periodStartDate: new Date('02/08/2018'),
// periodEndDate: new Date('03/07/2018'),
// usageTherms: 220,
// inclusionOverride: 'Include',
// }

// const naturalGasBillRecord04: NaturalGasBillRecord = {
// periodStartDate: new Date('03/08/2018'),
// periodEndDate: new Date('04/07/2018'),
// usageTherms: 196,
// inclusionOverride: 'Include',
// }

// const naturalGasBill = [
// naturalGasBillRecord01,
// naturalGasBillRecord02,
// naturalGasBillRecord03,
// naturalGasBillRecord04,
// ]

const naturalGasBillRecord02: NaturalGasBillRecord = {
periodStartDate: new Date('01/08/2018'),
periodEndDate: new Date('02/07/2018'),
usageTherms: 205,
inclusionOverride: 'Include',
}

const naturalGasBillRecord03: NaturalGasBillRecord = {
periodStartDate: new Date('02/08/2018'),
periodEndDate: new Date('03/07/2018'),
usageTherms: 220,
inclusionOverride: 'Include',
}

const naturalGasBillRecord04: NaturalGasBillRecord = {
periodStartDate: new Date('03/08/2018'),
periodEndDate: new Date('04/07/2018'),
usageTherms: 196,
inclusionOverride: 'Include',
}

const naturalGasBill = [
naturalGasBillRecord01,
naturalGasBillRecord02,
naturalGasBillRecord03,
naturalGasBillRecord04,
]

export function EnergyUseHistoryChart() {
// export function EnergyUseHistoryChart(props: z.infer<typeof NaturalGasUsageData>) {
export function EnergyUseHistoryChart(props: { usage_data: any }) {
console.log("EnergyUseHistoryChart Component:", props.usage_data?.get('billing_records'))

const billingRecords = props.usage_data?.get('billing_records')

const handleOverrideCheckboxChange = () => {
console.log("handleOverrideCheckboxChange")

}

return (
<Table>
<Table id='EnergyUseHistoryChart'>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">#</TableHead>
<TableHead>Include Data</TableHead>
<TableHead>Allowed Usage*</TableHead>
{/* TODO: add help text */}
<TableHead>Start Date</TableHead>
<TableHead>End Date</TableHead>
<TableHead>Days in Bill</TableHead>
<TableHead>Usage (therms)</TableHead>
<TableHead>Whole-home UA</TableHead>
<TableHead>Days in Period</TableHead>
<TableHead>Gas Usage (therms)</TableHead>
<TableHead>Whole-home UA (BTU/h-°F)</TableHead>
<TableHead>Override Default</TableHead>
{/* TODO: add help text */}
</TableRow>
</TableHeader>
<TableBody>
{naturalGasBill.map((period, index) => {
const timeInPeriod =
period.periodEndDate.getTime() - period.periodStartDate.getTime()
{/* {naturalGasBill.map((period, index) => { */}
{billingRecords.map((period: Map<string, any>, index: number) => {

const startDate = new Date(period.get('period_start_date'))
const endDate = new Date(period.get('period_end_date'))

// Calculate days in period
const timeInPeriod = endDate.getTime() - startDate.getTime()
const daysInPeriod = Math.round(timeInPeriod / (1000 * 3600 * 24))

// Set Analysis Type image and checkbox setting
const analysisType = period.get('analysis_type')
let analysisType_Image = undefined
let overrideCheckboxDisabled = false

/* switch case for 1, -1, 0 */
switch (analysisType){
case 1:
analysisType_Image = HeatingUsage
break
case -1:
analysisType_Image = NonHeatingUsage
break
case 0:
analysisType_Image = NotAllowedInCalculations
overrideCheckboxDisabled = true
break
}

// Adjust inclusion for user input
let calculatedInclusion = period.get('default_inclusion_by_calculation')
if (period.get('inclusion_override')) {
calculatedInclusion = !calculatedInclusion
}

const variant = calculatedInclusion ? 'included' : 'excluded'

return (
<TableRow key={index}>
<TableRow key={index} variant={variant}>
<TableCell className="font-medium">{index + 1}</TableCell>
<TableCell><img src={analysisType_Image} alt='Analysis Type'></img></TableCell>
<TableCell>{startDate.toLocaleDateString()}</TableCell>
<TableCell>{endDate.toLocaleDateString()}</TableCell>
<TableCell>{daysInPeriod}</TableCell>
<TableCell>{period.get('usage')}</TableCell>
<TableCell>
<Checkbox checked={period.inclusionOverride == 'Include'} />
</TableCell>
{period.get('whole_home_heat_loss_rate')?
period.get('whole_home_heat_loss_rate').toFixed(0)
:
"-"
}
</TableCell>
<TableCell>
{period.periodStartDate.toLocaleDateString()}
<Checkbox
checked={period.get('inclusion_override')}
disabled={overrideCheckboxDisabled}
onClick={handleOverrideCheckboxChange}
/>
</TableCell>
<TableCell>{period.periodEndDate.toLocaleDateString()}</TableCell>
<TableCell>{daysInPeriod}</TableCell>
<TableCell>{period.usageTherms}</TableCell>
<TableCell>0</TableCell>
</TableRow>
)
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AnalysisHeader } from './AnalysisHeader.tsx'
// import { AnalysisHeader } from './AnalysisHeader.tsx'
import { HeatLoad } from './Graphs/HeatLoad.tsx'
import { WholeHomeUAComparison } from './Graphs/WholeHomeUAComparison.tsx'

Expand All @@ -13,7 +13,7 @@ export function Graphs() {
</h2>
Fuel Type
{fuel_type}
<AnalysisHeader />
{/* <AnalysisHeader /> */}
<HeatLoad />
<WholeHomeUAComparison />
</div>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 63c6f78

Please sign in to comment.