@bytebrand/export-cars 1.26.22 → 1.26.24

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/package.json CHANGED
@@ -1,63 +1,63 @@
1
- {
2
- "name": "@bytebrand/export-cars",
3
- "version": "1.26.22",
4
- "description": "",
5
- "author": "bytebrand",
6
- "license": "UNLICENSED",
7
- "scripts": {
8
- "prebuild": "rimraf dist",
9
- "build": "nest build",
10
- "prepublishOnly": "npm run build",
11
- "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12
- "lint": "eslint -c .eslintrc.js --ext .ts src/",
13
- "start:dev": "nest start --watch",
14
- "test": "ts-node --transpile-only test/google-feed-envkv.test.ts"
15
- },
16
- "dependencies": {
17
- "@bytebrand/autode-request": "^1.19.2",
18
- "@bytebrand/car-basic-data-scope": "^9.11.24",
19
- "@bytebrand/car-description": "^1.17.20",
20
- "@bytebrand/car-schema": "^3.14.15",
21
- "@bytebrand/chunkable": "^5.19.8",
22
- "@bytebrand/cronicle-helper": "^1.21.8",
23
- "@bytebrand/fe-financing": "^5.14.19",
24
- "@bytebrand/i18n-dictionaries": "^1.5.0",
25
- "@bytebrand/import-helper": "^0.45.0",
26
- "@bytebrand/schema-tools": "^3.18.8",
27
- "@bytebrand/zipper": "^2.2.0",
28
- "@nestjs/cache-manager": "^2.1.0",
29
- "@nestjs/common": "^10.2.7",
30
- "@nestjs/core": "^10.2.7",
31
- "axios": "^1.5.1",
32
- "bottleneck": "^2.19.5",
33
- "cache-manager": "^5.2.4",
34
- "csv-write-stream": "^2.0.0",
35
- "google-spreadsheet": "^3.3.0",
36
- "lodash": "^4.17.21",
37
- "moment": "^2.29.4",
38
- "nest-commander": "~3.15.0",
39
- "reflect-metadata": "^0.1.13",
40
- "vin-generator": "^1.0.1"
41
- },
42
- "devDependencies": {
43
- "@nestjs/cli": "^10.1.18",
44
- "@types/google-spreadsheet": "^3.3.0",
45
- "@types/inquirer": "^8.2.4",
46
- "@types/lodash": "^4.14.186",
47
- "@types/node": "^16.18.58",
48
- "@typescript-eslint/eslint-plugin": "^6.8.0",
49
- "@typescript-eslint/parser": "^6.8.0",
50
- "eslint": "^8.0.1",
51
- "eslint-config-prettier": "^8.3.0",
52
- "eslint-plugin-prettier": "^4.0.0",
53
- "gts": "^3.1.1",
54
- "prettier": "^2.3.2",
55
- "ts-node": "^10.0.0",
56
- "tsconfig-paths": "4.1.0",
57
- "typescript": "^5.2.2"
58
- },
59
- "engines": {
60
- "node": ">=14.0.0"
61
- },
62
- "gitHead": "e827f35865032821bf3a081f03a026d3d3f8a540"
63
- }
1
+ {
2
+ "name": "@bytebrand/export-cars",
3
+ "version": "1.26.24",
4
+ "description": "",
5
+ "author": "bytebrand",
6
+ "license": "UNLICENSED",
7
+ "scripts": {
8
+ "prebuild": "rimraf dist",
9
+ "build": "nest build",
10
+ "prepublishOnly": "npm run build",
11
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12
+ "lint": "eslint -c .eslintrc.js --ext .ts src/",
13
+ "start:dev": "nest start --watch",
14
+ "test": "ts-node --transpile-only test/google-feed-envkv.test.ts"
15
+ },
16
+ "dependencies": {
17
+ "@bytebrand/autode-request": "^1.19.2",
18
+ "@bytebrand/car-basic-data-scope": "^9.11.24",
19
+ "@bytebrand/car-description": "^1.17.20",
20
+ "@bytebrand/car-schema": "^3.14.15",
21
+ "@bytebrand/chunkable": "^5.19.8",
22
+ "@bytebrand/cronicle-helper": "^1.21.8",
23
+ "@bytebrand/fe-financing": "^5.14.19",
24
+ "@bytebrand/i18n-dictionaries": "^1.5.0",
25
+ "@bytebrand/import-helper": "^0.45.0",
26
+ "@bytebrand/schema-tools": "^3.18.8",
27
+ "@bytebrand/zipper": "^2.2.0",
28
+ "@nestjs/cache-manager": "^2.1.0",
29
+ "@nestjs/common": "^10.2.7",
30
+ "@nestjs/core": "^10.2.7",
31
+ "axios": "^1.5.1",
32
+ "bottleneck": "^2.19.5",
33
+ "cache-manager": "^5.2.4",
34
+ "csv-write-stream": "^2.0.0",
35
+ "google-spreadsheet": "^3.3.0",
36
+ "lodash": "^4.17.21",
37
+ "moment": "^2.29.4",
38
+ "nest-commander": "~3.15.0",
39
+ "reflect-metadata": "^0.1.13",
40
+ "vin-generator": "^1.0.1"
41
+ },
42
+ "devDependencies": {
43
+ "@nestjs/cli": "^10.1.18",
44
+ "@types/google-spreadsheet": "^3.3.0",
45
+ "@types/inquirer": "^8.2.4",
46
+ "@types/lodash": "^4.14.186",
47
+ "@types/node": "^16.18.58",
48
+ "@typescript-eslint/eslint-plugin": "^6.8.0",
49
+ "@typescript-eslint/parser": "^6.8.0",
50
+ "eslint": "^8.0.1",
51
+ "eslint-config-prettier": "^8.3.0",
52
+ "eslint-plugin-prettier": "^4.0.0",
53
+ "gts": "^3.1.1",
54
+ "prettier": "^2.3.2",
55
+ "ts-node": "^10.0.0",
56
+ "tsconfig-paths": "4.1.0",
57
+ "typescript": "^5.2.2"
58
+ },
59
+ "engines": {
60
+ "node": ">=14.0.0"
61
+ },
62
+ "gitHead": "e827f35865032821bf3a081f03a026d3d3f8a540"
63
+ }
@@ -47,17 +47,46 @@ export function googleElectricRange(car: DataSchemes.Car): string | undefined {
47
47
  return range ? `${range}km` : undefined;
48
48
  }
49
49
 
50
+ // Plug-in hybrids: Google and Pkw-EnVKV (CoC 49.4) want the WEIGHTED combined values for fuel,
51
+ // electricity and CO2, and the fuel consumption with a DISCHARGED battery separately. In CBD those
52
+ // are wltpWeightedCombined, wltpWeightedPowerCombined, wltpCo2 (weighted for a plug-in) and
53
+ // wltpCombined (discharged for a plug-in); the legacy consumptionCombined / co2 are only a fallback.
54
+
50
55
  // Missing or 0 is omitted instead of sending "0l/100km". BEVs carry no fuel consumption
51
56
  // (CBD mirrors kWh into consumptionCombined for them).
52
57
  export function googleFuelConsumption(car: DataSchemes.Car): string | undefined {
53
58
  if (isBev(car)) return undefined;
54
- const value = positive(get(car, 'consumption.consumptionCombined'));
59
+ const value = isPlugIn(car)
60
+ ? positive(get(car, 'consumption.wltpWeightedCombined')) ?? positive(get(car, 'consumption.consumptionCombined'))
61
+ : positive(get(car, 'consumption.consumptionCombined'));
55
62
  return value ? `${value}l/100km` : undefined;
56
63
  }
57
64
 
58
- // A BEV emits 0 g/km by definition; for every other car a missing or 0 value is omitted.
65
+ // Plug-in only. No fallback: the legacy combined value is (close to) the weighted one, sending it
66
+ // here would repeat the weighted figure as the discharged one.
67
+ export function googleFuelConsumptionDischarged(car: DataSchemes.Car): string | undefined {
68
+ if (!isPlugIn(car)) return undefined;
69
+ const value = positive(get(car, 'consumption.wltpCombined'));
70
+ return value ? `${value}l/100km` : undefined;
71
+ }
72
+
73
+ // BEV: combined consumption. Plug-in: weighted combined. Nothing for other cars.
74
+ export function googleEnergyConsumption(car: DataSchemes.Car): string | undefined {
75
+ let value: number | undefined;
76
+ if (isBev(car)) {
77
+ value = positive(get(car, 'consumption.wltpPowerCombined')) ?? positive(get(car, 'consumption.consumptionPowerCombined'));
78
+ } else if (isPlugIn(car)) {
79
+ value = positive(get(car, 'consumption.wltpWeightedPowerCombined'));
80
+ }
81
+ return value ? `${value}kWh/100km` : undefined;
82
+ }
83
+
84
+ // A BEV emits 0 g/km by definition; a plug-in sends the weighted WLTP CO2; for every other car the
85
+ // stored value. A missing or 0 value is omitted.
59
86
  export function googleCo2(car: DataSchemes.Car): string | undefined {
60
87
  if (isBev(car)) return '0g/km';
61
- const value = positive(get(car, 'environmentEmissions.co2'));
88
+ const value = isPlugIn(car)
89
+ ? positive(get(car, 'environmentEmissions.wltpCo2')) ?? positive(get(car, 'environmentEmissions.co2'))
90
+ : positive(get(car, 'environmentEmissions.co2'));
62
91
  return value ? `${value}g/km` : undefined;
63
92
  }
@@ -8,7 +8,14 @@ import { Injectable } from '@nestjs/common';
8
8
  import { ConfigService, ImagesService, MappingService, TransformerService, UserService } from '../services';
9
9
  import { GoogleXmlItem } from '../interfaces';
10
10
  import { isValidVin } from '../utils';
11
- import { googleCo2, googleElectricRange, googleEngine, googleFuelConsumption } from './google-feed-envkv';
11
+ import {
12
+ googleCo2,
13
+ googleElectricRange,
14
+ googleEnergyConsumption,
15
+ googleEngine,
16
+ googleFuelConsumption,
17
+ googleFuelConsumptionDischarged,
18
+ } from './google-feed-envkv';
12
19
  import {
13
20
  META_PUSH_PRIORITY_ALLOWED_VALUES,
14
21
  META_PUSH_PRIORITY_STANDARD,
@@ -93,9 +100,9 @@ export class GoogleTransformer {
93
100
  capacity: this.getCapacity(car),
94
101
  date_first_registered: this.getDateFirstRegistered(car),
95
102
  fuel_consumption: googleFuelConsumption(car),
96
- energy_consumption: this.getEnergyConsumption(car),
103
+ energy_consumption: googleEnergyConsumption(car),
97
104
  electric_range: googleElectricRange(car),
98
- fuel_consumption_discharged_battery: this.getFuelConsumptionDischargedBattery(car),
105
+ fuel_consumption_discharged_battery: googleFuelConsumptionDischarged(car),
99
106
  co2_emissions: googleCo2(car),
100
107
  description: this.processText((await this.getDescription(car))),
101
108
  identifier_exists: 'yes',
@@ -332,26 +339,8 @@ export class GoogleTransformer {
332
339
  return certifications.length ? certifications : undefined;
333
340
  }
334
341
 
335
- private getEnergyConsumption(car: DataSchemes.Car): string | undefined {
336
- const fuel = get(car, 'consumption.fuel');
337
- const isElectricOrHybrid =
338
- ['selector_fuel_electrical', 'selector_fuel_hybridElectricalPetrol', 'selector_fuel_hybridElectricalDiesel'].includes(fuel) ||
339
- !!get(car, 'engineData.hybridPlugin');
340
- if (!isElectricOrHybrid) {
341
- return undefined;
342
- }
343
- const power = get(car, 'consumption.wltpPowerCombined') || get(car, 'consumption.consumptionPowerCombined');
344
- return power ? `${power}kWh/100km` : undefined;
345
- }
346
342
 
347
343
 
348
- private getFuelConsumptionDischargedBattery(car: DataSchemes.Car): string | undefined {
349
- if (!get(car, 'engineData.hybridPlugin')) {
350
- return undefined;
351
- }
352
- const consumption = get(car, 'consumption.consumptionCombined') || get(car, 'consumption.wltpCombined');
353
- return consumption ? `${consumption}l/100km` : undefined;
354
- }
355
344
 
356
345
  private getInstallment(car: DataSchemes.Car) {
357
346
  const monthlyInstallment = round(get(car, 'rate.monthlyInstallment', null));
@@ -1,6 +1,13 @@
1
1
  // Run: npm test (ts-node, no test framework needed)
2
2
  import * as assert from 'node:assert/strict';
3
- import { googleCo2, googleElectricRange, googleEngine, googleFuelConsumption } from '../src/transformers/google-feed-envkv';
3
+ import {
4
+ googleCo2,
5
+ googleElectricRange,
6
+ googleEnergyConsumption,
7
+ googleEngine,
8
+ googleFuelConsumption,
9
+ googleFuelConsumptionDischarged,
10
+ } from '../src/transformers/google-feed-envkv';
4
11
 
5
12
  const car = (fuel: string, extra: Record<string, unknown> = {}): any => ({
6
13
  consumption: { fuel, ...(extra.consumption as object) },
@@ -56,6 +63,42 @@ const cases: Array<[string, () => void]> = [
56
63
  assert.equal(googleCo2(car('selector_fuel_petrol', { environmentEmissions: { co2: 0 } })), undefined);
57
64
  assert.equal(googleCo2(car('selector_fuel_petrol', { environmentEmissions: { co2: 144 } })), '144g/km');
58
65
  }],
66
+ ['plug-in: weighted fuel, discharged fuel, weighted electricity and weighted CO2', () => {
67
+ const phev = car('selector_fuel_hybridElectricalPetrol', {
68
+ hybridPlugin: true,
69
+ consumption: { consumptionCombined: 1.4, wltpCombined: 6.3, wltpWeightedCombined: 1.2, wltpPowerCombined: 19.3, wltpWeightedPowerCombined: 17.3 },
70
+ environmentEmissions: { co2: 121, wltpCo2: 28 },
71
+ });
72
+ assert.equal(googleFuelConsumption(phev), '1.2l/100km');
73
+ assert.equal(googleFuelConsumptionDischarged(phev), '6.3l/100km');
74
+ assert.equal(googleEnergyConsumption(phev), '17.3kWh/100km');
75
+ assert.equal(googleCo2(phev), '28g/km');
76
+ }],
77
+ ['plug-in without weighted values falls back to the legacy combined ones, never the pure-electric value', () => {
78
+ const phev = car('selector_fuel_hybridElectricalPetrol', {
79
+ hybridPlugin: true,
80
+ consumption: { consumptionCombined: 1.4, wltpPowerCombined: 19.3 },
81
+ environmentEmissions: { co2: 32 },
82
+ });
83
+ assert.equal(googleFuelConsumption(phev), '1.4l/100km');
84
+ assert.equal(googleFuelConsumptionDischarged(phev), undefined);
85
+ assert.equal(googleEnergyConsumption(phev), undefined);
86
+ assert.equal(googleCo2(phev), '32g/km');
87
+ }],
88
+ ['non-plug-in hybrids and combustion cars keep the stored values and send no electricity', () => {
89
+ const hev = car('selector_fuel_hybridElectricalPetrol', {
90
+ consumption: { consumptionCombined: 5.1, wltpCombined: 5.3, wltpWeightedCombined: 2.3, wltpWeightedPowerCombined: 21.5 },
91
+ environmentEmissions: { co2: 116, wltpCo2: 119 },
92
+ });
93
+ assert.equal(googleFuelConsumption(hev), '5.1l/100km');
94
+ assert.equal(googleFuelConsumptionDischarged(hev), undefined);
95
+ assert.equal(googleEnergyConsumption(hev), undefined);
96
+ assert.equal(googleCo2(hev), '116g/km');
97
+ }],
98
+ ['BEV energy consumption is the combined value', () => {
99
+ assert.equal(googleEnergyConsumption(car('selector_fuel_electrical', { consumption: { wltpPowerCombined: 16.5 } })), '16.5kWh/100km');
100
+ assert.equal(googleEnergyConsumption(car('selector_fuel_electrical', { consumption: { consumptionPowerCombined: 18 } })), '18kWh/100km');
101
+ }],
59
102
  ];
60
103
 
61
104
  let failed = 0;