diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 62e78fc20f4b8284eaf94de21135d48f6cfc6e9f..03ac42282f60c8799d01ced29c9ac7e57339e3e6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -8,6 +8,7 @@ "dbaeumer.vscode-eslint", "andrejunges.handlebars", "streetsidesoftware.code-spell-checker", - "streetsidesoftware.code-spell-checker-french" + "streetsidesoftware.code-spell-checker-french", + "firsttris.vscode-jest-runner" ] } diff --git a/manifest.webapp b/manifest.webapp index 52a6bc49b62dc410bbbe13049dae55ca282b66c4..12d4e0cf017acee4422a15569d7081c71c40451d 100644 --- a/manifest.webapp +++ b/manifest.webapp @@ -235,7 +235,7 @@ "verbs": ["GET"] }, "ecolyo-dju": { - "type": "org.ecolyo.dju", + "type": "org.ecolyo.dju_v2", "verbs": ["GET"] }, "dacc": { diff --git a/src/components/Action/ActionOnGoing.spec.tsx b/src/components/Action/ActionOnGoing.spec.tsx index 5df6cbd2d62db613bfec354eda4870f9b0344aba..3929b747111b4194ba52c3f4b71f4805a20a0efb 100644 --- a/src/components/Action/ActionOnGoing.spec.tsx +++ b/src/components/Action/ActionOnGoing.spec.tsx @@ -37,7 +37,7 @@ const mockStore = configureStore([]) describe('ActionOnGoing component', () => { const userAction: UserAction = { ecogesture: null, - startDate: DateTime.local() + startDate: DateTime.local(2020, 1, 1) .setZone('utc', { keepLocalTime: true, }) diff --git a/src/components/Action/__snapshots__/ActionChoose.spec.tsx.snap b/src/components/Action/__snapshots__/ActionChoose.spec.tsx.snap index c9e003d09aef9854bee25b2a6ac09ac9287c43cf..7110cbf34e9e6cb1444e6729042e460634500239 100644 --- a/src/components/Action/__snapshots__/ActionChoose.spec.tsx.snap +++ b/src/components/Action/__snapshots__/ActionChoose.spec.tsx.snap @@ -158,160 +158,156 @@ exports[`ActionChoose component should render correctly 1`] = ` } } > - <div - className="action-choose" - > - <ActionBegin - setShowList={[Function]} - userChallenge={ - Object { - "action": Object { - "ecogesture": null, - "startDate": null, - "state": 0, - }, - "description": "Description challenge 2", - "duel": Object { - "description": "Je parie un ours polaire que vous ne pouvez pas consommer moins que #CONSUMPTION € en 1 semaine", - "duration": "P30D", - "fluidTypes": Array [], - "id": "DUEL001", - "startDate": null, - "state": 0, - "threshold": 0, - "title": "Title DUEL001", - "userConsumption": 0, - }, - "endingDate": null, - "exploration": Object { - "complementary_description": "Refaire un tour dans son profil si déjà fait", - "date": null, - "description": "Avoir complété son profil", - "ecogesture_id": "", - "fluid_condition": Array [], - "id": "EXPLORATION001", - "message_success": "Vous avez complété votre profil ou refait un tour dans votre profil", - "progress": 0, - "state": 0, - "target": 1, - "type": 1, - }, - "id": "CHALLENGE0002", - "progress": Object { - "actionProgress": 0, - "explorationProgress": 0, - "quizProgress": 0, + <ActionBegin + setShowList={[Function]} + userChallenge={ + Object { + "action": Object { + "ecogesture": null, + "startDate": null, + "state": 0, + }, + "description": "Description challenge 2", + "duel": Object { + "description": "Je parie un ours polaire que vous ne pouvez pas consommer moins que #CONSUMPTION € en 1 semaine", + "duration": "P30D", + "fluidTypes": Array [], + "id": "DUEL001", + "startDate": null, + "state": 0, + "threshold": 0, + "title": "Title DUEL001", + "userConsumption": 0, + }, + "endingDate": null, + "exploration": Object { + "complementary_description": "Refaire un tour dans son profil si déjà fait", + "date": null, + "description": "Avoir complété son profil", + "ecogesture_id": "", + "fluid_condition": Array [], + "id": "EXPLORATION001", + "message_success": "Vous avez complété votre profil ou refait un tour dans votre profil", + "progress": 0, + "state": 0, + "target": 1, + "type": 1, + }, + "id": "CHALLENGE0002", + "progress": Object { + "actionProgress": 0, + "explorationProgress": 0, + "quizProgress": 0, + }, + "quiz": Object { + "customQuestion": Object { + "interval": 20, + "period": Object {}, + "questionLabel": "Custom1", + "result": 0, + "singleFluid": false, + "timeStep": 20, + "type": 0, }, - "quiz": Object { - "customQuestion": Object { - "interval": 20, - "period": Object {}, - "questionLabel": "Custom1", + "id": "QUIZ001", + "questions": Array [ + Object { + "answers": Array [ + Object { + "answerLabel": "86 km", + "isTrue": true, + }, + Object { + "answerLabel": "78 km", + "isTrue": false, + }, + Object { + "answerLabel": "56 km", + "isTrue": false, + }, + ], + "explanation": "L’aqueduc du Gier est un des aqueducs antiques de Lyon desservant la ville antique de Lugdunum. Avec ses 86 km il est le plus long des quatre aqueducs ayant alimenté la ville en eau, et celui dont les structures sont le mieux conservées. Il doit son nom au fait qu'il puise aux sources du Gier, affluent du Rhône", + "questionLabel": "Quelle longueur faisait l’aqueduc du Gier pour acheminer l’eau sur Lyon à l’époque romaine ?", "result": 0, - "singleFluid": false, - "timeStep": 20, - "type": 0, + "source": "string", }, - "id": "QUIZ001", - "questions": Array [ - Object { - "answers": Array [ - Object { - "answerLabel": "86 km", - "isTrue": true, - }, - Object { - "answerLabel": "78 km", - "isTrue": false, - }, - Object { - "answerLabel": "56 km", - "isTrue": false, - }, - ], - "explanation": "L’aqueduc du Gier est un des aqueducs antiques de Lyon desservant la ville antique de Lugdunum. Avec ses 86 km il est le plus long des quatre aqueducs ayant alimenté la ville en eau, et celui dont les structures sont le mieux conservées. Il doit son nom au fait qu'il puise aux sources du Gier, affluent du Rhône", - "questionLabel": "Quelle longueur faisait l’aqueduc du Gier pour acheminer l’eau sur Lyon à l’époque romaine ?", - "result": 0, - "source": "string", - }, - Object { - "answers": Array [ - Object { - "answerLabel": "1 point d’eau public pour 800 habitants.", - "isTrue": true, - }, - Object { - "answerLabel": "1 point d’eau public pour 400 habitants.", - "isTrue": false, - }, - Object { - "answerLabel": "1 point d’eau public pour 200 habitants.", - "isTrue": false, - }, - ], - "explanation": "string", - "questionLabel": "En 1800 à Lyon, combien de points d'eau y avait-il par habitants ?", - "result": 0, - "source": "string", - }, - Object { - "answers": Array [ - Object { - "answerLabel": "François Mitterrand", - "isTrue": false, - }, - Object { - "answerLabel": "Napoléon Ier", - "isTrue": true, - }, - Object { - "answerLabel": "Napoléon III", - "isTrue": false, - }, - ], - "explanation": "string", - "questionLabel": "Qui officialise la création de la Compagnie Générale des eaux ?", - "result": 0, - "source": "string", - }, - Object { - "answers": Array [ - Object { - "answerLabel": "string", - "isTrue": false, - }, - Object { - "answerLabel": "string", - "isTrue": false, - }, - Object { - "answerLabel": "Aristide Dumont", - "isTrue": true, - }, - ], - "explanation": "string", - "questionLabel": "Quel ingénieur est à l’origine du projet d’alimentation en eau en 1856 ?", - "result": 0, - "source": "string", - }, - ], - "result": 0, - "startDate": null, - "state": 0, - }, + Object { + "answers": Array [ + Object { + "answerLabel": "1 point d’eau public pour 800 habitants.", + "isTrue": true, + }, + Object { + "answerLabel": "1 point d’eau public pour 400 habitants.", + "isTrue": false, + }, + Object { + "answerLabel": "1 point d’eau public pour 200 habitants.", + "isTrue": false, + }, + ], + "explanation": "string", + "questionLabel": "En 1800 à Lyon, combien de points d'eau y avait-il par habitants ?", + "result": 0, + "source": "string", + }, + Object { + "answers": Array [ + Object { + "answerLabel": "François Mitterrand", + "isTrue": false, + }, + Object { + "answerLabel": "Napoléon Ier", + "isTrue": true, + }, + Object { + "answerLabel": "Napoléon III", + "isTrue": false, + }, + ], + "explanation": "string", + "questionLabel": "Qui officialise la création de la Compagnie Générale des eaux ?", + "result": 0, + "source": "string", + }, + Object { + "answers": Array [ + Object { + "answerLabel": "string", + "isTrue": false, + }, + Object { + "answerLabel": "string", + "isTrue": false, + }, + Object { + "answerLabel": "Aristide Dumont", + "isTrue": true, + }, + ], + "explanation": "string", + "questionLabel": "Quel ingénieur est à l’origine du projet d’alimentation en eau en 1856 ?", + "result": 0, + "source": "string", + }, + ], + "result": 0, "startDate": null, - "state": 4, - "success": 1, - "target": 15, - "title": "Challenge 2", - } + "state": 0, + }, + "startDate": null, + "state": 4, + "success": 1, + "target": 15, + "title": "Challenge 2", } - > - <div - className="action-begin" - /> - </ActionBegin> - </div> + } + > + <div + className="action-begin" + /> + </ActionBegin> </ActionChoose> </Provider> `; diff --git a/src/components/Action/__snapshots__/ActionOnGoing.spec.tsx.snap b/src/components/Action/__snapshots__/ActionOnGoing.spec.tsx.snap index b864a4b10757ae8f34fa0664501aa32eb4f3cd4b..bb9fa52a80a3e3659e68e396e173ac96dc8ffde6 100644 --- a/src/components/Action/__snapshots__/ActionOnGoing.spec.tsx.snap +++ b/src/components/Action/__snapshots__/ActionOnGoing.spec.tsx.snap @@ -17,7 +17,7 @@ exports[`ActionOnGoing component should render correctly 1`] = ` userAction={ Object { "ecogesture": null, - "startDate": "2023-06-20T00:00:00.000Z", + "startDate": "2020-01-01T00:00:00.000Z", "state": 1, } } diff --git a/src/components/Action/__snapshots__/ActionView.spec.tsx.snap b/src/components/Action/__snapshots__/ActionView.spec.tsx.snap index 5dd71af41906ccb41a307074e1d74037c51071ac..ec1ba3e6c35f9cfd16362702059d7640b08b3c70 100644 --- a/src/components/Action/__snapshots__/ActionView.spec.tsx.snap +++ b/src/components/Action/__snapshots__/ActionView.spec.tsx.snap @@ -171,156 +171,152 @@ exports[`ActionView component should render ActionChoose component 1`] = ` } } > - <div - className="action-choose" - > - <mock-action-begin - setShowList={[Function]} - userChallenge={ - Object { - "action": Object { - "ecogesture": null, - "startDate": null, - "state": 0, - }, - "description": "Description challenge 2", - "duel": Object { - "description": "Je parie un ours polaire que vous ne pouvez pas consommer moins que #CONSUMPTION € en 1 semaine", - "duration": "P30D", - "fluidTypes": Array [], - "id": "DUEL001", - "startDate": null, - "state": 0, - "threshold": 0, - "title": "Title DUEL001", - "userConsumption": 0, - }, - "endingDate": null, - "exploration": Object { - "complementary_description": "Refaire un tour dans son profil si déjà fait", - "date": null, - "description": "Avoir complété son profil", - "ecogesture_id": "", - "fluid_condition": Array [], - "id": "EXPLORATION001", - "message_success": "Vous avez complété votre profil ou refait un tour dans votre profil", - "progress": 0, - "state": 0, - "target": 1, - "type": 1, - }, - "id": "CHALLENGE0002", - "progress": Object { - "actionProgress": 0, - "explorationProgress": 0, - "quizProgress": 0, + <mock-action-begin + setShowList={[Function]} + userChallenge={ + Object { + "action": Object { + "ecogesture": null, + "startDate": null, + "state": 0, + }, + "description": "Description challenge 2", + "duel": Object { + "description": "Je parie un ours polaire que vous ne pouvez pas consommer moins que #CONSUMPTION € en 1 semaine", + "duration": "P30D", + "fluidTypes": Array [], + "id": "DUEL001", + "startDate": null, + "state": 0, + "threshold": 0, + "title": "Title DUEL001", + "userConsumption": 0, + }, + "endingDate": null, + "exploration": Object { + "complementary_description": "Refaire un tour dans son profil si déjà fait", + "date": null, + "description": "Avoir complété son profil", + "ecogesture_id": "", + "fluid_condition": Array [], + "id": "EXPLORATION001", + "message_success": "Vous avez complété votre profil ou refait un tour dans votre profil", + "progress": 0, + "state": 0, + "target": 1, + "type": 1, + }, + "id": "CHALLENGE0002", + "progress": Object { + "actionProgress": 0, + "explorationProgress": 0, + "quizProgress": 0, + }, + "quiz": Object { + "customQuestion": Object { + "interval": 20, + "period": Object {}, + "questionLabel": "Custom1", + "result": 0, + "singleFluid": false, + "timeStep": 20, + "type": 0, }, - "quiz": Object { - "customQuestion": Object { - "interval": 20, - "period": Object {}, - "questionLabel": "Custom1", + "id": "QUIZ001", + "questions": Array [ + Object { + "answers": Array [ + Object { + "answerLabel": "86 km", + "isTrue": true, + }, + Object { + "answerLabel": "78 km", + "isTrue": false, + }, + Object { + "answerLabel": "56 km", + "isTrue": false, + }, + ], + "explanation": "L’aqueduc du Gier est un des aqueducs antiques de Lyon desservant la ville antique de Lugdunum. Avec ses 86 km il est le plus long des quatre aqueducs ayant alimenté la ville en eau, et celui dont les structures sont le mieux conservées. Il doit son nom au fait qu'il puise aux sources du Gier, affluent du Rhône", + "questionLabel": "Quelle longueur faisait l’aqueduc du Gier pour acheminer l’eau sur Lyon à l’époque romaine ?", "result": 0, - "singleFluid": false, - "timeStep": 20, - "type": 0, + "source": "string", }, - "id": "QUIZ001", - "questions": Array [ - Object { - "answers": Array [ - Object { - "answerLabel": "86 km", - "isTrue": true, - }, - Object { - "answerLabel": "78 km", - "isTrue": false, - }, - Object { - "answerLabel": "56 km", - "isTrue": false, - }, - ], - "explanation": "L’aqueduc du Gier est un des aqueducs antiques de Lyon desservant la ville antique de Lugdunum. Avec ses 86 km il est le plus long des quatre aqueducs ayant alimenté la ville en eau, et celui dont les structures sont le mieux conservées. Il doit son nom au fait qu'il puise aux sources du Gier, affluent du Rhône", - "questionLabel": "Quelle longueur faisait l’aqueduc du Gier pour acheminer l’eau sur Lyon à l’époque romaine ?", - "result": 0, - "source": "string", - }, - Object { - "answers": Array [ - Object { - "answerLabel": "1 point d’eau public pour 800 habitants.", - "isTrue": true, - }, - Object { - "answerLabel": "1 point d’eau public pour 400 habitants.", - "isTrue": false, - }, - Object { - "answerLabel": "1 point d’eau public pour 200 habitants.", - "isTrue": false, - }, - ], - "explanation": "string", - "questionLabel": "En 1800 à Lyon, combien de points d'eau y avait-il par habitants ?", - "result": 0, - "source": "string", - }, - Object { - "answers": Array [ - Object { - "answerLabel": "François Mitterrand", - "isTrue": false, - }, - Object { - "answerLabel": "Napoléon Ier", - "isTrue": true, - }, - Object { - "answerLabel": "Napoléon III", - "isTrue": false, - }, - ], - "explanation": "string", - "questionLabel": "Qui officialise la création de la Compagnie Générale des eaux ?", - "result": 0, - "source": "string", - }, - Object { - "answers": Array [ - Object { - "answerLabel": "string", - "isTrue": false, - }, - Object { - "answerLabel": "string", - "isTrue": false, - }, - Object { - "answerLabel": "Aristide Dumont", - "isTrue": true, - }, - ], - "explanation": "string", - "questionLabel": "Quel ingénieur est à l’origine du projet d’alimentation en eau en 1856 ?", - "result": 0, - "source": "string", - }, - ], - "result": 0, - "startDate": null, - "state": 0, - }, + Object { + "answers": Array [ + Object { + "answerLabel": "1 point d’eau public pour 800 habitants.", + "isTrue": true, + }, + Object { + "answerLabel": "1 point d’eau public pour 400 habitants.", + "isTrue": false, + }, + Object { + "answerLabel": "1 point d’eau public pour 200 habitants.", + "isTrue": false, + }, + ], + "explanation": "string", + "questionLabel": "En 1800 à Lyon, combien de points d'eau y avait-il par habitants ?", + "result": 0, + "source": "string", + }, + Object { + "answers": Array [ + Object { + "answerLabel": "François Mitterrand", + "isTrue": false, + }, + Object { + "answerLabel": "Napoléon Ier", + "isTrue": true, + }, + Object { + "answerLabel": "Napoléon III", + "isTrue": false, + }, + ], + "explanation": "string", + "questionLabel": "Qui officialise la création de la Compagnie Générale des eaux ?", + "result": 0, + "source": "string", + }, + Object { + "answers": Array [ + Object { + "answerLabel": "string", + "isTrue": false, + }, + Object { + "answerLabel": "string", + "isTrue": false, + }, + Object { + "answerLabel": "Aristide Dumont", + "isTrue": true, + }, + ], + "explanation": "string", + "questionLabel": "Quel ingénieur est à l’origine du projet d’alimentation en eau en 1856 ?", + "result": 0, + "source": "string", + }, + ], + "result": 0, "startDate": null, - "state": 4, - "success": 1, - "target": 15, - "title": "Challenge 2", - } + "state": 0, + }, + "startDate": null, + "state": 4, + "success": 1, + "target": 15, + "title": "Challenge 2", } - /> - </div> + } + /> </ActionChoose> </mock-content> </ActionView> diff --git a/src/components/Analysis/AnalysisConsumption.tsx b/src/components/Analysis/AnalysisConsumption.tsx index ad5efafcdb595980f14c6cdaee777e0afe901481..80c8c93bf400a58bf11af6527525aba834d9dda7 100644 --- a/src/components/Analysis/AnalysisConsumption.tsx +++ b/src/components/Analysis/AnalysisConsumption.tsx @@ -110,7 +110,7 @@ const AnalysisConsumption = ({ const profileTypeService: ProfileTypeService = new ProfileTypeService( profileType, client, - analysisMonth.year + analysisMonth.minus({ month: 1 }).year ) const monthlyForecast: MonthlyForecast = await profileTypeService.getMonthlyForecast( diff --git a/src/components/Content/Content.tsx b/src/components/Content/Content.tsx index d1b1a370d08d87c057b7385d3b739b12bcbef122..caf29cd956b6185a27e3130d7256889265622533 100644 --- a/src/components/Content/Content.tsx +++ b/src/components/Content/Content.tsx @@ -7,7 +7,7 @@ import { changeScreenType } from 'store/global/global.actions' import { openFeedbackModal } from 'store/modal/modal.slice' import './content.scss' interface ContentProps { - children?: React.ReactNode + children: React.ReactNode height?: number background?: string } diff --git a/src/components/Ecogesture/EcogestureView.tsx b/src/components/Ecogesture/EcogestureView.tsx index 25e0e53311b4ba1986349711bb1337a913c52a81..afc1e1fe9bddcb1ac9fa0b80273b70bb54950668 100644 --- a/src/components/Ecogesture/EcogestureView.tsx +++ b/src/components/Ecogesture/EcogestureView.tsx @@ -21,7 +21,7 @@ import EcogestureReinitModal from './EcogestureReinitModal' import './ecogestureView.scss' interface TabPanelProps { - children?: React.ReactNode + children: React.ReactNode tab: EcogestureTab value: number } diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 943f408a4f0e458e94fd3dbb2224b1df27ce24f1..428868001f84e7c0efb8d60e5c46008e4e4c73b8 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -15,6 +15,7 @@ interface HeaderProps { /** translation key used as t('key.value') */ desktopTitleKey: string displayBackArrow?: boolean + /** additional information to put below the main header */ children?: React.ReactNode setHeaderHeight(height: number): void backFunction?: () => void diff --git a/src/doctypes/remote/org.ecolyo.dju.ts b/src/doctypes/remote/org.ecolyo.dju.ts new file mode 100644 index 0000000000000000000000000000000000000000..ccc8e113b55304436422772de428c90ed6165094 --- /dev/null +++ b/src/doctypes/remote/org.ecolyo.dju.ts @@ -0,0 +1 @@ +export const REMOTE_ORG_ECOLYO_DJU = '/remote/org.ecolyo.dju_v2' diff --git a/src/models/dju.model.ts b/src/models/dju.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..a74baa1625edf2d6fb82661d45a1e63c330469bc --- /dev/null +++ b/src/models/dju.model.ts @@ -0,0 +1,14 @@ +export interface DjuResult { + fields: string[] + layer_name: string + nb_result: number + table_href: string + values: DjuMeasure[] +} + +export interface DjuMeasure { + horodate: string + measurement: number + observation: string + unit: string +} diff --git a/src/services/consumption.service.ts b/src/services/consumption.service.ts index e929762966bd32633c7d305d806d4fda35464ad5..48fb3f060614ce28957d46b43270676d3fbbdcdc 100644 --- a/src/services/consumption.service.ts +++ b/src/services/consumption.service.ts @@ -564,6 +564,7 @@ export default class ConsumptionDataManager { ): Promise<DataloadEntity[] | EnedisMonthlyAnalysisData[] | null> { const query: QueryDefinition = Q(doctype) .where({}) + .indexFields(['year', 'month']) .sortBy([{ year: 'asc' }, { month: 'asc' }]) .limitBy(1) const data = await this._client.query(query) @@ -575,8 +576,11 @@ export default class ConsumptionDataManager { ): Promise<DataloadEntity | null> { const query: QueryDefinition = Q(doctype) .where({ - price: { - $exists: false, + year: { + $gt: null, + }, + month: { + $gt: null, }, }) .partialIndex({ diff --git a/src/services/ecogesture.service.ts b/src/services/ecogesture.service.ts index b7e0ae588ca8bcf707310532dab4f50aa10bfdb1..0420aa5094fe95e39671618f3433766c7a17b709 100644 --- a/src/services/ecogesture.service.ts +++ b/src/services/ecogesture.service.ts @@ -121,11 +121,18 @@ export default class EcogestureService { if (seasonFilter && seasonFilter !== Season.NONE) { query = query .where({ season: { $ne: seasonFilter } }) + .indexFields(['season']) .sortBy([{ season: 'desc' }]) } else if (orderByID) { - query = query.where({}).sortBy([{ _id: 'asc' }]) + query = query + .where({}) + .indexFields(['_id']) + .sortBy([{ _id: 'asc' }]) } else { - query = query.where({}).sortBy([{ season: 'desc' }]) + query = query + .where({}) + .indexFields(['season']) + .sortBy([{ season: 'desc' }]) } const { data: ecogestures }: QueryResult<Ecogesture[]> = @@ -136,6 +143,7 @@ export default class EcogestureService { await this._client.query( Q(ECOGESTURE_DOCTYPE) .where({ season: { $eq: seasonFilter } }) + .indexFields(['season']) .sortBy([{ season: 'asc' }]) ) return [...ecogesturesWithSeason, ...ecogestures] diff --git a/src/services/enedisMonthlyAnalysisData.service.ts b/src/services/enedisMonthlyAnalysisData.service.ts index 70af4e00a46a9e7a73bb58922b001a60d9ac797c..0eff9590f3fb55bb6819be5b1e2624a3aa36e0e6 100644 --- a/src/services/enedisMonthlyAnalysisData.service.ts +++ b/src/services/enedisMonthlyAnalysisData.service.ts @@ -49,6 +49,7 @@ export default class EnedisMonthlyAnalysisDataService { > { const query: QueryDefinition = Q(ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE) .where({}) + .indexFields(['year', 'month']) .sortBy([{ year: 'desc' }, { month: 'desc' }]) .limitBy(1) const data = await this._client.query(query) @@ -115,6 +116,7 @@ export default class EnedisMonthlyAnalysisDataService { ): Promise<EnedisMonthlyAnalysisData[]> { const query: QueryDefinition = Q(ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE) .where({ year: year, month: month }) + .indexFields(['year', 'month']) .sortBy([{ year: 'desc' }, { month: 'desc' }]) .limitBy(1) const data = await this._client.query(query) @@ -161,6 +163,7 @@ export default class EnedisMonthlyAnalysisDataService { ): Promise<MaxPowerEntity[]> { const query: QueryDefinition = Q(ENEDIS_MAXPOWER_DOCTYPE) .where({ year: year, month: month }) + .indexFields(['year', 'month']) .sortBy([{ year: 'desc' }, { month: 'desc' }]) const data = await this._client.query(query) diff --git a/src/services/fluidsPrices.service.ts b/src/services/fluidsPrices.service.ts index acbaf74193adc63ab60866e89986231ab7a5dd81..7ea8c8fb3e3e072a5d7e5ae21de0a2c5ca4f47c9 100644 --- a/src/services/fluidsPrices.service.ts +++ b/src/services/fluidsPrices.service.ts @@ -40,13 +40,13 @@ export default class FluidPricesService { date: DateTime ): Promise<FluidPrice> { const query: QueryDefinition = Q(FLUIDSPRICES_DOCTYPE) - .indexFields(['startDate']) .where({ startDate: { $lte: date.toISO({ suppressMilliseconds: true }).toString(), }, fluidType, }) + .indexFields(['startDate']) .sortBy([{ startDate: 'desc' }]) .limitBy(1) @@ -61,8 +61,8 @@ export default class FluidPricesService { */ public async getAllLastPrices(): Promise<FluidPrice[]> { const query: QueryDefinition = Q(FLUIDSPRICES_DOCTYPE) - .indexFields(['fluidType']) .where({ endDate: { $eq: '' } }) + .indexFields(['fluidType']) .sortBy([{ fluidType: 'asc' }]) .limitBy(3) diff --git a/src/services/profileType.service.ts b/src/services/profileType.service.ts index f911b87f894a6ae38c19496dff57d6ab13bd4ef1..2255192a8c05659608928105a284886e8dd118a7 100644 --- a/src/services/profileType.service.ts +++ b/src/services/profileType.service.ts @@ -6,6 +6,7 @@ import elecSpeData from 'constants/consumptionConstants/electricSpecific.json' import heatingData from 'constants/consumptionConstants/heating.json' import { Client } from 'cozy-client' import logger from 'cozy-logger' +import { REMOTE_ORG_ECOLYO_DJU } from 'doctypes/remote/org.ecolyo.dju' import { FluidType } from 'enum/fluid.enum' import { ConstructionYear, @@ -18,6 +19,7 @@ import { ThreeChoicesAnswer, } from 'enum/profileType.enum' import { DateTime } from 'luxon' +import { DjuResult } from 'models/dju.model' import { DetailsMonthlyForecast, FluidForecast, @@ -496,7 +498,6 @@ export default class ProfileTypeService { .set({ year: this.year }) .set({ month: month }) .startOf('month') - .minus({ minutes: 5 }) .toISO() const endDate: string = DateTime.local() .setZone('utc', { @@ -505,21 +506,22 @@ export default class ProfileTypeService { .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 + const result: DjuResult = await this._client .getStackClient() .fetchJSON( 'GET', - `/remote/org.ecolyo.dju?observedProperty=degreeDay&startDate=${startDate}&endDate=${endDate}` + `${REMOTE_ORG_ECOLYO_DJU}?startDate=${startDate}&endDate=${endDate}` ) let monthDju = 0 if (result) { - for (const observation of result.observations) { - monthDju += observation.result.value + const degreeDayObservations = result.values.filter( + value => value.observation === 'degreeDay' + ) + for (const observation of degreeDayObservations) { + monthDju += observation.measurement } } if (monthDju === 0) { diff --git a/src/services/profileTypeEntity.service.ts b/src/services/profileTypeEntity.service.ts index b436858f0d00fa5bb5633b008c238baf84c0884d..bb68839a13cdca9313f5d79a101531ce07037132 100644 --- a/src/services/profileTypeEntity.service.ts +++ b/src/services/profileTypeEntity.service.ts @@ -30,6 +30,7 @@ export default class ProfileTypeEntityService { }: QueryResult<ProfileType[]> = await this._client.query( query .where({ updateDate: { $lte: date.toString() } }) + .indexFields(['updateDate']) .sortBy([{ updateDate: 'desc' }]) .limitBy(1) ) @@ -65,7 +66,8 @@ export default class ProfileTypeEntityService { data: [profileType], }: QueryResult<ProfileType[]> = await this._client.query( query - .where({ _id: { $gt: null } }) + .where({}) + .indexFields(['updateDate']) .sortBy([{ updateDate: 'desc' }]) .limitBy(1) ) @@ -96,6 +98,7 @@ export default class ProfileTypeEntityService { $gte: timePeriod.startDate.toString(), }, }) + .indexFields(['updateDate']) .sortBy([{ updateDate: 'asc' }]) .limitBy(100) ) diff --git a/src/services/queryRunner.service.ts b/src/services/queryRunner.service.ts index 6fa8128692d7b81e1c8f31e468c83f3a575140e3..dffb788de165216d9d5890ef1e6a3f7c239e5c93 100644 --- a/src/services/queryRunner.service.ts +++ b/src/services/queryRunner.service.ts @@ -58,14 +58,14 @@ export default class QueryRunner { return Q(doctype) .where(this.getPredicate(maxTimePeriod, TimeStep.HALF_AN_HOUR)) .indexFields(['load']) - .limitBy(1) .sortBy([{ load: 'desc' }]) + .limitBy(1) } return Q(doctype) .where(this.getPredicate(maxTimePeriod, timeStep)) .indexFields(['load']) - .limitBy(limit) .sortBy([{ load: 'desc' }]) + .limitBy(limit) } private buildFirstDateQuery( @@ -472,7 +472,7 @@ export default class QueryRunner { public async getEntries(fluidType: FluidType, timeStep: TimeStep) { const doctype = this.getRelevantDoctype(fluidType, timeStep) try { - const query = Q(doctype).where({}).limitBy(1) + const query = Q(doctype).limitBy(1) const result = await this._client.query(query) return result } catch (error) { diff --git a/src/services/terms.service.ts b/src/services/terms.service.ts index bb063f147f4557ab2e27a7d1b1a1cb083086472f..c9c3061a95372e312e8dffa58493fd61164a002c 100644 --- a/src/services/terms.service.ts +++ b/src/services/terms.service.ts @@ -21,6 +21,7 @@ export default class TermsService { public async getLastTerm(): Promise<Term> { const query: QueryDefinition = Q(TERMS_DOCTYPE) .where({}) + .indexFields(['acceptedAt']) .sortBy([{ acceptedAt: 'desc' }]) .limitBy(1) const { @@ -36,6 +37,7 @@ export default class TermsService { public async isLastTermValidated(): Promise<boolean> { const query: QueryDefinition = Q(TERMS_DOCTYPE) .where({}) + .indexFields(['acceptedAt']) .sortBy([{ acceptedAt: 'desc' }]) .limitBy(1) const { diff --git a/src/services/usageEvent.service.ts b/src/services/usageEvent.service.ts index 9b18e59c4e342fd543fb8410e12019c12a244779..33fb71ea2b15ace740910d1d21852b30ad64f971 100644 --- a/src/services/usageEvent.service.ts +++ b/src/services/usageEvent.service.ts @@ -80,10 +80,13 @@ export default class UsageEventService { // Get last Connection attempt Event const query: QueryDefinition = Q(USAGEEVENT_DOCTYPE) .where({ - type: UsageEventType.KONNECTOR_ATTEMPT_EVENT, target: konnectorSlug, + }) + .partialIndex({ + type: UsageEventType.KONNECTOR_ATTEMPT_EVENT, result: 'error', }) + .indexFields(['eventDate', 'type']) .sortBy([{ eventDate: 'desc' }]) .limitBy(1) const { diff --git a/src/targets/browser/index.tsx b/src/targets/browser/index.tsx index 7322f9628adee0b245b903712706e538ed892b99..d8800d9b42c840aacf87c62f71dbf5cc28899e77 100644 --- a/src/targets/browser/index.tsx +++ b/src/targets/browser/index.tsx @@ -8,6 +8,7 @@ declare let Piwik: any import * as Sentry from '@sentry/react' import { BrowserTracing } from '@sentry/tracing' import CozyClient, { Client, CozyProvider } from 'cozy-client' +import { isFlagshipApp } from 'cozy-device-helper' import { handleOAuthResponse } from 'cozy-harvest-lib/dist/helpers/oauth' import { WebviewIntentProvider } from 'cozy-intent' import { I18n, initTranslation } from 'cozy-ui/transpiled/react/I18n' @@ -64,7 +65,7 @@ const setupApp = memoize(() => { replaceTitleOnMobile: false, appSlug: data.app.slug, appNamePrefix: data.app.prefix, - isInvertedTheme: true, + isInvertedTheme: isFlagshipApp(), }) let tracker: undefined | MatomoTracker diff --git a/src/utils/utils.spec.ts b/src/utils/utils.spec.ts index 1394922982da09dd5ce6d269bdbaf314908e8ac6..b2b98f20c8e63424ddddcb015b57f8932854142d 100644 --- a/src/utils/utils.spec.ts +++ b/src/utils/utils.spec.ts @@ -1,14 +1,20 @@ import { Season } from 'enum/ecogesture.enum' -import { FluidType } from 'enum/fluid.enum' +import { FluidState, FluidType } from 'enum/fluid.enum' import { FluidSlugType } from 'enum/fluidSlug.enum' +import { KonnectorUpdate } from 'enum/konnectorUpdate.enum' import { DateTime } from 'luxon' +import { FluidStatus } from 'models' import { formatNumberValues, getChallengeTitleWithLineReturn, getFluidType, getKonnectorSlug, + getKonnectorUpdateError, + getMonthFullName, + getMonthName, getMonthNameWithPrep, getSeason, + isKonnectorActive, } from './utils' describe('utils test', () => { @@ -42,6 +48,80 @@ describe('utils test', () => { expect(slug).toBe(FluidSlugType.GAS) }) }) + describe('getKonnectorUpdateError', () => { + it('should return KonnectorUpdate.ERROR_UPDATE_OAUTH for USER_ACTION_NEEDED.OAUTH_OUTDATED', () => { + const result = getKonnectorUpdateError( + 'USER_ACTION_NEEDED.OAUTH_OUTDATED' + ) + expect(result).toBe(KonnectorUpdate.ERROR_UPDATE_OAUTH) + }) + it('should return KonnectorUpdate.LOGIN_FAILED for LOGIN_FAILED', () => { + const result = getKonnectorUpdateError('LOGIN_FAILED') + expect(result).toBe(KonnectorUpdate.LOGIN_FAILED) + }) + it('should return KonnectorUpdate.ERROR_CONSENT_FORM_GAS for CHALLENGE_ASKED', () => { + const result = getKonnectorUpdateError('CHALLENGE_ASKED') + expect(result).toBe(KonnectorUpdate.ERROR_CONSENT_FORM_GAS) + }) + it('should return KonnectorUpdate.ERROR_UPDATE for an unknown type', () => { + const result = getKonnectorUpdateError('UNKNOWN_TYPE') + expect(result).toBe(KonnectorUpdate.ERROR_UPDATE) + }) + }) + + describe('isKonnectorActive', () => { + describe('for MULTIFLUID', () => { + it('should return false when all fluids are not connected', () => { + const fluidStatus = [ + { status: FluidState.NOT_CONNECTED }, + { status: FluidState.KONNECTOR_NOT_FOUND }, + { status: FluidState.NOT_CONNECTED }, + ] as FluidStatus[] + const result = isKonnectorActive(fluidStatus, FluidType.MULTIFLUID) + expect(result).toBe(false) + }) + it('should return true when some fluids are connected', () => { + const fluidStatus = [ + { status: FluidState.DONE }, + { status: FluidState.NOT_CONNECTED }, + { status: FluidState.DONE }, + ] as FluidStatus[] + const result = isKonnectorActive(fluidStatus, FluidType.MULTIFLUID) + expect(result).toBe(true) + }) + }) + + describe('for SINGLE fluids', () => { + it('should return false for a single fluid not connected', () => { + const fluidStatus = [ + {}, + { + status: FluidState.NOT_CONNECTED, + fluidType: FluidType.GAS, + }, + { + status: FluidState.KONNECTOR_NOT_FOUND, + fluidType: FluidType.WATER, + }, + ] as FluidStatus[] + expect(isKonnectorActive(fluidStatus, FluidType.GAS)).toBe(false) + expect(isKonnectorActive(fluidStatus, FluidType.WATER)).toBe(false) + }) + it('should return true for a single fluid connected', () => { + const fluidStatus = [ + { + status: FluidState.DONE, + fluidType: FluidType.ELECTRICITY, + }, + { status: FluidState.ERROR, fluidType: FluidType.WATER }, + { status: FluidState.ERROR_LOGIN_FAILED, fluidType: FluidType.GAS }, + ] as FluidStatus[] + expect(isKonnectorActive(fluidStatus, FluidType.ELECTRICITY)).toBe(true) + expect(isKonnectorActive(fluidStatus, FluidType.GAS)).toBe(true) + expect(isKonnectorActive(fluidStatus, FluidType.WATER)).toBe(true) + }) + }) + }) describe('formatNumberValues test', () => { it('should return --,-- if there is not value', () => { @@ -105,6 +185,18 @@ describe('utils test', () => { expect(getChallengeTitleWithLineReturn('CHALLENGE0000')).toBe(undefined) }) }) + + describe('getMonthFullName', () => { + it('should return the name of the month', () => { + expect(getMonthFullName(3)).toBe('Mars') + }) + }) + + describe('getMonthFullName', () => { + it('should return the name of the month', () => { + expect(getMonthName(DateTime.local(2023, 6, 1))).toBe('juin') + }) + }) describe('getMonthNameWithPrep', () => { it('should return the name of the month with " de " ', () => { const date = DateTime.fromISO('2020-11-29T23:59:59.999Z')