Skip to content
Snippets Groups Projects
Commit 90dabbd9 authored by Adel LAKHDAR's avatar Adel LAKHDAR
Browse files

feat(astuces) - Retravailler l'ordre d'affichage des astuces"

parent b0ae4acb
No related branches found
No related tags found
2 merge requests!11623.0 Release,!1143feat(astuces) - Retravailler l'ordre d'affichage des astuces"
...@@ -25,4 +25,6 @@ export interface Ecogesture { ...@@ -25,4 +25,6 @@ export interface Ecogesture {
_id: string _id: string
_rev?: string _rev?: string
_type?: string _type?: string
/** computed value with efficiency, difficulty and current season */
score?: number
} }
...@@ -10,9 +10,11 @@ import { ...@@ -10,9 +10,11 @@ import {
ecogesturesECSData, ecogesturesECSData,
ecogesturesHeatingData, ecogesturesHeatingData,
mockedEcogesturesData, mockedEcogesturesData,
mockedEcogesturesSortedData,
} from 'tests/__mocks__/ecogesturesData.mock' } from 'tests/__mocks__/ecogesturesData.mock'
import { mockProfileEcogesture } from 'tests/__mocks__/profileEcogesture.mock' import { mockProfileEcogesture } from 'tests/__mocks__/profileEcogesture.mock'
import { getError } from 'tests/__mocks__/testUtils' import { getError } from 'tests/__mocks__/testUtils'
import { SUMMER_MONTH_END, SUMMER_MONTH_START } from 'utils/date'
import { hashFile } from 'utils/hash' import { hashFile } from 'utils/hash'
import EcogestureService from './ecogesture.service' import EcogestureService from './ecogesture.service'
...@@ -33,6 +35,13 @@ const mockQueryResultMockedEcogestures: QueryResult<Ecogesture[]> = { ...@@ -33,6 +35,13 @@ const mockQueryResultMockedEcogestures: QueryResult<Ecogesture[]> = {
skip: 0, skip: 0,
} }
const mockQueryResultMockedSortedEcogestures: QueryResult<Ecogesture[]> = {
data: mockedEcogesturesSortedData,
bookmark: '',
next: false,
skip: 0,
}
const mockQueryResultEmpty: QueryResult<Ecogesture[]> = { const mockQueryResultEmpty: QueryResult<Ecogesture[]> = {
data: [], data: [],
bookmark: '', bookmark: '',
...@@ -40,13 +49,19 @@ const mockQueryResultEmpty: QueryResult<Ecogesture[]> = { ...@@ -40,13 +49,19 @@ const mockQueryResultEmpty: QueryResult<Ecogesture[]> = {
skip: 0, skip: 0,
} }
const currentMonth = new Date().getMonth() + 1
const isSummerOrSpring =
currentMonth >= SUMMER_MONTH_START && currentMonth <= SUMMER_MONTH_END
describe('Ecogesture service', () => { describe('Ecogesture service', () => {
const ecogestureService = new EcogestureService(mockClient) const ecogestureService = new EcogestureService(mockClient)
describe('getAllEcogestures', () => { describe('getAllEcogestures', () => {
it('should return all ecogestures', async () => { it('should return all ecogestures', async () => {
mockClient.query.mockResolvedValueOnce(mockQueryResultMockedEcogestures) mockClient.query.mockResolvedValueOnce(
mockQueryResultMockedSortedEcogestures
)
const result = await ecogestureService.getAllEcogestures() const result = await ecogestureService.getAllEcogestures()
expect(result).toEqual(mockedEcogesturesData) expect(result).toEqual(mockedEcogesturesSortedData)
}) })
it('should return empty array when no ecogestures stored', async () => { it('should return empty array when no ecogestures stored', async () => {
mockClient.query.mockResolvedValueOnce(mockQueryResultEmpty) mockClient.query.mockResolvedValueOnce(mockQueryResultEmpty)
...@@ -181,8 +196,9 @@ describe('Ecogesture service', () => { ...@@ -181,8 +196,9 @@ describe('Ecogesture service', () => {
expect(result.includes(BoilerEcogestureFalse[0])).toBeTruthy() expect(result.includes(BoilerEcogestureFalse[0])).toBeTruthy()
}) })
}) })
describe('getEcogestureListByProfile', () => { describe('getEcogestureListByProfile', () => {
it('should return ecogesture list according to profile ecogesture, sorted and filtered', async () => { it('should return ecogesture list according to profile ecogesture, sorted and filtered, depending on the current season (WINTER/SUMMER)', async () => {
const mockProfileEcogestureFull: ProfileEcogesture = { const mockProfileEcogestureFull: ProfileEcogesture = {
...mockProfileEcogesture, ...mockProfileEcogesture,
equipments: [EquipmentType.WASHING_MACHINE, EquipmentType.DISHWASHER], equipments: [EquipmentType.WASHING_MACHINE, EquipmentType.DISHWASHER],
...@@ -193,10 +209,48 @@ describe('Ecogesture service', () => { ...@@ -193,10 +209,48 @@ describe('Ecogesture service', () => {
const result = await ecogestureService.getEcogestureListByProfile( const result = await ecogestureService.getEcogestureListByProfile(
mockProfileEcogestureFull mockProfileEcogestureFull
) )
expect(result.length).toBe(2) expect(result.length).toBe(2)
expect(result[0]).toBe(mockedEcogesturesData[0]) if (isSummerOrSpring) {
// eslint-disable-next-line jest/no-conditional-expect
expect(result[1]).toBe(mockedEcogesturesData[0])
} else {
// eslint-disable-next-line jest/no-conditional-expect
expect(result[0]).toBe(mockedEcogesturesData[0])
}
}) })
}) })
describe('calculateScore', () => {
describe('during summer season', () => {
if (isSummerOrSpring) {
it('should return correct scores for each ecogesture', () => {
const scores = mockedEcogesturesData.map(ecogesture =>
ecogestureService.calculateScore(ecogesture)
)
expect(scores[0]).toBe(0)
expect(scores[1]).toBe(8)
expect(scores[2]).toBe(1)
})
}
})
describe('during winter season', () => {
if (!isSummerOrSpring) {
it('should return correct scores for each ecogesture', () => {
const scores = mockedEcogesturesData.map(ecogesture =>
ecogestureService.calculateScore(ecogesture)
)
expect(scores[0]).toBe(8)
expect(scores[1]).toBe(0)
expect(scores[2]).toBe(1)
})
}
})
})
describe('getEcogesturesByIds', () => { describe('getEcogesturesByIds', () => {
it('Should return corresponding ecogestures', async () => { it('Should return corresponding ecogestures', async () => {
const mockQueryResult: QueryResult<Ecogesture[]> = { const mockQueryResult: QueryResult<Ecogesture[]> = {
......
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
} from 'enums' } from 'enums'
import { orderBy } from 'lodash' import { orderBy } from 'lodash'
import { Ecogesture, ProfileEcogesture } from 'models' import { Ecogesture, ProfileEcogesture } from 'models'
import { getCurrentSeason, getOppositeSeason } from 'utils/date'
import { logDuration } from 'utils/duration' import { logDuration } from 'utils/duration'
import { hashFile } from 'utils/hash' import { hashFile } from 'utils/hash'
import logApp from 'utils/logger' import logApp from 'utils/logger'
...@@ -36,7 +37,7 @@ export default class EcogestureService { ...@@ -36,7 +37,7 @@ export default class EcogestureService {
}> { }> {
const startTime = performance.now() const startTime = performance.now()
const hashEcogestureType = hashFile(ecogestureData) const hashEcogestureType = hashFile(ecogestureData)
const ecogestures = await this.getAllEcogestures(undefined, true) const ecogestures = await this.getAllEcogestures(true)
if (!ecogestures || ecogestures?.length === 0) { if (!ecogestures || ecogestures?.length === 0) {
// Populate data if none ecogesture exists // Populate data if none ecogesture exists
...@@ -115,18 +116,35 @@ export default class EcogestureService { ...@@ -115,18 +116,35 @@ export default class EcogestureService {
} }
} }
// TODO add default params /**
public async getAllEcogestures( * Calculate Ecogesture score from efficiency and difficulty
seasonFilter?: Season, *
orderByID?: boolean * Base score = efficiency * 2 - difficulty
): Promise<Ecogesture[]> { * - If current season, Score + 1
* - If opposite season, Score = 0
* - If no season, base score
*/
public calculateScore(ecogesture: Ecogesture): number {
const score = ecogesture.efficiency * 2 - ecogesture.difficulty
const currentSeason = getCurrentSeason()
const oppositeSeason = getOppositeSeason(currentSeason)
if (ecogesture.season !== Season.NONE) {
if (ecogesture.season === currentSeason) {
return score + 1
} else if (ecogesture.season === oppositeSeason) {
return 0
}
}
return score
}
public async getAllEcogestures(orderByID?: boolean): Promise<Ecogesture[]> {
let query: QueryDefinition = Q(ECOGESTURE_DOCTYPE) let query: QueryDefinition = Q(ECOGESTURE_DOCTYPE)
if (seasonFilter && seasonFilter !== Season.NONE) {
query = query if (orderByID) {
.where({ season: { $ne: seasonFilter } })
.indexFields(['season'])
.sortBy([{ season: 'desc' }])
} else if (orderByID) {
query = query query = query
.where({}) .where({})
.indexFields(['_id']) .indexFields(['_id'])
...@@ -141,17 +159,13 @@ export default class EcogestureService { ...@@ -141,17 +159,13 @@ export default class EcogestureService {
const { data: ecogestures }: QueryResult<Ecogesture[]> = const { data: ecogestures }: QueryResult<Ecogesture[]> =
await this._client.query(query) await this._client.query(query)
if (seasonFilter && seasonFilter !== Season.NONE) { for (const ecogesture of ecogestures) {
const { data: ecogesturesWithSeason }: QueryResult<Ecogesture[]> = const score = this.calculateScore(ecogesture)
await this._client.query( ecogesture.score = score
Q(ECOGESTURE_DOCTYPE)
.where({ season: { $eq: seasonFilter } })
.indexFields(['season'])
.sortBy([{ season: 'asc' }])
)
return [...ecogesturesWithSeason, ...ecogestures]
} }
return ecogestures
const sortedByScoreDesc = orderBy(ecogestures, 'score', 'desc')
return sortedByScoreDesc
} }
/** /**
...@@ -269,14 +283,17 @@ export default class EcogestureService { ...@@ -269,14 +283,17 @@ export default class EcogestureService {
profileEcogesture: ProfileEcogesture profileEcogesture: ProfileEcogesture
): Promise<Ecogesture[]> { ): Promise<Ecogesture[]> {
const ecogestureList = await this.getAllEcogestures() const ecogestureList = await this.getAllEcogestures()
const filteredByUsage = this.filterByUsage( const filteredByUsage = this.filterByUsage(
ecogestureList, ecogestureList,
profileEcogesture profileEcogesture
) )
const filteredByEquipment = this.filterByEquipment( const filteredByEquipment = this.filterByEquipment(
filteredByUsage, filteredByUsage,
profileEcogesture profileEcogesture
) )
const filteredFlaggedEcogesture = filteredByEquipment.filter( const filteredFlaggedEcogesture = filteredByEquipment.filter(
ecogesture => ecogesture =>
(ecogesture.objective === false && (ecogesture.objective === false &&
...@@ -284,19 +301,14 @@ export default class EcogestureService { ...@@ -284,19 +301,14 @@ export default class EcogestureService {
ecogesture.viewedInSelection === false) || ecogesture.viewedInSelection === false) ||
ecogesture.viewedInSelection === true ecogesture.viewedInSelection === true
) )
const sortedByDifficultyAndEfficiency = orderBy(
const sortedByScoreDesc = orderBy(
filteredFlaggedEcogesture, filteredFlaggedEcogesture,
[ 'score',
ecogesture => { 'desc'
return ecogesture.difficulty
},
ecogesture => {
return ecogesture.efficiency
},
],
['asc', 'desc']
) )
return sortedByDifficultyAndEfficiency
return sortedByScoreDesc
} }
/** /**
......
import { FluidType, TimeStep } from 'enums' import { FluidType, Season, TimeStep } from 'enums'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import { Dataload } from 'models' import { Dataload } from 'models'
import { getMonthNameWithPrep } from './utils' import { getMonthNameWithPrep } from './utils'
export const SUMMER_MONTH_START = 3
export const SUMMER_MONTH_END = 8
export function compareDates(dateA: DateTime, dateB: DateTime) { export function compareDates(dateA: DateTime, dateB: DateTime) {
return dateA < dateB ? -1 : 1 return dateA < dateB ? -1 : 1
} }
...@@ -144,3 +147,27 @@ export const getActualAnalysisDate = (): DateTime => { ...@@ -144,3 +147,27 @@ export const getActualAnalysisDate = (): DateTime => {
return now.set({ day: 3, month: now.month }) return now.set({ day: 3, month: now.month })
} }
} }
/**
* Determines the current season based on the month of the year.
* @returns {Season} Returns Season.SUMMER if the current month is between March and August, and Season.WINTER for the rest of the months.
*/
export function getCurrentSeason(): Season {
const currentMonth = new Date().getMonth() + 1
if (currentMonth >= SUMMER_MONTH_START && currentMonth <= SUMMER_MONTH_END) {
return Season.SUMMER
} else {
return Season.WINTER
}
}
export function getOppositeSeason(currentSeason: Season): Season {
if (currentSeason === Season.WINTER) {
return Season.SUMMER
} else if (currentSeason === Season.SUMMER) {
return Season.WINTER
} else {
throw new Error('Invalid current season.')
}
}
...@@ -89,6 +89,92 @@ export const mockedEcogesturesData: Ecogesture[] = [ ...@@ -89,6 +89,92 @@ export const mockedEcogesturesData: Ecogesture[] = [
}, },
] ]
export const mockedEcogesturesSortedData: Ecogesture[] = [
{
fluidTypes: [FluidType.ELECTRICITY],
id: 'ECOGESTURE0002',
longDescription:
"Cela permet de garder la fraîcheur à l'intérieur. Le climatiseur n'est pas là pour refroidir la rue mais bien la pièce.",
longName: 'Je ferme mes fenêtres quand la climatisation est en marche',
shortName: 'Portique thermique',
usage: Usage.AIR_CONDITIONING,
impactLevel: 8,
efficiency: 4,
difficulty: 1,
room: [Room.ALL],
season: Season.SUMMER,
equipment: true,
equipmentType: [EquipmentType.AIR_CONDITIONING],
equipmentInstallation: true,
investment: null,
action: false,
actionName: null,
actionDuration: 3,
doing: false,
objective: false,
viewedInSelection: false,
_id: 'ECOGESTURE0002',
_rev: '19-9b604bcf7e55f8650e2be4f676fddaf0',
_type: 'com.grandlyon.ecolyo.ecogesture',
},
{
fluidTypes: [FluidType.ELECTRICITY],
id: 'ECOGESTURE0035',
longDescription:
'Réglez votre climatisation au plus bas à 26 °C et veillez à ce qu’il n’y ait jamais plus de 5 à 7 °C de différence entre l’intérieur et l’extérieur. Attention aux grands écarts de température qui peuvent provoquer des chocs thermiques.',
longName:
'Je règle ma climatisation au plus bas à 26°C en veillant à ce qu’il n’y ait pas jamais plus de 5°C à 7°C de différence entre l’intérieur et l’extérieur.',
shortName: "La Juste Clim'",
usage: Usage.AIR_CONDITIONING,
impactLevel: 8,
efficiency: 4,
difficulty: 2,
room: [Room.ALL],
season: Season.SUMMER,
equipment: true,
equipmentType: [EquipmentType.AIR_CONDITIONING],
equipmentInstallation: true,
investment: null,
action: false,
actionName: null,
actionDuration: 3,
doing: false,
objective: false,
viewedInSelection: false,
_id: 'ECOGESTURE0035',
_rev: '17-666648f24f6c797443470a54785322c5',
_type: 'com.grandlyon.ecolyo.ecogesture',
},
{
fluidTypes: [FluidType.ELECTRICITY],
id: 'ECOGESTURE0034',
longDescription:
"Cela permet d'évite des consommations inutiles. Le froid ne restera pas dans la pièce. Donc il est préférable d'allumer le ventilateur ou climatiseur seulement quand des personnes sont présentes dans la pièce.",
longName:
'Je ne fais pas fonctionner mon ventilateur ou la climatisation dans les pièces non occupées',
shortName: 'Bulles-à-part',
usage: Usage.AIR_CONDITIONING,
impactLevel: 8,
efficiency: 4,
difficulty: 2,
room: [Room.ALL],
season: Season.SUMMER,
equipment: true,
equipmentType: [EquipmentType.AIR_CONDITIONING, EquipmentType.FAN],
equipmentInstallation: true,
investment: null,
action: false,
actionName: null,
actionDuration: 3,
doing: false,
objective: false,
viewedInSelection: false,
_id: 'ECOGESTURE0034',
_rev: '25-75810c7b375dcf2742f0b225fda7c6d6',
_type: 'com.grandlyon.ecolyo.ecogesture',
},
]
export const ecogesturesHeatingData: Ecogesture[] = [ export const ecogesturesHeatingData: Ecogesture[] = [
{ {
fluidTypes: [FluidType.ELECTRICITY], fluidTypes: [FluidType.ELECTRICITY],
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment