@open3cl/engine 1.4.0 → 1.4.1
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.
- package/README.md +44 -20
- package/dpe-sanitizer.service.js +90 -0
- package/engine.js +23 -5
- package/index.js +8 -2
- package/package.json +1 -1
- package/utils.js +33 -33
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ C'est un bon moyen de détecter un éventuel problème dans le dpe ou la librair
|
|
|
85
85
|
```javascript
|
|
86
86
|
import { calcul_3cl } from 'open3cl';
|
|
87
87
|
|
|
88
|
-
// Exemple d'objet JSON issu d'un fichier XML DPE
|
|
88
|
+
// Exemple d'objet JSON (partiel) issu d'un fichier XML DPE
|
|
89
89
|
const dpeData = {
|
|
90
90
|
numero_dpe: '2113E1018248X',
|
|
91
91
|
statut: 'ACTIF',
|
|
@@ -108,7 +108,23 @@ const dpeData = {
|
|
|
108
108
|
}
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
+
// Execution d'un dpe avec la librairie Open3CL avec pré-transformation / nettoyage du dpe (comportement par défaut)
|
|
111
112
|
const result = calcul_3cl(dpeData);
|
|
113
|
+
const result = calcul_3cl(dpeData, { sanitize: true });
|
|
114
|
+
|
|
115
|
+
// Execution d'un dpe avec la librairie Open3CL sans pré-transformation / nettoyage du dpe
|
|
116
|
+
const result = calcul_3cl(dpeData, { sanitize: false });
|
|
117
|
+
|
|
118
|
+
// Execution d'un dpe au format xml avec la librairie Open3CL avec pré-transformation / nettoyage du dpe (comportement par défaut)
|
|
119
|
+
const result = calcul_3cl_xml('<xml><dpe><numero_dpe>2113E1018248X</numero_dpe></dpe</xml>');
|
|
120
|
+
const result = calcul_3cl_xml('<xml><dpe><numero_dpe>2113E1018248X</numero_dpe></dpe</xml>', {
|
|
121
|
+
sanitize: true
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Execution d'un dpe au format xml avec la librairie Open3CL sans pré-transformation / nettoyage du dpe (comportement par défaut)
|
|
125
|
+
const result = calcul_3cl_xml('<xml><dpe><numero_dpe>2113E1018248X</numero_dpe></dpe</xml>', {
|
|
126
|
+
sanitize: false
|
|
127
|
+
});
|
|
112
128
|
```
|
|
113
129
|
|
|
114
130
|
## Variables d'environnements
|
|
@@ -252,27 +268,35 @@ Résultats des tests de corpus avec le mode de compatibilité activé.
|
|
|
252
268
|
| 1.3.20 | dpe_immeuble_chauffage_individuel.csv | 7106 | 71% | |
|
|
253
269
|
| 1.3.20 | dpe_immeuble_chauffage_collectif.csv | 6083 | 61% | |
|
|
254
270
|
| 1.3.20 | dpe_immeuble_chauffage_mixte.csv | 4751 | 47% | |
|
|
271
|
+
| <ins>**1.3.21**<ins> | <ins>**corpus_dpe.csv**<ins> | <ins>**4502**<ins> (+2) | <ins>**45%**<ins> | |
|
|
272
|
+
| 1.3.21 | dpe_logement_individuel_2025.csv | 8504 (+45) | 85% (+1%) | |
|
|
273
|
+
| 1.3.21 | dpe_maison_individuelle_2025.csv | 8652 (+14) | 86% | |
|
|
274
|
+
| 1.3.21 | dpe_appartement_individuel_chauffage_individuel_2025.csv | 8578 (+17) | 86% | |
|
|
275
|
+
| 1.3.21 | dpe_appartement_individuel_chauffage_collectif_2025.csv | 6681 | 67% | |
|
|
276
|
+
| 1.3.21 | dpe_immeuble_chauffage_individuel.csv | 7144 (+38) | 71% | |
|
|
277
|
+
| 1.3.21 | dpe_immeuble_chauffage_collectif.csv | 6144 (+61) | 61% | |
|
|
278
|
+
| 1.3.21 | dpe_immeuble_chauffage_mixte.csv | 4760 (+9) | 47% | |
|
|
255
279
|
|
|
256
280
|
</details>
|
|
257
281
|
|
|
258
|
-
| Version librairie | corpus | Nb en dessous du taux d'erreur | Taux de réussite | Description
|
|
259
|
-
| :------------------- | -------------------------------------------------------- | ------------------------------ | ----------------- |
|
|
260
|
-
| <ins>**1.3.
|
|
261
|
-
| 1.3.
|
|
262
|
-
| 1.3.
|
|
263
|
-
| 1.3.
|
|
264
|
-
| 1.3.
|
|
265
|
-
| 1.3.
|
|
266
|
-
| 1.3.
|
|
267
|
-
| 1.3.
|
|
268
|
-
| <ins>**1.
|
|
269
|
-
| 1.
|
|
270
|
-
| 1.
|
|
271
|
-
| 1.
|
|
272
|
-
| 1.
|
|
273
|
-
| 1.
|
|
274
|
-
| 1.
|
|
275
|
-
| 1.
|
|
282
|
+
| Version librairie | corpus | Nb en dessous du taux d'erreur | Taux de réussite | Description |
|
|
283
|
+
| :------------------- | -------------------------------------------------------- | ------------------------------ | ----------------- | ------------------------------------ |
|
|
284
|
+
| <ins>**1.3.25**<ins> | <ins>**corpus_dpe.csv**<ins> | <ins>**4515**<ins> (+2) | <ins>**45%**<ins> | |
|
|
285
|
+
| 1.3.25 | dpe_logement_individuel_2025.csv | 8512 (+8) | 85% | |
|
|
286
|
+
| 1.3.25 | dpe_maison_individuelle_2025.csv | 8674 (+22) | 87% (+1%) | |
|
|
287
|
+
| 1.3.25 | dpe_appartement_individuel_chauffage_individuel_2025.csv | 8581 (+3) | 86% | |
|
|
288
|
+
| 1.3.25 | dpe_appartement_individuel_chauffage_collectif_2025.csv | 6680 (-1) | 67% | Dpe erronés enlevés du corpus |
|
|
289
|
+
| 1.3.25 | dpe_immeuble_chauffage_individuel.csv | 7142 (-2) | 71% | Dpe erronés enlevés du corpus |
|
|
290
|
+
| 1.3.25 | dpe_immeuble_chauffage_collectif.csv | 6144 | 61% | |
|
|
291
|
+
| 1.3.25 | dpe_immeuble_chauffage_mixte.csv | 4758 (-2) | 47% | Dpe erronés enlevés du corpus |
|
|
292
|
+
| <ins>**1.4.0**<ins> | <ins>**corpus_dpe.csv**<ins> | <ins>**4515**<ins> | <ins>**45%**<ins> | Maj coeff electricité (janvier 2026) |
|
|
293
|
+
| 1.4.0 | dpe_logement_individuel_2025.csv | 8512 | 85% | Maj coeff electricité (janvier 2026) |
|
|
294
|
+
| 1.4.0 | dpe_maison_individuelle_2025.csv | 8674 | 87% | Maj coeff electricité (janvier 2026) |
|
|
295
|
+
| 1.4.0 | dpe_appartement_individuel_chauffage_individuel_2025.csv | 8581 | 86% | Maj coeff electricité (janvier 2026) |
|
|
296
|
+
| 1.4.0 | dpe_appartement_individuel_chauffage_collectif_2025.csv | 6680 | 67% | Maj coeff electricité (janvier 2026) |
|
|
297
|
+
| 1.4.0 | dpe_immeuble_chauffage_individuel.csv | 7142 | 71% | Maj coeff electricité (janvier 2026) |
|
|
298
|
+
| 1.4.0 | dpe_immeuble_chauffage_collectif.csv | 6144 | 61% | Maj coeff electricité (janvier 2026) |
|
|
299
|
+
| 1.4.0 | dpe_immeuble_chauffage_mixte.csv | 4758 | 47% | Maj coeff electricité (janvier 2026) |
|
|
276
300
|
|
|
277
301
|
## Roadmap
|
|
278
302
|
|
|
@@ -308,7 +332,7 @@ Nous accueillons les contributions avec plaisir ! Si vous souhaitez améliorer O
|
|
|
308
332
|
|
|
309
333
|
## Licence
|
|
310
334
|
|
|
311
|
-
Distribué sous la license `
|
|
335
|
+
Distribué sous la license `MIT`. Lire le fichier `LICENSE` pour plus d'informations.
|
|
312
336
|
|
|
313
337
|
<p align="right">(<a href="#readme-top">Retour sommaire</a>)</p>
|
|
314
338
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ObjectUtil } from './core/util/infrastructure/object-util.js';
|
|
2
|
+
|
|
3
|
+
const nodesToMap = [
|
|
4
|
+
'mur',
|
|
5
|
+
'plancher_bas',
|
|
6
|
+
'plancher_haut',
|
|
7
|
+
'baie_vitree',
|
|
8
|
+
'porte',
|
|
9
|
+
'pont_thermique',
|
|
10
|
+
'ventilation',
|
|
11
|
+
'installation_ecs',
|
|
12
|
+
'generateur_ecs',
|
|
13
|
+
'climatisation',
|
|
14
|
+
'installation_chauffage',
|
|
15
|
+
'generateur_chauffage',
|
|
16
|
+
'emetteur_chauffage',
|
|
17
|
+
'sortie_par_energie'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Transform single nodes in {@link nodesToMap} into array of nodes.
|
|
22
|
+
* Transform string number into digits
|
|
23
|
+
* These transformations should be done inside the open3cl library
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Will transform
|
|
27
|
+
* "plancher_haut_collection": {
|
|
28
|
+
* "plancher_haut": {"id": 1}
|
|
29
|
+
* }
|
|
30
|
+
* // Into
|
|
31
|
+
* "plancher_haut_collection": {
|
|
32
|
+
* "plancher_haut": [{"id": 1}]
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Will transform
|
|
37
|
+
* "surface_paroi_opaque": "40.94"
|
|
38
|
+
* // Into
|
|
39
|
+
* "surface_paroi_opaque": 40.94
|
|
40
|
+
*/
|
|
41
|
+
export default class DpeSanitizerService {
|
|
42
|
+
/**
|
|
43
|
+
* @param dpe {FullDpe}
|
|
44
|
+
* @return {FullDpe}
|
|
45
|
+
*/
|
|
46
|
+
execute(dpe) {
|
|
47
|
+
return ObjectUtil.deepObjectTransform(
|
|
48
|
+
dpe,
|
|
49
|
+
(key) => key,
|
|
50
|
+
(val, key) => {
|
|
51
|
+
if (this.#needTransform(key, val)) {
|
|
52
|
+
return [val];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (this.#isEnum(key)) {
|
|
56
|
+
return val;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (this.#isUndefinedVal(val)) {
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.#isEmptyArray(val)) {
|
|
64
|
+
return val;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (Number.isNaN(Number(val))) {
|
|
68
|
+
return val;
|
|
69
|
+
}
|
|
70
|
+
return Number(val);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#isEnum(key) {
|
|
76
|
+
return key.startsWith('enum_') || key.startsWith('original_enum');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#needTransform(key, val) {
|
|
80
|
+
return typeof val === 'object' && !Array.isArray(val) && nodesToMap.includes(key);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#isUndefinedVal(val) {
|
|
84
|
+
return val === '' || val === null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#isEmptyArray(val) {
|
|
88
|
+
return Array.isArray(val) && val.length === 0;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/engine.js
CHANGED
|
@@ -19,13 +19,16 @@ import {
|
|
|
19
19
|
collectionCanBeEmpty,
|
|
20
20
|
containsAnySubstring,
|
|
21
21
|
isEffetJoule,
|
|
22
|
-
|
|
22
|
+
xmlParser
|
|
23
23
|
} from './utils.js';
|
|
24
24
|
import { Inertie } from './7_inertie.js';
|
|
25
25
|
import getFicheTechnique from './ficheTechnique.js';
|
|
26
26
|
import { ProductionENR } from './16.2_production_enr.js';
|
|
27
|
+
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
27
28
|
|
|
28
|
-
const LIB_VERSION = '1.4.
|
|
29
|
+
const LIB_VERSION = '1.4.1';
|
|
30
|
+
|
|
31
|
+
const dpeSanitizerService = new DpeSanitizerService();
|
|
29
32
|
|
|
30
33
|
function calc_th(map_id) {
|
|
31
34
|
const map = enums.methode_application_dpe_log[map_id];
|
|
@@ -44,11 +47,26 @@ export function getVersion() {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
|
-
*
|
|
50
|
+
* Run the engine with a full dpe xml content
|
|
51
|
+
* @param dpeXmlContent {string} A full dpe xml content
|
|
52
|
+
* @param options {{sanitize: boolean}?}
|
|
53
|
+
* @return {FullDpe}
|
|
54
|
+
*/
|
|
55
|
+
export function calcul_3cl_xml(dpeXmlContent, options) {
|
|
56
|
+
/** @type {{dpe: FullDpe}} **/
|
|
57
|
+
const xmlDpe = xmlParser.parse(dpeXmlContent);
|
|
58
|
+
return calcul_3cl(xmlDpe.dpe, options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Run the engine with a javascript plain object dpe
|
|
63
|
+
* @param inputDpe {FullDpe} A full plain object dpe
|
|
64
|
+
* @param options {{sanitize: boolean}?}
|
|
48
65
|
* @return {FullDpe}
|
|
49
66
|
*/
|
|
50
|
-
export function calcul_3cl(
|
|
51
|
-
|
|
67
|
+
export function calcul_3cl(inputDpe, options) {
|
|
68
|
+
if (!options) options = { sanitize: true };
|
|
69
|
+
const dpe = options.sanitize ? dpeSanitizerService.execute(inputDpe) : inputDpe;
|
|
52
70
|
const modele = enums.modele_dpe[dpe.administratif.enum_modele_dpe_id];
|
|
53
71
|
const dateDpe = dpe.administratif.date_etablissement_dpe;
|
|
54
72
|
if (modele !== 'dpe 3cl 2021 méthode logement') {
|
package/index.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
calcul_3cl,
|
|
3
|
+
calcul_3cl_xml,
|
|
4
|
+
get_classe_ges_dpe,
|
|
5
|
+
get_conso_coeff_1_9_2026,
|
|
6
|
+
getVersion
|
|
7
|
+
} from './engine.js';
|
|
8
|
+
export { calcul_3cl, calcul_3cl_xml, get_classe_ges_dpe, get_conso_coeff_1_9_2026, getVersion };
|
|
3
9
|
import { Umur, Uph, Upb, Uporte, Ubv, Upt } from './3_deperdition.js';
|
|
4
10
|
export { Umur, Uph, Upb, Uporte, Ubv, Upt };
|
package/package.json
CHANGED
package/utils.js
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import enums from './enums.js';
|
|
2
2
|
import tvs from './tv.js';
|
|
3
|
-
import { set
|
|
3
|
+
import { set } from 'lodash-es';
|
|
4
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
5
|
+
|
|
6
|
+
export const xmlParser = new XMLParser({
|
|
7
|
+
// We want to make sure collections of length 1 are still parsed as arrays
|
|
8
|
+
isArray: (name) => {
|
|
9
|
+
const collectionNames = [
|
|
10
|
+
'mur',
|
|
11
|
+
'plancher_bas',
|
|
12
|
+
'plancher_haut',
|
|
13
|
+
'baie_vitree',
|
|
14
|
+
'porte',
|
|
15
|
+
'pont_thermique',
|
|
16
|
+
'ventilation',
|
|
17
|
+
'installation_ecs',
|
|
18
|
+
'generateur_ecs',
|
|
19
|
+
'climatisation',
|
|
20
|
+
'installation_chauffage',
|
|
21
|
+
'generateur_chauffage',
|
|
22
|
+
'emetteur_chauffage',
|
|
23
|
+
'sortie_par_energie'
|
|
24
|
+
];
|
|
25
|
+
if (collectionNames.includes(name)) return true;
|
|
26
|
+
},
|
|
27
|
+
tagValueProcessor: (tagName, val) => {
|
|
28
|
+
if (tagName.startsWith('enum_')) {
|
|
29
|
+
// Preserve value as string for tags starting with "enum_"
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
if (Number.isNaN(Number(val))) return val;
|
|
33
|
+
return Number(val);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
4
36
|
|
|
5
37
|
export let bug_for_bug_compat = false;
|
|
6
38
|
export function set_bug_for_bug_compat() {
|
|
@@ -285,44 +317,12 @@ export function removeKeyFromJSON(jsonObj, keyToRemove, skipKeys) {
|
|
|
285
317
|
}
|
|
286
318
|
}
|
|
287
319
|
|
|
288
|
-
export function useEnumAsString(jsonObj) {
|
|
289
|
-
for (const key in jsonObj) {
|
|
290
|
-
if (jsonObj.hasOwnProperty(key)) {
|
|
291
|
-
if (key.startsWith('enum_')) {
|
|
292
|
-
if (jsonObj[key] !== null) jsonObj[key] = jsonObj[key].toString();
|
|
293
|
-
} else if (typeof jsonObj[key] === 'object') {
|
|
294
|
-
useEnumAsString(jsonObj[key]);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
320
|
export function clean_dpe(dpe_in) {
|
|
301
321
|
// skip generateur_[ecs|chauffage] because some input data is contained in donnee_intermediaire (e.g. pn, qp0, ...)
|
|
302
322
|
removeKeyFromJSON(dpe_in, 'donnee_intermediaire', ['generateur_ecs', 'generateur_chauffage']);
|
|
303
323
|
set(dpe_in, 'logement.sortie', null);
|
|
304
324
|
}
|
|
305
325
|
|
|
306
|
-
export function sanitize_dpe(dpe_in) {
|
|
307
|
-
const collection_paths = [
|
|
308
|
-
'logement.enveloppe.plancher_bas_collection.plancher_bas',
|
|
309
|
-
'logement.enveloppe.plancher_haut_collection.plancher_haut',
|
|
310
|
-
'logement.ventilation_collection.ventilation',
|
|
311
|
-
'logement.climatisation_collection.climatisation',
|
|
312
|
-
'logement.enveloppe.baie_vitree_collection.baie_vitree',
|
|
313
|
-
'logement.enveloppe.porte_collection.porte',
|
|
314
|
-
'logement.enveloppe.pont_thermique_collection.pont_thermique'
|
|
315
|
-
];
|
|
316
|
-
for (const path of collection_paths) {
|
|
317
|
-
if (!has(dpe_in, path)) {
|
|
318
|
-
set(dpe_in, path, []);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (use_enum_as_string) {
|
|
322
|
-
useEnumAsString(dpe_in);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
326
|
/**
|
|
327
327
|
* Retrieve a number describing a thickness from the description
|
|
328
328
|
* @param description string in which to get the number
|