From 1efe69231dea16c3268c5d85c3c9a57d90cb5b76 Mon Sep 17 00:00:00 2001 From: Guilhem CARRON <gcarron@grandlyon.com> Date: Thu, 29 Jul 2021 14:03:22 +0000 Subject: [PATCH] feat: add dju processing in order to retrive a more accurate value in the app --- manifest.webapp | 10 +++ .../Analysis/AnalysisConsumption.spec.tsx | 26 ++++++ .../Analysis/AnalysisConsumption.tsx | 17 +++- src/components/Analysis/MonthlyAnalysis.tsx | 1 + src/services/profileType.service.spec.ts | 85 ++++++++++++------ src/services/profileType.service.ts | 89 +++++++++++++++---- 6 files changed, 183 insertions(+), 45 deletions(-) diff --git a/manifest.webapp b/manifest.webapp index fc8c4c31a..929187be6 100644 --- a/manifest.webapp +++ b/manifest.webapp @@ -42,6 +42,9 @@ "settings": { "description": "Requis pour afficher les paramètres Cozy dans la barre Cozy." }, + "ecolyo-dju": { + "description": "Requis pour récupérer les dju." + }, "dacc-dev": { "description": "Requis pour envoyer des statistiques d'utilisation anonymisées." }, @@ -84,6 +87,9 @@ "settings": { "description": "Required to display Cozy settings in the Cozy bar." }, + "ecolyo-dju": { + "description": "Required to gather dju data." + }, "dacc-dev": { "description": "Required for sending cozy anonymized stats." }, @@ -150,6 +156,10 @@ "type": "io.cozy.settings", "verbs": ["GET"] }, + "ecolyo-dju": { + "type": "org.ecolyo.dju", + "verbs": ["GET"] + }, "dacc": { "type": "cc.cozycloud.dacc", "verbs": ["ALL"] diff --git a/src/components/Analysis/AnalysisConsumption.spec.tsx b/src/components/Analysis/AnalysisConsumption.spec.tsx index 68edcd45b..5414edde9 100644 --- a/src/components/Analysis/AnalysisConsumption.spec.tsx +++ b/src/components/Analysis/AnalysisConsumption.spec.tsx @@ -12,6 +12,7 @@ import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' import { mockMonthlyForecastJanuaryTestProfile1 } from '../../../tests/__mocks__/profileType.mock' import { FluidType } from 'enum/fluid.enum' import { PerformanceIndicator } from 'models' +import { DateTime } from 'luxon' jest.mock('cozy-ui/transpiled/react/I18n', () => { return { @@ -82,6 +83,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) @@ -110,6 +114,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={mockAggregatedPerformanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) @@ -133,6 +140,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) @@ -165,6 +175,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) @@ -206,6 +219,7 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={profileData.monthlyAnalysisDate} /> </Provider> ) @@ -250,6 +264,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) @@ -290,6 +307,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) @@ -330,6 +350,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) @@ -365,6 +388,9 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} + analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { + zone: 'utc', + })} /> </Provider> ) diff --git a/src/components/Analysis/AnalysisConsumption.tsx b/src/components/Analysis/AnalysisConsumption.tsx index e0aba7784..ca4bfc344 100644 --- a/src/components/Analysis/AnalysisConsumption.tsx +++ b/src/components/Analysis/AnalysisConsumption.tsx @@ -11,18 +11,23 @@ import Button from '@material-ui/core/Button' import AnalysisConsumptionRow from 'components/Analysis/AnalysisConsumptionRow' import StyledCard from 'components/CommonKit/Card/StyledCard' import ProfileTypeService from 'services/profileType.service' +import { Client, useClient } from 'cozy-client' +import { DateTime } from 'luxon' interface AnalysisConsumptionProps { aggregatedPerformanceIndicator: PerformanceIndicator performanceIndicators: PerformanceIndicator[] + analysisDate: DateTime } const AnalysisConsumption: React.FC<AnalysisConsumptionProps> = ({ aggregatedPerformanceIndicator, performanceIndicators, + analysisDate, }: AnalysisConsumptionProps) => { const { t } = useI18n() const history = useHistory() + const client: Client = useClient() const userPriceConsumption: number = aggregatedPerformanceIndicator.value || 0 const profile: Profile = useSelector( (state: AppStore) => state.ecolyo.profile @@ -65,12 +70,14 @@ const AnalysisConsumption: React.FC<AnalysisConsumptionProps> = ({ useEffect(() => { let subscribed = true - function loadAverageComsumption() { + async function loadAverageComsumption() { const profileTypeService: ProfileTypeService = new ProfileTypeService( - profile.profileType + profile.profileType, + client, + analysisDate.year ) - const monthlyForecast: MonthlyForecast = profileTypeService.getMonthlyForecast( - profile.monthlyAnalysisDate.month - 1 + const monthlyForecast: MonthlyForecast = await profileTypeService.getMonthlyForecast( + analysisDate.month - 1 ) if (subscribed) { setForecast(monthlyForecast) @@ -87,6 +94,8 @@ const AnalysisConsumption: React.FC<AnalysisConsumptionProps> = ({ profile.profileType, profile.monthlyAnalysisDate.month, getTotalValueWithConnectedFluids, + client, + analysisDate.month, ]) return ( diff --git a/src/components/Analysis/MonthlyAnalysis.tsx b/src/components/Analysis/MonthlyAnalysis.tsx index 63c965464..77131983d 100644 --- a/src/components/Analysis/MonthlyAnalysis.tsx +++ b/src/components/Analysis/MonthlyAnalysis.tsx @@ -131,6 +131,7 @@ const MonthlyAnalysis: React.FC<MonthlyAnalysisProps> = ({ aggregatedPerformanceIndicators } performanceIndicators={performanceIndicators} + analysisDate={analysisDate} /> ) : ( <AnalysisErrorModal /> diff --git a/src/services/profileType.service.spec.ts b/src/services/profileType.service.spec.ts index 54fb34f55..302f69e63 100644 --- a/src/services/profileType.service.spec.ts +++ b/src/services/profileType.service.spec.ts @@ -35,9 +35,11 @@ import { mockMonthlyForecastJanuaryTest1WithFullArrays, } from '../../tests/__mocks__/profileType.mock' import ProfileTypeService from './profileType.service' +import mockClient from '../../tests/__mocks__/client' + const wrongNumber = 99999 describe('ProfileType service', () => { - const profileTypeService = new ProfileTypeService(mockProfileType) + const profileTypeService = new ProfileTypeService(mockProfileType, mockClient) describe('calculateWarmingEstimatedConsumption', () => { it('shoud calculate the Warming Estimated Consumption', () => { @@ -51,29 +53,35 @@ describe('ProfileType service', () => { ) expect(correctedConsumption).toEqual(mockCorrectedConsumption) }) - it('shoud calculate the Warming Month Consumption', () => { - const monthConsumption = profileTypeService.calculateWarmingMonthConsumption( + it('shoud calculate the Warming Month Consumption', async () => { + const monthConsumption = await profileTypeService.calculateWarmingMonthConsumption( mockCorrectedConsumption, 3 ) expect(monthConsumption).toEqual(mockMonthConsumption) }) - it('shoud get the heating consumption', () => { - const monthConsumption = profileTypeService.getMonthHeating(3) + it('shoud get the heating consumption', async () => { + const monthConsumption = await profileTypeService.getMonthHeating(3) expect(monthConsumption).toEqual(mockMonthConsumption) }) }) describe('shoud get the heating consumption for a flat with collective heating', () => { - const _profileTypeService = new ProfileTypeService(mockProfileType1) - it('shoud get the heating consumption', () => { - const monthConsumption = _profileTypeService.getMonthHeating(2) + const _profileTypeService = new ProfileTypeService( + mockProfileType1, + mockClient + ) + it('shoud get the heating consumption', async () => { + const monthConsumption = await _profileTypeService.getMonthHeating(2) expect(monthConsumption).toEqual(mockMonthConsumption1) }) }) describe('shoud get the heating consumption for a house with individual heating, facilities, installation and individual work', () => { - const _profileTypeService = new ProfileTypeService(mockProfileType2) - it('shoud get the heating consumption', () => { - const monthConsumption = _profileTypeService.getMonthHeating(1) + const _profileTypeService = new ProfileTypeService( + mockProfileType2, + mockClient + ) + it('shoud get the heating consumption', async () => { + const monthConsumption = await _profileTypeService.getMonthHeating(1) expect(monthConsumption).toEqual(mockMonthConsumption2) }) }) @@ -121,7 +129,10 @@ describe('ProfileType service', () => { describe('shoud get the month cooking consumption', () => { it('shoud get the month cooking consumption', () => { - const _profileTypeService = new ProfileTypeService(mockProfileType) + const _profileTypeService = new ProfileTypeService( + mockProfileType, + mockClient + ) const monthCookingConsumption = _profileTypeService.getMonthCookingConsumption( 1 @@ -131,7 +142,10 @@ describe('ProfileType service', () => { }) describe('shoud get the month electric specific consumption', () => { it('shoud get the electric specific consumption', () => { - const _profileTypeService = new ProfileTypeService(mockProfileType) + const _profileTypeService = new ProfileTypeService( + mockProfileType, + mockClient + ) const monthElectricSpecificConsumption = _profileTypeService.getMonthElectricSpecificConsumption( 1 @@ -143,7 +157,10 @@ describe('ProfileType service', () => { }) describe('shoud get the month cold water consumption', () => { it('shoud get the cold water consumption', () => { - const _profileTypeService = new ProfileTypeService(mockProfileType) + const _profileTypeService = new ProfileTypeService( + mockProfileType, + mockClient + ) const monthColdWaterConsumption = _profileTypeService.getMonthColdWaterConsumption( 1 ) @@ -153,22 +170,31 @@ describe('ProfileType service', () => { }) describe('shoud get the monthly Forecast', () => { // For month of january - const profileTypeService1 = new ProfileTypeService(mockTestProfile1) - it('shoud get the monthly forecast for test profile 1', () => { - const monthlyForecast = profileTypeService1.getMonthlyForecast(1) + const profileTypeService1 = new ProfileTypeService( + mockTestProfile1, + mockClient + ) + it('shoud get the monthly forecast for test profile 1', async () => { + const monthlyForecast = await profileTypeService1.getMonthlyForecast(1) expect(monthlyForecast).toEqual(mockMonthlyForecastJanuaryTestProfile1) }) - const profileTypeService2 = new ProfileTypeService(mockTestProfile2) - it('shoud get the monthly forecast for test profile 2', () => { - const monthlyForecast = profileTypeService2.getMonthlyForecast(1) + const profileTypeService2 = new ProfileTypeService( + mockTestProfile2, + mockClient + ) + it('shoud get the monthly forecast for test profile 2', async () => { + const monthlyForecast = await profileTypeService2.getMonthlyForecast(1) expect(monthlyForecast).toEqual(mockMonthlyForecastJanuaryTestProfile2) }) - const profileTypeService3 = new ProfileTypeService(mockTestProfile3) - it('shoud get the monthly forecast for test profile 3', () => { - const monthlyForecast = profileTypeService3.getMonthlyForecast(1) + const profileTypeService3 = new ProfileTypeService( + mockTestProfile3, + mockClient + ) + it('shoud get the monthly forecast for test profile 3', async () => { + const monthlyForecast = await profileTypeService3.getMonthlyForecast(1) expect(monthlyForecast).toEqual(mockMonthlyForecastJanuaryTestProfile3) }) - it('shoud get the monthly forecast for test profile 1 with wall and roof and window insulation', () => { + it('shoud get the monthly forecast for test profile 1 with wall and roof and window insulation', async () => { const mockProfileWithThreeInsulation: ProfileType = { ...mockTestProfile1, individualInsulationWork: [ @@ -180,9 +206,10 @@ describe('ProfileType service', () => { hasReplacedHeater: ThreeChoicesAnswer.YES, } const _profileTypeService = new ProfileTypeService( - mockProfileWithThreeInsulation + mockProfileWithThreeInsulation, + mockClient ) - const monthlyForecast = _profileTypeService.getMonthlyForecast(1) + const monthlyForecast = await _profileTypeService.getMonthlyForecast(1) expect(monthlyForecast).toEqual( mockMonthlyForecastJanuaryTest1WithFullArrays ) @@ -406,4 +433,10 @@ describe('ProfileType service', () => { expect(result).toEqual(expectedResult) }) }) + describe('getDjU', () => { + it('shoud return default dju', async () => { + const result = await profileTypeService.fetchDJU(2) + expect(result).toEqual(363) + }) + }) }) diff --git a/src/services/profileType.service.ts b/src/services/profileType.service.ts index c750cf723..51526c030 100644 --- a/src/services/profileType.service.ts +++ b/src/services/profileType.service.ts @@ -25,12 +25,17 @@ import { } from 'enum/profileType.enum' import { FluidType } from 'enum/fluid.enum' import ConverterService from './converter.service' +import { Client } from 'cozy-client' export default class ProfileTypeService { private readonly profileType: ProfileType + private readonly _client: Client + private readonly year: number - constructor(profileType: ProfileType) { + constructor(profileType: ProfileType, _client: Client, year: number) { this.profileType = profileType + this._client = _client + this.year = year } /** * calculateWarmingEstimatedConsumption @@ -170,11 +175,11 @@ export default class ProfileTypeService { @param {number} number @returns {number} monthConsumption */ - public calculateWarmingMonthConsumption( + public async calculateWarmingMonthConsumption( correctedConsumption: number, month: number - ): number { - const djuCurrentMonth = heatingData.dju_average_by_month[month - 1] + ): Promise<number> { + const djuCurrentMonth = await this.fetchDJU(month) const monthConsumption = (correctedConsumption / heatingData.dju_annual) * djuCurrentMonth @@ -186,12 +191,12 @@ export default class ProfileTypeService { * @param {number} month * @returns {number} Month heating consumption in kw/h */ - public getMonthHeating(month: number): number { + public async getMonthHeating(month: number): Promise<number> { const estimatedConsumption = this.calculateWarmingEstimatedConsumption() const correctedConsumption = this.calculateWarmingCorrectedConsumption( estimatedConsumption ) - const monthConsumption = this.calculateWarmingMonthConsumption( + const monthConsumption = await this.calculateWarmingMonthConsumption( correctedConsumption, month ) @@ -361,10 +366,10 @@ export default class ProfileTypeService { * @param {number} month * @returns {numeber} DetailsMnthlyForecast */ - public getDetailsMonthlyForecast( + public async getDetailsMonthlyForecast( fluidType: FluidType, month: number - ): DetailsMonthlyForecast { + ): Promise<DetailsMonthlyForecast> { const warmingFluid = this.profileType.warmingFluid const hotWaterFluid = this.profileType.hotWaterFluid const cookingFluid = this.profileType.cookingFluid @@ -374,7 +379,7 @@ export default class ProfileTypeService { this.profileType.heating === IndividualOrCollective.COLLECTIVE ? null : fluidType === warmingFluid - ? this.getMonthHeating(month) + ? await this.getMonthHeating(month) : null, ecsConsumption: this.profileType.heating === IndividualOrCollective.COLLECTIVE @@ -404,8 +409,11 @@ export default class ProfileTypeService { * @param {number} month @returns {FluidForecast} fluidForecast */ - public getFluidForecast(fluidType: FluidType, month: number): FluidForecast { - const detailsMonthlyForecast: DetailsMonthlyForecast = this.getDetailsMonthlyForecast( + public async getFluidForecast( + fluidType: FluidType, + month: number + ): Promise<FluidForecast> { + const detailsMonthlyForecast: DetailsMonthlyForecast = await this.getDetailsMonthlyForecast( fluidType, month ) @@ -434,16 +442,16 @@ export default class ProfileTypeService { * @param {number} month * @returns {MonthlyForecast} MonthlyForecast */ - public getMonthlyForecast(month: number): MonthlyForecast { - const elecForecast: FluidForecast = this.getFluidForecast( + public async getMonthlyForecast(month: number): Promise<MonthlyForecast> { + const elecForecast: FluidForecast = await this.getFluidForecast( FluidType.ELECTRICITY, month ) - const waterForecast: FluidForecast = this.getFluidForecast( + const waterForecast: FluidForecast = await this.getFluidForecast( FluidType.WATER, month ) - const gasForecast: FluidForecast = this.getFluidForecast( + const gasForecast: FluidForecast = await this.getFluidForecast( FluidType.GAS, month ) @@ -456,6 +464,57 @@ export default class ProfileTypeService { return monthlyForecast } + /** + * Try to fetch dju from remote doctype, if no data or error, return default data + * @param {Client} client + * @param {number} month + * @returns {number} monthDju + */ + public fetchDJU = async (month: number): Promise<number> => { + const startDate: string = DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .set({ year: this.year }) + .set({ month: month }) + .startOf('month') + .minus({ minutes: 5 }) + .toISO() + const endDate: string = DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .set({ year: this.year }) + .set({ month: month }) + .endOf('month') + .plus({ minutes: 5 }) + .toISO() + //We add or remove 5 minutes so the api returns the exact period + try { + const result = await this._client + .getStackClient() + .fetchJSON( + 'GET', + `/remote/org.ecolyo.dju?observedProperty=degreeDay&startDate=${startDate}&endDate=${endDate}` + ) + let monthDju = 0 + + if (result) { + for (const observation of result.observations) { + monthDju += observation.result.value + } + } + if (monthDju === 0) { + return heatingData.dju_average_by_month[month - 1] + } else { + return monthDju + } + } catch (error) { + console.log('errFetch', error) + return heatingData.dju_average_by_month[month - 1] + } + } + /** * getNextFormStep * @param {ProfileTypeStepForm} step -- GitLab