@bytebrand/export-cars 1.21.16 → 1.21.19
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/dist/formats/santander-csv.builder.js +6 -3
- package/dist/formats/santander-csv.builder.js.map +1 -1
- package/dist/interfaces/export-item.interface.d.ts +20 -1
- package/dist/transformers/google.transformer.d.ts +10 -2
- package/dist/transformers/google.transformer.js +143 -9
- package/dist/transformers/google.transformer.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/formats/santander-csv.builder.ts +6 -3
- package/src/interfaces/export-item.interface.ts +33 -12
- package/src/transformers/google.transformer.ts +165 -9
package/package.json
CHANGED
|
@@ -6,12 +6,15 @@ export class SantanderCSVBuilder extends XMLBuilderBase {
|
|
|
6
6
|
|
|
7
7
|
async addItem(car: unknown): Promise<void> {
|
|
8
8
|
let values = Object.values(car).map(value => {
|
|
9
|
-
if (typeof value === 'string'
|
|
10
|
-
|
|
9
|
+
if (typeof value === 'string') {
|
|
10
|
+
if (value.includes(','))
|
|
11
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
12
|
+
if (value.includes(';'))
|
|
13
|
+
return `"${value}"`;
|
|
11
14
|
}
|
|
12
15
|
return value;
|
|
13
16
|
});
|
|
14
|
-
await this.write(values.join('
|
|
17
|
+
await this.write(values.join(';') + '\n');
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
async buildEnd(): Promise<void> {
|
|
@@ -153,7 +153,28 @@ export interface GoogleXmlItem {
|
|
|
153
153
|
energy_efficiency_class: string;
|
|
154
154
|
color: string;
|
|
155
155
|
installment: { paybackPeriod: any; monthlyInstallment: any };
|
|
156
|
-
|
|
156
|
+
|
|
157
|
+
additional_image_link: string;
|
|
158
|
+
model: string;
|
|
159
|
+
VIN: string;
|
|
160
|
+
trim: string;
|
|
161
|
+
year: number;
|
|
162
|
+
mileage: number;
|
|
163
|
+
vehicle_option: string;
|
|
164
|
+
body_style: string,
|
|
165
|
+
engine: string,
|
|
166
|
+
date_first_registered: string,
|
|
167
|
+
fuel_consumption: string,
|
|
168
|
+
energy_consumption: string,
|
|
169
|
+
emissions_co2: string,
|
|
170
|
+
|
|
171
|
+
ads_redirect?: string;
|
|
172
|
+
"certified_pre-owned"?: boolean;
|
|
173
|
+
custom_label_0?: string;
|
|
174
|
+
custom_label_1?: string,
|
|
175
|
+
custom_label_2?: string,
|
|
176
|
+
custom_label_3?: string,
|
|
177
|
+
custom_label_4?: string,
|
|
157
178
|
}
|
|
158
179
|
|
|
159
180
|
export interface RubrikkXmlItem {
|
|
@@ -343,25 +364,25 @@ export interface TrovitXmlItem {
|
|
|
343
364
|
export interface Awin {
|
|
344
365
|
product_id: string;
|
|
345
366
|
product_name: string;
|
|
346
|
-
brand_name: string;
|
|
347
|
-
product_model: string;
|
|
348
|
-
deep_link: string;
|
|
367
|
+
brand_name: string;
|
|
368
|
+
product_model: string;
|
|
369
|
+
deep_link: string;
|
|
349
370
|
image_url: string;
|
|
350
371
|
alternate_image: string;
|
|
351
372
|
alternate_image_two: string;
|
|
352
373
|
alternate_image_three: string;
|
|
353
374
|
alternate_image_four: string;
|
|
354
|
-
price: number | string;
|
|
375
|
+
price: number | string;
|
|
355
376
|
currency: string;
|
|
356
|
-
rrp_price: number | string;
|
|
357
|
-
saving: number| string;
|
|
358
|
-
savings_percent: number| string;
|
|
377
|
+
rrp_price: number | string;
|
|
378
|
+
saving: number | string;
|
|
379
|
+
savings_percent: number | string;
|
|
359
380
|
colour: string;
|
|
360
381
|
condition: string;
|
|
361
382
|
delivery_time: string;
|
|
362
383
|
last_updated: string;
|
|
363
|
-
merchant_category: string;
|
|
364
|
-
warranty: string;
|
|
365
|
-
energy_label: string;
|
|
384
|
+
merchant_category: string;
|
|
385
|
+
warranty: string;
|
|
386
|
+
energy_label: string;
|
|
366
387
|
description: string;
|
|
367
|
-
|
|
388
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { escape, get, join, round, toString, trim } from 'lodash';
|
|
1
|
+
import { escape, get, join, round, toSafeInteger, toString, trim, truncate } from 'lodash';
|
|
2
2
|
import { CarDescriptionFactory, DescriptionTypeEnum } from '@bytebrand/car-description';
|
|
3
3
|
import { DataSchemes } from '@bytebrand/car-schema';
|
|
4
4
|
import { TranslatorExtended } from '@bytebrand/schema-tools';
|
|
5
5
|
import { de, deVehicleDetailsPage, deVehicleProps } from '@bytebrand/i18n-dictionaries';
|
|
6
6
|
import { Injectable } from '@nestjs/common';
|
|
7
|
-
import { ConfigService, ImagesService, UserService } from '../services';
|
|
7
|
+
import { ConfigService, ImagesService, TransformerService, UserService } from '../services';
|
|
8
8
|
import { GoogleXmlItem } from '../interfaces';
|
|
9
|
+
import moment from 'moment';
|
|
10
|
+
|
|
11
|
+
const GOOGLE_MAX_TRIM_LENGTH = 50;
|
|
12
|
+
const TIMESTAMP_FIRST_YEAR = 1970;
|
|
9
13
|
|
|
10
14
|
@Injectable()
|
|
11
15
|
export class GoogleTransformer {
|
|
@@ -15,6 +19,7 @@ export class GoogleTransformer {
|
|
|
15
19
|
private userService: UserService,
|
|
16
20
|
private configService: ConfigService,
|
|
17
21
|
private imageService: ImagesService,
|
|
22
|
+
private readonly transformService: TransformerService,
|
|
18
23
|
) {
|
|
19
24
|
const dict = { ...de, ...deVehicleProps, ...deVehicleDetailsPage };
|
|
20
25
|
this.translator = new TranslatorExtended(dict);
|
|
@@ -30,20 +35,41 @@ export class GoogleTransformer {
|
|
|
30
35
|
);
|
|
31
36
|
|
|
32
37
|
return {
|
|
33
|
-
id: carId,
|
|
34
|
-
title: this.getTitle(car),
|
|
35
|
-
description: await this.getDescription(car),
|
|
36
38
|
google_product_category: this.getBreadcrumbs(car),
|
|
39
|
+
title: this.getTitle(car),
|
|
37
40
|
product_type: this.getCategory(car),
|
|
38
|
-
|
|
41
|
+
VIN: this.transformService.vinCode(car),
|
|
42
|
+
id: carId,
|
|
39
43
|
image_link: transformedImages[0],
|
|
44
|
+
additional_image_link: transformedImages.slice(1).join(','),
|
|
45
|
+
// ads_redirect: this.getLink(car),
|
|
46
|
+
link: this.transformService.link(car),
|
|
40
47
|
condition: this.mapState(car),
|
|
48
|
+
price: this.getPrice(car),
|
|
49
|
+
// certified_pre-owned: '',
|
|
50
|
+
brand: this.transformService.make(car),
|
|
51
|
+
model: this.transformService.model(car),
|
|
52
|
+
trim: this.getTrim(car),
|
|
53
|
+
year: this.getYear(car),
|
|
54
|
+
mileage: this.transformService.mileage(car),
|
|
55
|
+
color: this.getColor(car),
|
|
56
|
+
vehicle_option: this.getExtrasField(car),
|
|
57
|
+
body_style: this.mapBodyStyle(car),
|
|
58
|
+
engine: this.mapFuel(car),
|
|
59
|
+
date_first_registered: this.getDateFirstRegistered(car),
|
|
60
|
+
fuel_consumption: `${this.transformService.fuelConsumption(car)}'l/100km'`,
|
|
61
|
+
energy_consumption: `${this.transformService.fuelConsumption(car)}'kWt/100km'`,
|
|
62
|
+
emissions_co2: `${this.transformService.cO2Emission(car)}g/km`,
|
|
63
|
+
description: await this.getDescription(car),
|
|
64
|
+
// custom_label_0: '',
|
|
65
|
+
// custom_label_1: '',
|
|
66
|
+
// custom_label_2: '',
|
|
67
|
+
// custom_label_3: '',
|
|
68
|
+
// custom_label_4: '',
|
|
69
|
+
|
|
41
70
|
availability: this.getAvailability(car),
|
|
42
|
-
brand: get(car, 'mainData.make', ''),
|
|
43
|
-
price: this.getRetailPrice(car),
|
|
44
71
|
sale_price: this.getPrice(car),
|
|
45
72
|
energy_efficiency_class: this.getEnergyClass(car),
|
|
46
|
-
color: this.getColor(car),
|
|
47
73
|
installment: this.getInstallment(car),
|
|
48
74
|
custom_label_0: `${this.getInstallment(car)} EUR mtl.`,
|
|
49
75
|
};
|
|
@@ -166,4 +192,134 @@ export class GoogleTransformer {
|
|
|
166
192
|
return make;
|
|
167
193
|
}
|
|
168
194
|
}
|
|
195
|
+
|
|
196
|
+
private mapFuel(car: DataSchemes.Car): string {
|
|
197
|
+
const available = {
|
|
198
|
+
selector_fuel_petrol: 'Gasoline',
|
|
199
|
+
selector_fuel_diesel: 'Diesel',
|
|
200
|
+
selector_fuel_electrical: 'Electric',
|
|
201
|
+
selector_fuel_hybridElectricalPetrol: 'Hybrid',
|
|
202
|
+
selector_fuel_hybridElectricalDiesel: 'Hybrid',
|
|
203
|
+
selector_fuel_carGas: 'CarGas',
|
|
204
|
+
selector_fuel_naturalGas: 'NaturalGas',
|
|
205
|
+
selector_fuel_ethanol: 'Ethanol',
|
|
206
|
+
selector_fuel_hydrogen: 'Hydrogen',
|
|
207
|
+
selector_unknown: 'Other',
|
|
208
|
+
};
|
|
209
|
+
const fuel = get(car, 'consumption.fuel', 'selector_unknown');
|
|
210
|
+
return get(available, fuel);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private mapBodyStyle(car: DataSchemes.Car): string {
|
|
214
|
+
const available = {
|
|
215
|
+
selector_unknown: 'Other',
|
|
216
|
+
selector_category_combi: 'Wagon',
|
|
217
|
+
selector_category_sedan: 'Sedan',
|
|
218
|
+
selector_category_compact: 'Small_car',
|
|
219
|
+
selector_category_coupe: 'Coupe',
|
|
220
|
+
selector_category_convertible: 'Convertible',
|
|
221
|
+
selector_category_suv: 'Suv',
|
|
222
|
+
selector_category_van: 'Van',
|
|
223
|
+
selector_category_transporter: 'Truck',
|
|
224
|
+
selector_category_minibus: 'Minivan',
|
|
225
|
+
};
|
|
226
|
+
const category = get(car, 'mainData.category', 'selector_unknown');
|
|
227
|
+
return get(available, category);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private getExtrasField(car: DataSchemes.Car): string {
|
|
231
|
+
const booleanFields: string[] = [
|
|
232
|
+
'engineData.startStopSystem',
|
|
233
|
+
'window.panoramicGlassRoof',
|
|
234
|
+
'assistants.automaticRainSensor',
|
|
235
|
+
'assistants.cruiseControl',
|
|
236
|
+
'assistants.headUpDisplay',
|
|
237
|
+
'assistants.abs',
|
|
238
|
+
'assistants.esp',
|
|
239
|
+
'assistants.collisionAvoidance',
|
|
240
|
+
'assistants.trafficSignRecognition',
|
|
241
|
+
'lights.xenonHeadlights',
|
|
242
|
+
'lights.ledHeadlights',
|
|
243
|
+
'lights.laserHeadlights',
|
|
244
|
+
'lights.lightSensor',
|
|
245
|
+
'wheel.multifunctionalWheel',
|
|
246
|
+
'heatingCooling.auxiliaryHeating',
|
|
247
|
+
'trailerCoupling.trailerCouplingFix',
|
|
248
|
+
'trailerCoupling.trailerCouplingRemovable',
|
|
249
|
+
'trailerCoupling.trailerCouplingRotatable',
|
|
250
|
+
'multimedia.navigationSystem',
|
|
251
|
+
'multimedia.bluetooth',
|
|
252
|
+
'multimedia.handsFreePhoneSystem',
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
const selectorFields: string[] = [
|
|
256
|
+
'driveSuspension.driveType',
|
|
257
|
+
'interior.interiorType',
|
|
258
|
+
'heatingCooling.climatisation',
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
const selectorListFields: string[] = [
|
|
262
|
+
'seats.driverSeats',
|
|
263
|
+
'seats.coDriverSeats',
|
|
264
|
+
'seats.backSeats',
|
|
265
|
+
'assistants.parkingAssistants',
|
|
266
|
+
'airbags.airbag',
|
|
267
|
+
];
|
|
268
|
+
|
|
269
|
+
const filteredBooleanFields = booleanFields.filter((field: string) => {
|
|
270
|
+
return get(car, field, false);
|
|
271
|
+
});
|
|
272
|
+
const filteredSelectorFields = selectorFields.filter((field: string) => {
|
|
273
|
+
const value = get(car, field);
|
|
274
|
+
return value && value !== 'selector_unknown';
|
|
275
|
+
});
|
|
276
|
+
const filteredSelectorListFields = selectorListFields.filter((field: string) => {
|
|
277
|
+
const list: string[] = get(car, field);
|
|
278
|
+
if (this.isNotEmptyArray(list)) {
|
|
279
|
+
const filtered: string[] = list.filter((selector: string) => {
|
|
280
|
+
return selector && selector !== 'selector_unknown';
|
|
281
|
+
});
|
|
282
|
+
return filtered.length;
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
});
|
|
286
|
+
return filteredBooleanFields
|
|
287
|
+
.concat(filteredSelectorFields)
|
|
288
|
+
.concat(filteredSelectorListFields)
|
|
289
|
+
.map((field: string) => {
|
|
290
|
+
return this.translator.translateVal(field.replace('.', '_'));
|
|
291
|
+
})
|
|
292
|
+
.join(', ');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private getTrim(car: DataSchemes.Car): string {
|
|
296
|
+
const trim = toString(get(car, 'mainData.subModel', ''));
|
|
297
|
+
return truncate(trim, { length: GOOGLE_MAX_TRIM_LENGTH, separator: ' ' });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private getYear(car: DataSchemes.Car) {
|
|
301
|
+
const firstModelsProductionDate = +get(car, 'mainData.firstModelsProductionDate', 0);
|
|
302
|
+
const firstModelsProductionYear = toSafeInteger(moment(firstModelsProductionDate * 1000).format('YYYY'));
|
|
303
|
+
|
|
304
|
+
return firstModelsProductionYear > TIMESTAMP_FIRST_YEAR ? firstModelsProductionYear : null
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private getDateFirstRegistered(car: DataSchemes.Car): string {
|
|
308
|
+
const timestamp = +get(car, 'mainData.mileage', '')
|
|
309
|
+
|
|
310
|
+
if (!timestamp)
|
|
311
|
+
return null
|
|
312
|
+
|
|
313
|
+
const date = new Date(timestamp * 1000);
|
|
314
|
+
const month = date.getUTCMonth() + 1;
|
|
315
|
+
const year = date.getUTCFullYear();
|
|
316
|
+
|
|
317
|
+
const formattedMonth = month < 10 ? `0${month}` : month;
|
|
318
|
+
|
|
319
|
+
return `${formattedMonth}/${year}`;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private isNotEmptyArray(array: any[]): boolean {
|
|
323
|
+
return array && Array.isArray(array) && !!array.length;
|
|
324
|
+
}
|
|
169
325
|
}
|