@open3cl/engine 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/10_besoin_fr.js +76 -0
  2. package/10_clim.js +45 -0
  3. package/11_besoin_ecs.js +25 -0
  4. package/11_ecs.js +95 -0
  5. package/11_nadeq.js +87 -0
  6. package/11_nadeq.spec.js +55 -0
  7. package/12.4_pac.js +54 -0
  8. package/13.2_generateur_combustion.js +295 -0
  9. package/13.2_generateur_combustion_bouilleur.js +173 -0
  10. package/13.2_generateur_combustion_ch.js +195 -0
  11. package/13.2_generateur_combustion_chaudiere.js +151 -0
  12. package/13.2_generateur_pac.js +36 -0
  13. package/13_rendement_distribution_ecs.js +12 -0
  14. package/14_generateur_ecs.js +388 -0
  15. package/15_conso_aux.js +257 -0
  16. package/16.2_production_enr.js +328 -0
  17. package/16.2_production_enr.spec.js +251 -0
  18. package/16_conso_eclairage.js +37 -0
  19. package/2021_04_13_confort_ete.js +61 -0
  20. package/2021_04_13_qualite_isolation.js +174 -0
  21. package/3.1_b.js +141 -0
  22. package/3.2.1_mur.js +331 -0
  23. package/3.2.1_mur.spec.js +46 -0
  24. package/3.2.2_plancher_bas.js +259 -0
  25. package/3.2.2_plancher_bas.spec.js +88 -0
  26. package/3.2.3_plancher_haut.js +158 -0
  27. package/3.3.1.4_porte.js +32 -0
  28. package/3.3_baie_vitree.js +308 -0
  29. package/3.3_baie_vitree.spec.js +333 -0
  30. package/3.4_pont_thermique.js +463 -0
  31. package/3_deperdition.js +258 -0
  32. package/4_ventilation.js +197 -0
  33. package/5_conso_ventilation.js +127 -0
  34. package/6.1_apport_gratuit.js +61 -0
  35. package/6.1_apport_gratuit.spec.js +181 -0
  36. package/6.2_surface_sud_equivalente.js +109 -0
  37. package/7_inertie.js +178 -0
  38. package/7_inertie.spec.js +263 -0
  39. package/8_intermittence.js +5 -0
  40. package/9_besoin_ch.js +198 -0
  41. package/9_chauffage.js +291 -0
  42. package/9_chauffage.spec.js +101 -0
  43. package/9_conso_ch.js +95 -0
  44. package/9_conso_ch.spec.js +255 -0
  45. package/9_emetteur_ch.js +122 -0
  46. package/9_generateur_ch.js +230 -0
  47. package/9_generateur_ch.spec.js +87 -0
  48. package/README.md +43 -0
  49. package/apport_et_besoin.js +55 -0
  50. package/conso.js +529 -0
  51. package/conso.spec.js +90 -0
  52. package/core/assets/domain/add-additionnal-ue-values-tables.js +57 -0
  53. package/core/assets/domain/synchronize-assets.js +29 -0
  54. package/core/assets/domain/synchronize-assets.spec.js +37 -0
  55. package/core/assets/domain/synchronize-c1-tables.js +61 -0
  56. package/core/assets/domain/synchronize-c1-tables.spec.js +35 -0
  57. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.js +73 -0
  58. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +72 -0
  59. package/core/assets/domain/synchronize-enum-tables.js +77 -0
  60. package/core/assets/domain/synchronize-enum-tables.spec.js +31 -0
  61. package/core/assets/domain/synchronize-solicitations-tables.js +72 -0
  62. package/core/assets/domain/synchronize-solicitations-tables.spec.js +47 -0
  63. package/core/assets/domain/synchronize-valeur-tables.js +146 -0
  64. package/core/assets/domain/synchronize-valeur-tables.spec.js +54 -0
  65. package/core/conf/infrastructure/application.config.js +33 -0
  66. package/core/file/infrastructure/adapter/file.store.js +75 -0
  67. package/core/file/infrastructure/adapter/file.store.spec.js +30 -0
  68. package/core/tv/infrastructure/assets/additional-ue-values.js +69 -0
  69. package/core/tv/infrastructure/tvs.store.js +40 -0
  70. package/core/tv/infrastructure/tvs.store.spec.js +34 -0
  71. package/core/util/infrastructure/object-util.js +23 -0
  72. package/core/util/infrastructure/object-util.spec.js +25 -0
  73. package/engine.js +503 -0
  74. package/enums.js +1155 -0
  75. package/ficheTechnique.js +86 -0
  76. package/ficheTechnique.spec.js +181 -0
  77. package/index.js +4 -0
  78. package/package.json +87 -0
  79. package/tv/18.2_sollicitations_ext.ods +0 -0
  80. package/tv/18.5_c1.ods +0 -0
  81. package/tv/dpe_ges_limit_values.ods +0 -0
  82. package/tv.js +80811 -0
  83. package/tvs.d.ts +7 -0
  84. package/utils.js +500 -0
  85. package/utils.spec.js +36 -0
package/conso.spec.js ADDED
@@ -0,0 +1,90 @@
1
+ import calc_conso from './conso.js';
2
+
3
+ describe('Recherche de bugs dans le calcul de la consommation', () => {
4
+ test('calcul de la consommation de chauffage pour 2475E2510509B', () => {
5
+ const Sh = 22.76; // Surface habitable
6
+ const zc_id = 1; // Zone climatique
7
+ const ca_id = 1; // Classe d'altitude
8
+ const vt = [];
9
+ const ch = [
10
+ {
11
+ donnee_entree: {
12
+ description:
13
+ 'Convecteur électrique NFC, NF** et NF*** avec programmateur pièce par pièce (système individuel)',
14
+ reference: '2024_07_11_09_30_44_722898200737262',
15
+ surface_chauffee: 22.76,
16
+ rdim: 1,
17
+ nombre_niveau_installation_ch: 1,
18
+ enum_cfg_installation_ch_id: '1',
19
+ enum_type_installation_id: '1',
20
+ enum_methode_calcul_conso_id: '1'
21
+ },
22
+ emetteur_chauffage_collection: {
23
+ emetteur_chauffage: [
24
+ {
25
+ donnee_entree: {
26
+ description: '',
27
+ reference: 'Emetteur:2024_07_11_09_30_44_722898200737262#1',
28
+ surface_chauffee: 22.76,
29
+ tv_rendement_emission_id: 1,
30
+ tv_rendement_distribution_ch_id: 1,
31
+ tv_rendement_regulation_id: 1,
32
+ enum_type_emission_distribution_id: '1',
33
+ tv_intermittence_id: 138,
34
+ reseau_distribution_isole: 0,
35
+ enum_equipement_intermittence_id: '4',
36
+ enum_type_regulation_id: '2',
37
+ enum_periode_installation_emetteur_id: '1',
38
+ enum_type_chauffage_id: '1',
39
+ enum_temp_distribution_ch_id: '1',
40
+ enum_lien_generateur_emetteur_id: '1'
41
+ },
42
+ donnee_intermediaire: {
43
+ rendement_distribution: 1,
44
+ rendement_emission: 0.95,
45
+ rendement_regulation: 0.99,
46
+ i0: 0.86
47
+ }
48
+ }
49
+ ]
50
+ },
51
+ generateur_chauffage_collection: {
52
+ generateur_chauffage: [
53
+ {
54
+ donnee_entree: {
55
+ description: 'Electrique - Convecteur électrique NFC, NF** et NF***',
56
+ reference: 'Generateur:2024_07_11_09_30_44_722898200737262#1',
57
+ reference_generateur_mixte: '',
58
+ ref_produit_generateur_ch: 'Sans Objet',
59
+ enum_type_generateur_ch_id: '98',
60
+ enum_usage_generateur_id: '1',
61
+ enum_type_energie_id: '1',
62
+ position_volume_chauffe: 1,
63
+ tv_rendement_generation_id: 29,
64
+ identifiant_reseau_chaleur: '',
65
+ enum_methode_saisie_carac_sys_id: '1',
66
+ enum_lien_generateur_emetteur_id: '1'
67
+ },
68
+ donnee_intermediaire: {
69
+ rendement_generation: 1,
70
+ conso_ch: 6322.706407855126,
71
+ conso_ch_depensier: 7679.56883918009,
72
+ rg: 1,
73
+ rg_dep: 1,
74
+ conso_auxiliaire_generation_ch: 0,
75
+ conso_auxiliaire_generation_ch_depensier: 0
76
+ }
77
+ }
78
+ ]
79
+ }
80
+ }
81
+ ];
82
+ const ecs = [];
83
+ const fr = [];
84
+
85
+ const result = calc_conso(Sh, zc_id, ca_id, vt, ch, ecs, fr);
86
+
87
+ expect(result.ef_conso.conso_ch).toBe(6322.706407855126);
88
+ expect(result.ef_conso.conso_ch_depensier).toBe(7679.56883918009);
89
+ });
90
+ });
@@ -0,0 +1,57 @@
1
+ import { UPB_ADDITIONAL_VALUES } from '../../tv/infrastructure/assets/additional-ue-values.js';
2
+
3
+ /**
4
+ * Add additional values for ue
5
+ */
6
+ export class AddAdditionnalUeValuesTables {
7
+ /**
8
+ * @type {FileStore}
9
+ */
10
+ #fileStore;
11
+
12
+ /**
13
+ * @type {ApplicationConfig}
14
+ */
15
+ #appConfig;
16
+
17
+ /**
18
+ * @param fileStore {FileStore}
19
+ * @param appConfig {ApplicationConfig}
20
+ */
21
+ constructor(fileStore, appConfig) {
22
+ this.#fileStore = fileStore;
23
+ this.#appConfig = appConfig;
24
+ }
25
+
26
+ /**
27
+ * Ajout de valeurs supplémentaires pour le calcul du facteur Ue pour les déperditions plancher_bas
28
+ * @param tableValues
29
+ * @returns {*}
30
+ */
31
+ execute(tableValues) {
32
+ UPB_ADDITIONAL_VALUES.forEach(
33
+ ({
34
+ type_adjacence_plancher,
35
+ enum_type_adjacence_id,
36
+ type_adjacence,
37
+ enum_periode_construction_id,
38
+ periode_construction,
39
+ values
40
+ }) => {
41
+ const baseData = {
42
+ type_adjacence_plancher,
43
+ enum_type_adjacence_id,
44
+ type_adjacence,
45
+ enum_periode_construction_id,
46
+ periode_construction
47
+ };
48
+
49
+ values.forEach(({ ue, '2s_p': key2SP, upb }) => {
50
+ tableValues['ue'].push({ ...baseData, '2s_p': key2SP, upb, ue });
51
+ });
52
+ }
53
+ );
54
+
55
+ return tableValues;
56
+ }
57
+ }
@@ -0,0 +1,29 @@
1
+ export class SynchronizeAssets {
2
+ /**
3
+ * @type {SynchronizeEnumTables}
4
+ */
5
+ #synchronizeEnumTables;
6
+
7
+ /**
8
+ * @type {SynchronizeValeurTables}
9
+ */
10
+ #synchronizeValeurTables;
11
+
12
+ /**
13
+ * @param synchronizeEnumTables {SynchronizeEnumTables}
14
+ * @param synchronizeValeurTables {SynchronizeValeurTables}
15
+ */
16
+ constructor(synchronizeEnumTables, synchronizeValeurTables) {
17
+ this.#synchronizeEnumTables = synchronizeEnumTables;
18
+ this.#synchronizeValeurTables = synchronizeValeurTables;
19
+ }
20
+
21
+ execute() {
22
+ return Promise.all([
23
+ this.#synchronizeEnumTables.execute(),
24
+ this.#synchronizeValeurTables.execute()
25
+ ])
26
+ .then(() => console.log('enum and valeur tables are synchronized successfully'))
27
+ .catch((error) => console.error('Could not synchronize files', error));
28
+ }
29
+ }
@@ -0,0 +1,37 @@
1
+ import { SynchronizeAssets } from './synchronize-assets.js';
2
+ import { SynchronizeEnumTables } from './synchronize-enum-tables.js';
3
+ import { SynchronizeValeurTables } from './synchronize-valeur-tables.js';
4
+ import { jest } from '@jest/globals';
5
+
6
+ describe('SynchronizeAssets unit tests', () => {
7
+ it('should synchronize xlsx and ods files', () => {
8
+ const synchronizeEnumTables = new SynchronizeEnumTables(null, null);
9
+ const synchronizeValeurTables = new SynchronizeValeurTables(null, null, null);
10
+ const synchronizeAssets = new SynchronizeAssets(synchronizeEnumTables, synchronizeValeurTables);
11
+
12
+ jest.spyOn(console, 'log').mockReturnValue(null);
13
+ jest.spyOn(synchronizeEnumTables, 'execute').mockResolvedValue({});
14
+ jest.spyOn(synchronizeValeurTables, 'execute').mockResolvedValue({});
15
+
16
+ return synchronizeAssets.execute().then(() => {
17
+ expect(synchronizeEnumTables.execute).toHaveBeenCalled();
18
+ expect(synchronizeValeurTables.execute).toHaveBeenCalled();
19
+ expect(console.log).toHaveBeenCalled();
20
+ });
21
+ });
22
+
23
+ it('should log errors if synchronization has failed', () => {
24
+ const synchronizeEnumTables = new SynchronizeEnumTables(null, null);
25
+ const synchronizeValeurTables = new SynchronizeValeurTables(null, null, null);
26
+ const synchronizeAssets = new SynchronizeAssets(synchronizeEnumTables, synchronizeValeurTables);
27
+
28
+ jest.spyOn(console, 'error').mockReturnValue(null);
29
+ jest.spyOn(synchronizeEnumTables, 'execute').mockResolvedValue({});
30
+ jest.spyOn(synchronizeValeurTables, 'execute').mockRejectedValue(null);
31
+
32
+ return synchronizeAssets.execute().catch((error) => {
33
+ expect(error).toBeDefined();
34
+ expect(console.error).toHaveBeenCalled();
35
+ });
36
+ });
37
+ });
@@ -0,0 +1,61 @@
1
+ import { set } from 'lodash-es';
2
+
3
+ const ZONE_CLIMATIQUE_PROPERTY = 'zc';
4
+ const MOIS_PROPERTY = 'mois';
5
+ const EXCLUDED_PROPERTIES = [ZONE_CLIMATIQUE_PROPERTY, MOIS_PROPERTY];
6
+
7
+ /**
8
+ * Read the `18.5_c1.ods` local file
9
+ * - Convert the file into a json object
10
+ * - Extract, format data and return data
11
+ *
12
+ * The file content is merged in the `tv.js` file generated in the {@link SynchronizeValeurTables} use case.
13
+ */
14
+ export class SynchronizeC1Tables {
15
+ /**
16
+ * @type {FileStore}
17
+ */
18
+ #fileStore;
19
+
20
+ /**
21
+ * @type {ApplicationConfig}
22
+ */
23
+ #appConfig;
24
+
25
+ /**
26
+ * @param fileStore {FileStore}
27
+ * @param appConfig {ApplicationConfig}
28
+ */
29
+ constructor(fileStore, appConfig) {
30
+ this.#fileStore = fileStore;
31
+ this.#appConfig = appConfig;
32
+ }
33
+
34
+ /**
35
+ *
36
+ * @return {Promise<any>}
37
+ */
38
+ execute() {
39
+ return this.#fileStore.readLocalOdsFileAndConvertToJson(this.#appConfig.c1FilePath).then(
40
+ /** @param excelSheets {{[key: string]: {zc: string, mois: string}[]}} **/ (excelSheets) => {
41
+ const output = {};
42
+
43
+ // For each tab in Excel file
44
+ for (const sheetName in excelSheets) {
45
+ output[sheetName] = {};
46
+
47
+ excelSheets[sheetName].forEach((sheetValue) => {
48
+ for (const sheetValueKey in sheetValue) {
49
+ if (!EXCLUDED_PROPERTIES.includes(sheetValueKey)) {
50
+ const path = `${sheetName}.${sheetValue.zc}.${sheetValue.mois}.${sheetValueKey}`;
51
+ set(output, path, parseFloat(sheetValue[sheetValueKey]));
52
+ }
53
+ }
54
+ });
55
+ }
56
+
57
+ return output;
58
+ }
59
+ );
60
+ }
61
+ }
@@ -0,0 +1,35 @@
1
+ import { SynchronizeC1Tables } from './synchronize-c1-tables.js';
2
+ import { FileStore } from '../../file/infrastructure/adapter/file.store.js';
3
+ import { ApplicationConfig } from '../../conf/infrastructure/application.config.js';
4
+ import { C1TablesFixture } from '../../../../test/fixtures/core/assets/c1-tables.fixture.js';
5
+ import { jest } from '@jest/globals';
6
+
7
+ describe('SynchronizeC1Tables unit tests', () => {
8
+ it('should read and parse 18.5_c1.ods file', () => {
9
+ const fileStore = new FileStore();
10
+ const appConfig = new ApplicationConfig();
11
+ const synchronizeC1Tables = new SynchronizeC1Tables(fileStore, appConfig);
12
+
13
+ const c1Data = C1TablesFixture.aC1Example();
14
+ jest.spyOn(fileStore, 'readLocalOdsFileAndConvertToJson').mockResolvedValue(c1Data);
15
+ jest.spyOn(ApplicationConfig.prototype, 'c1FilePath', 'get').mockReturnValue('src/file.ods');
16
+
17
+ return synchronizeC1Tables.execute().then((output) => {
18
+ expect(fileStore.readLocalOdsFileAndConvertToJson).toHaveBeenCalled();
19
+ expect(output).toMatchObject({
20
+ c1: {
21
+ h1a: {
22
+ Janvier: {
23
+ 'sud sup75°': 1.0,
24
+ 'sud 25°-75°': 1.67
25
+ },
26
+ Février: {
27
+ 'sud sup75°': 1.0,
28
+ 'sud 25°-75°': 1.78
29
+ }
30
+ }
31
+ }
32
+ });
33
+ });
34
+ });
35
+ });
@@ -0,0 +1,73 @@
1
+ import { set } from 'lodash-es';
2
+
3
+ const CLASSE_ALTITUDE_PROPERTY = 'classe_altitude';
4
+ const SURFACE_PROPERTY = 'surface';
5
+ const EXCLUDED_PROPERTIES = [CLASSE_ALTITUDE_PROPERTY, SURFACE_PROPERTY];
6
+
7
+ /**
8
+ * Read the `dpe_ges_limit_values.ods` local file
9
+ * - Convert the file into a json object
10
+ * - Extract, format data and return data
11
+ *
12
+ * The file content is merged in the `tv.js` file generated in the {@link SynchronizeValeurTables} use case.
13
+ */
14
+ export class SynchronizeDpeGesLimitValuesTables {
15
+ /**
16
+ * @type {FileStore}
17
+ */
18
+ #fileStore;
19
+
20
+ /**
21
+ * @type {ApplicationConfig}
22
+ */
23
+ #appConfig;
24
+
25
+ /**
26
+ * @param fileStore {FileStore}
27
+ * @param appConfig {ApplicationConfig}
28
+ */
29
+ constructor(fileStore, appConfig) {
30
+ this.#fileStore = fileStore;
31
+ this.#appConfig = appConfig;
32
+ }
33
+
34
+ /**
35
+ * @return {Promise<any>}
36
+ */
37
+ execute() {
38
+ return this.#fileStore
39
+ .readLocalOdsFileAndConvertToJson(this.#appConfig.dpeGesLimitValuesFilePath)
40
+ .then(
41
+ /** @param excelSheets {{[key: string]: {classe_altitude: string, surface: string}[]}} **/ (
42
+ excelSheets
43
+ ) => {
44
+ const output = {};
45
+
46
+ // For each tab in Excel file
47
+ for (const sheetName in excelSheets) {
48
+ output[sheetName] = {};
49
+
50
+ excelSheets[sheetName].forEach((sheetValue) => {
51
+ // Group by "classe_altitude" an "surface"
52
+ let groupKey = sheetValue.classe_altitude;
53
+
54
+ if (!output[sheetName][groupKey]) {
55
+ output[sheetName][groupKey] = {};
56
+ }
57
+
58
+ output[sheetName][groupKey][parseFloat(sheetValue.surface)] = {};
59
+
60
+ for (const sheetValueKey in sheetValue) {
61
+ if (!EXCLUDED_PROPERTIES.includes(sheetValueKey)) {
62
+ const path = `${sheetName}.${groupKey}.${sheetValue.surface}.${sheetValueKey}`;
63
+ set(output, path, parseFloat(sheetValue[sheetValueKey]));
64
+ }
65
+ }
66
+ });
67
+ }
68
+
69
+ return output;
70
+ }
71
+ );
72
+ }
73
+ }
@@ -0,0 +1,72 @@
1
+ import { FileStore } from '../../file/infrastructure/adapter/file.store.js';
2
+ import { ApplicationConfig } from '../../conf/infrastructure/application.config.js';
3
+ import { DpeGesLimitValuesTablesFixture } from '../../../../test/fixtures/core/assets/dpe-ges-limit-values-tables.fixture.js';
4
+ import { SynchronizeDpeGesLimitValuesTables } from './synchronize-dpe-ges-limit-values-tables.js';
5
+ import { jest } from '@jest/globals';
6
+
7
+ describe('SynchronizeDpeGesLimitValuesTables unit tests', () => {
8
+ it('should read and parse dpe_ges_limit_values file', () => {
9
+ const fileStore = new FileStore();
10
+ const appConfig = new ApplicationConfig();
11
+ const synchronizeDpeGesLimitValuesTables = new SynchronizeDpeGesLimitValuesTables(
12
+ fileStore,
13
+ appConfig
14
+ );
15
+
16
+ const dpeGesLimitData = DpeGesLimitValuesTablesFixture.aDpeGesLimitExample();
17
+ jest.spyOn(fileStore, 'readLocalOdsFileAndConvertToJson').mockResolvedValue(dpeGesLimitData);
18
+ jest
19
+ .spyOn(ApplicationConfig.prototype, 'solicitationsExtFilePath', 'get')
20
+ .mockReturnValue('src/file.ods');
21
+
22
+ return synchronizeDpeGesLimitValuesTables.execute().then((output) => {
23
+ expect(fileStore.readLocalOdsFileAndConvertToJson).toHaveBeenCalled();
24
+ expect(output).toStrictEqual({
25
+ dpe_class_limit: {
26
+ 'inférieur à 400m': {
27
+ 3: {
28
+ A: 146,
29
+ B: 186,
30
+ C: 386,
31
+ D: 505,
32
+ E: 622,
33
+ F: 739
34
+ }
35
+ },
36
+ '400-800m': {
37
+ 10: {
38
+ A: 124,
39
+ B: 164,
40
+ C: 329,
41
+ D: 428,
42
+ E: 533,
43
+ F: 640
44
+ }
45
+ }
46
+ },
47
+ ges_class_limit: {
48
+ '400-800m': {
49
+ 10: {
50
+ A: 10,
51
+ B: 15,
52
+ C: 40,
53
+ D: 62,
54
+ E: 84,
55
+ F: 115
56
+ }
57
+ },
58
+ 'inférieur à 400m': {
59
+ 3: {
60
+ A: 11,
61
+ B: 16,
62
+ C: 44,
63
+ D: 68,
64
+ E: 90,
65
+ F: 122
66
+ }
67
+ }
68
+ }
69
+ });
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Download the `enum_tables.xlsx` file from the official ademe repository and generates a new `enums.js` file
3
+ * Please see @link https://gitlab.com/observatoire-dpe/observatoire-dpe/-/tree/master
4
+ * - Convert the file into a json object
5
+ * - Extract, format data and then generate a new enums.js file
6
+ */
7
+ export class SynchronizeEnumTables {
8
+ /**
9
+ * @type {FileStore}
10
+ */
11
+ #fileStore;
12
+
13
+ /**
14
+ * @type {ApplicationConfig}
15
+ */
16
+ #appConfig;
17
+
18
+ /**
19
+ * @param fileStore {FileStore}
20
+ * @param appConfig {ApplicationConfig}
21
+ */
22
+ constructor(fileStore, appConfig) {
23
+ this.#fileStore = fileStore;
24
+ this.#appConfig = appConfig;
25
+ }
26
+
27
+ /**
28
+ * @return {Promise<void>}
29
+ */
30
+ execute() {
31
+ return this.#fileStore
32
+ .downloadXlsxFileAndConvertToJson(this.#appConfig.ademeEnumTablesFileUrl)
33
+ .then(
34
+ /** @param excelSheets {{[tabName: string]: {id: string, lib: string}[]}} xlsx content file grouped by tab */
35
+ (excelSheets) => {
36
+ const enumsOutput = {};
37
+
38
+ // for each tab in xlsx file
39
+ Object.keys(excelSheets)
40
+ .filter((sheetName) => sheetName !== 'index') // Exclude first tab called "index"
41
+ .forEach((sheetName) => {
42
+ /**
43
+ * Each Excel sheet has a list of columns
44
+ * Keep only the two first columns for now for compatibility with the legacy `enums.js` file
45
+ * @type {{id: string, lib: string}[]}
46
+ */
47
+ const excelSheetValues = excelSheets[sheetName];
48
+
49
+ const outputTabValues = {};
50
+
51
+ /**
52
+ * Convert object in the form of [{id: 1, lib: "test"}, {id: 2, lib: "test 2"}]
53
+ * to {1: "test", 2: "test 2"}
54
+ */
55
+ excelSheetValues.forEach((excelSheetValue) => {
56
+ outputTabValues[excelSheetValue.id] = excelSheetValue.lib.replace(/ :/g, ' :');
57
+
58
+ // For compatibility with the legacy `enums.js` file, force the lowercase for
59
+ // each value except the `classe_etiquette`
60
+ if (sheetName !== 'classe_etiquette') {
61
+ outputTabValues[excelSheetValue.id] =
62
+ outputTabValues[excelSheetValue.id].toLowerCase();
63
+ }
64
+ });
65
+
66
+ enumsOutput[sheetName] = outputTabValues;
67
+ });
68
+
69
+ // Overwrite the enums.js file in filesystem
70
+ return this.#fileStore.writeFileToLocalSystem(
71
+ `${this.#appConfig.assetsOutputFolder}/enums.js`,
72
+ `/** @type {TableEnum} **/\nconst enums = ${JSON.stringify(enumsOutput, null, 2)};\n export default enums;`
73
+ );
74
+ }
75
+ );
76
+ }
77
+ }
@@ -0,0 +1,31 @@
1
+ import { FileStore } from '../../file/infrastructure/adapter/file.store.js';
2
+ import { ApplicationConfig } from '../../conf/infrastructure/application.config.js';
3
+ import { EnumTablesFixture } from '../../../../test/fixtures/core/assets/enum-tables.fixture.js';
4
+ import { SynchronizeEnumTables } from './synchronize-enum-tables.js';
5
+ import { jest } from '@jest/globals';
6
+
7
+ describe('SynchronizeEnumTables', () => {
8
+ it('should download, parse and convert enum_tables.xlsx file', () => {
9
+ const fileStore = new FileStore();
10
+ const appConfig = new ApplicationConfig();
11
+ const synchronizeEnumTables = new SynchronizeEnumTables(fileStore, appConfig);
12
+
13
+ const enumTablesData = EnumTablesFixture.anEnumTableExample();
14
+ jest
15
+ .spyOn(ApplicationConfig.prototype, 'ademeEnumTablesFileUrl', 'get')
16
+ .mockReturnValue('http://localhost/file.xlsx');
17
+ jest
18
+ .spyOn(ApplicationConfig.prototype, 'assetsOutputFolder', 'get')
19
+ .mockReturnValue('src/assets');
20
+ jest.spyOn(fileStore, 'downloadXlsxFileAndConvertToJson').mockResolvedValue(enumTablesData);
21
+ jest.spyOn(fileStore, 'writeFileToLocalSystem').mockResolvedValue(null);
22
+
23
+ return synchronizeEnumTables.execute().then(() => {
24
+ expect(fileStore.downloadXlsxFileAndConvertToJson).toHaveBeenCalled();
25
+ expect(fileStore.writeFileToLocalSystem).toHaveBeenCalledWith(
26
+ 'src/assets/enums.js',
27
+ expect.any(String)
28
+ );
29
+ });
30
+ });
31
+ });
@@ -0,0 +1,72 @@
1
+ import { set } from 'lodash-es';
2
+
3
+ const ILPA_PROPERTY = 'ilpa';
4
+ const CLASSE_ALTITUDE_PROPERTY = 'classe_altitude';
5
+ const MOIS_PROPERTY = 'mois';
6
+ const EXCLUDED_PROPERTIES = [ILPA_PROPERTY, CLASSE_ALTITUDE_PROPERTY, MOIS_PROPERTY];
7
+
8
+ /**
9
+ * Read the `18.2_sollicitations_ext.ods` local file
10
+ * - Convert the file into a json object
11
+ * - Extract, format data and return data
12
+ *
13
+ * The file content is merged in the `tv.js` file generated in the {@link SynchronizeValeurTables} use case.
14
+ */
15
+ export class SynchronizeSolicitationsTables {
16
+ /**
17
+ * @type {FileStore}
18
+ */
19
+ #fileStore;
20
+
21
+ /**
22
+ * @type {ApplicationConfig}
23
+ */
24
+ #appConfig;
25
+
26
+ /**
27
+ * @param fileStore {FileStore}
28
+ * @param appConfig {ApplicationConfig}
29
+ */
30
+ constructor(fileStore, appConfig) {
31
+ this.#fileStore = fileStore;
32
+ this.#appConfig = appConfig;
33
+ }
34
+
35
+ /**
36
+ * @return {Promise<any>}
37
+ */
38
+ execute() {
39
+ return this.#fileStore
40
+ .readLocalOdsFileAndConvertToJson(this.#appConfig.solicitationsExtFilePath)
41
+ .then(
42
+ /** @param excelSheets {{[key: string]: {ilpa?: number, classe_altitude: string, mois: string}[]}} **/ (
43
+ excelSheets
44
+ ) => {
45
+ const output = {};
46
+
47
+ // For each tab in Excel file
48
+ for (const sheetName in excelSheets) {
49
+ output[sheetName] = {};
50
+
51
+ excelSheets[sheetName].forEach((sheetValue) => {
52
+ // Group by "ilpa" property if it exists or "classe_altitude" otherwise
53
+ let groupKey = sheetValue.hasOwnProperty(ILPA_PROPERTY)
54
+ ? '.' + sheetValue.ilpa
55
+ : sheetValue.hasOwnProperty(CLASSE_ALTITUDE_PROPERTY)
56
+ ? '.' + sheetValue.classe_altitude
57
+ : '';
58
+
59
+ for (const sheetValueKey in sheetValue) {
60
+ if (!EXCLUDED_PROPERTIES.includes(sheetValueKey)) {
61
+ const path = `${sheetName}${groupKey}${sheetValue.hasOwnProperty(ILPA_PROPERTY) ? '.' + sheetValue.classe_altitude : ''}.${sheetValue.mois}.${sheetValueKey}`;
62
+ set(output, path, parseFloat(sheetValue[sheetValueKey]));
63
+ }
64
+ }
65
+ });
66
+ }
67
+
68
+ return output;
69
+ }
70
+ );
71
+ }
72
+ }
@@ -0,0 +1,47 @@
1
+ import { FileStore } from '../../file/infrastructure/adapter/file.store.js';
2
+ import { ApplicationConfig } from '../../conf/infrastructure/application.config.js';
3
+ import { SynchronizeSolicitationsTables } from './synchronize-solicitations-tables.js';
4
+ import { SolicitationsTablesFixture } from '../../../../test/fixtures/core/assets/solicitations-tables.fixture.js';
5
+ import { jest } from '@jest/globals';
6
+
7
+ describe('SynchronizeSolicitationsTables unit tests', () => {
8
+ it('should read and parse 18.2_sollicitations_ext.ods file', () => {
9
+ const fileStore = new FileStore();
10
+ const appConfig = new ApplicationConfig();
11
+ const synchronizeSolicitationsTables = new SynchronizeSolicitationsTables(fileStore, appConfig);
12
+
13
+ const solicitationsData = SolicitationsTablesFixture.aSolicitationExample();
14
+ jest.spyOn(fileStore, 'readLocalOdsFileAndConvertToJson').mockResolvedValue(solicitationsData);
15
+ jest
16
+ .spyOn(ApplicationConfig.prototype, 'solicitationsExtFilePath', 'get')
17
+ .mockReturnValue('src/file.ods');
18
+
19
+ return synchronizeSolicitationsTables.execute().then((output) => {
20
+ expect(fileStore.readLocalOdsFileAndConvertToJson).toHaveBeenCalled();
21
+ expect(output).toMatchObject({
22
+ e: {
23
+ 0: {
24
+ 'inférieur à 400m': {
25
+ Janvier: {
26
+ h1a: 38.36
27
+ },
28
+ Février: {
29
+ h1a: 37.47
30
+ }
31
+ }
32
+ }
33
+ },
34
+ e_fr_26: {
35
+ 'inférieur à 400m': {
36
+ Janvier: {
37
+ h1a: 0
38
+ },
39
+ Février: {
40
+ h1a: 0
41
+ }
42
+ }
43
+ }
44
+ });
45
+ });
46
+ });
47
+ });