@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/export-cars",
3
- "version": "1.21.16",
3
+ "version": "1.21.19",
4
4
  "description": "",
5
5
  "author": "bytebrand",
6
6
  "license": "UNLICENSED",
@@ -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' && value.includes(',')) {
10
- return `"${value.replace(/"/g, '""')}"`;
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(',') + '\n');
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
- custom_label_0: string;
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
- link: this.getLink(car),
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
  }