@bytebrand/export-cars 1.21.34 → 1.21.37

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 (36) hide show
  1. package/dist/export.schema.d.ts +2 -2
  2. package/dist/export.schema.js +1 -1
  3. package/dist/export.schema.js.map +1 -1
  4. package/dist/exporters/criteo.exporter.d.ts +12 -0
  5. package/dist/exporters/criteo.exporter.js +61 -0
  6. package/dist/exporters/criteo.exporter.js.map +1 -0
  7. package/dist/exporters/index.d.ts +1 -0
  8. package/dist/exporters/index.js +1 -0
  9. package/dist/exporters/index.js.map +1 -1
  10. package/dist/exporters/santander.exporter.js +5 -3
  11. package/dist/exporters/santander.exporter.js.map +1 -1
  12. package/dist/formats/google-xml.builder.d.ts +2 -2
  13. package/dist/formats/google-xml.builder.js.map +1 -1
  14. package/dist/interfaces/export-item.interface.d.ts +21 -0
  15. package/dist/transformers/criteo.transformer.d.ts +27 -0
  16. package/dist/transformers/criteo.transformer.js +161 -0
  17. package/dist/transformers/criteo.transformer.js.map +1 -0
  18. package/dist/transformers/google.transformer.js +4 -3
  19. package/dist/transformers/google.transformer.js.map +1 -1
  20. package/dist/transformers/mobile.transformer.d.ts +2 -1
  21. package/dist/transformers/mobile.transformer.js.map +1 -1
  22. package/dist/transformers/santander.transformer.d.ts +2 -0
  23. package/dist/transformers/santander.transformer.js +15 -0
  24. package/dist/transformers/santander.transformer.js.map +1 -1
  25. package/dist/tsconfig.build.tsbuildinfo +1 -1
  26. package/package.json +2 -2
  27. package/src/export.schema.ts +2 -1
  28. package/src/exporters/criteo.exporter.ts +46 -0
  29. package/src/exporters/index.ts +1 -0
  30. package/src/exporters/santander.exporter.ts +6 -4
  31. package/src/formats/google-xml.builder.ts +2 -2
  32. package/src/interfaces/export-item.interface.ts +19 -0
  33. package/src/transformers/criteo.transformer.ts +169 -0
  34. package/src/transformers/google.transformer.ts +4 -5
  35. package/src/transformers/mobile.transformer.ts +1 -1
  36. package/src/transformers/santander.transformer.ts +17 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/export-cars",
3
- "version": "1.21.34",
3
+ "version": "1.21.37",
4
4
  "description": "",
5
5
  "author": "bytebrand",
6
6
  "license": "UNLICENSED",
@@ -58,4 +58,4 @@
58
58
  "node": ">=14.0.0"
59
59
  },
60
60
  "gitHead": "e827f35865032821bf3a081f03a026d3d3f8a540"
61
- }
61
+ }
@@ -40,6 +40,7 @@ import {
40
40
  TrovitExporter,
41
41
  AwinExporter,
42
42
  SantanderExporter,
43
+ CriteoExporter,
43
44
  } from './exporters';
44
45
 
45
46
  export const exportSchema = {
@@ -105,7 +106,7 @@ export const exportSchema = {
105
106
  },
106
107
  [ExportType.CRITEO]: {
107
108
  config: CriteoConfig,
108
- exporter: GoogleExporter,
109
+ exporter: CriteoExporter,
109
110
  },
110
111
  [ExportType.FACEBOOK_SHOP]: {
111
112
  config: FacebookShopConfig,
@@ -0,0 +1,46 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { ModuleRef } from '@nestjs/core';
3
+
4
+ import { CronicleUtils } from '@bytebrand/cronicle-helper';
5
+ import { FsChunkInterface } from '@bytebrand/chunkable';
6
+
7
+ import { CarService, ConfigService, LoaderService, RulesService } from '../services';
8
+ import { GoogleXmlBuilder } from '../formats/google-xml.builder';
9
+ import { Exporter } from '../interfaces/exporter.interface';
10
+ import { CriteoTransformer } from 'src/transformers/criteo.transformer';
11
+
12
+ @Injectable()
13
+ export class CriteoExporter implements Exporter {
14
+ constructor(
15
+ private carService: CarService,
16
+ private configService: ConfigService,
17
+ private rulesService: RulesService,
18
+ private loaderService: LoaderService,
19
+ private moduleRef: ModuleRef,
20
+ ) { }
21
+
22
+ async export(): Promise<void> {
23
+ const [fileName] = this.configService.config.outputFiles;
24
+ await CronicleUtils.ensureDir(this.configService.config.pathesConfig.localConvertedDirectory);
25
+ await CronicleUtils.emptyDir(this.configService.config.pathesConfig.localConvertedDirectory);
26
+
27
+ const googleXmlBuilder = new GoogleXmlBuilder(fileName);
28
+ await googleXmlBuilder.buildStart(this.configService.config.domain);
29
+
30
+ const criteoTransformer = await this.moduleRef.create(CriteoTransformer);
31
+
32
+ await this.loaderService.process(async (chunk: FsChunkInterface) => {
33
+ chunk.data = chunk.data.filter(car => this.carService.filterCar(car, this.rulesService.rules));
34
+
35
+ for (const data of chunk.data) {
36
+ const car = await criteoTransformer.transform(data);
37
+ if (car)
38
+ await googleXmlBuilder.addItem(car);
39
+ this.carService.counter.countCar(data.images);
40
+ }
41
+ });
42
+
43
+ await googleXmlBuilder.buildEnd();
44
+ console.log(this.carService.counter.getCountedCars());
45
+ }
46
+ }
@@ -2,6 +2,7 @@ export * from './as24.exporter';
2
2
  export * from './auto24.exporter';
3
3
  export * from './autouncle.exporter';
4
4
  export * from './by-owner.exporter';
5
+ export * from './criteo.exporter';
5
6
  export * from './fb.exporter';
6
7
  export * from './google.exporter';
7
8
  export * from './meinstadt.exporter';
@@ -3,6 +3,7 @@ import { ModuleRef } from '@nestjs/core';
3
3
 
4
4
  import { CronicleUtils } from '@bytebrand/cronicle-helper';
5
5
  import { FsChunkInterface } from '@bytebrand/chunkable';
6
+ import { Car } from '@bytebrand/car-schema/dist/schema/data';
6
7
 
7
8
  import { CarService, RulesService, LoaderService, ConfigService } from '../services';
8
9
  import { MobileTransformer } from '../transformers/mobile.transformer';
@@ -37,10 +38,11 @@ export class SantanderExporter implements Exporter {
37
38
  if (chunk && chunk.data) {
38
39
  for (const data of chunk.data) {
39
40
  const { car, images } = data;
40
- const transformedData = await santanderTransformer.transform(car, images);
41
- const transformedNormalizedData = await santanderTransformer.normalizeCarData(transformedData);
42
- await santanderCSVBuilder.addItem(transformedNormalizedData);
43
-
41
+ if (!(!car.mainData.firstRegistration && car.mainData.mileage > 1000)) {
42
+ const transformedData = await santanderTransformer.transform(car, images);
43
+ const transformedNormalizedData = await santanderTransformer.normalizeCarData(transformedData);
44
+ await santanderCSVBuilder.addItem(transformedNormalizedData);
45
+ }
44
46
  this.carService.counter.countCar(images);
45
47
  }
46
48
  }
@@ -1,5 +1,5 @@
1
1
  import { XMLBuilderBase } from '@bytebrand/cronicle-helper';
2
- import { GoogleXmlItem, FbXmlItem } from '../interfaces';
2
+ import { GoogleXmlItem, FbXmlItem, CriteoXmlItem } from '../interfaces';
3
3
 
4
4
  export class GoogleXmlBuilder extends XMLBuilderBase {
5
5
  async buildStart(url?: string): Promise<void> {
@@ -8,7 +8,7 @@ export class GoogleXmlBuilder extends XMLBuilderBase {
8
8
  );
9
9
  }
10
10
 
11
- async addItem(item: FbXmlItem | GoogleXmlItem): Promise<void> {
11
+ async addItem(item: FbXmlItem | GoogleXmlItem | CriteoXmlItem): Promise<void> {
12
12
  const listing: string = Object.entries(item)
13
13
  .map(([key, data]): string => {
14
14
  if (key === 'installment') {
@@ -137,6 +137,25 @@ export interface AutouncleJsonItem {
137
137
  status: string;
138
138
  }
139
139
 
140
+ export interface CriteoXmlItem {
141
+ id: string;
142
+ title: string;
143
+ description: string;
144
+ google_product_category: string;
145
+ product_type: string;
146
+ link: string;
147
+ image_link: string;
148
+ condition: string;
149
+ availability: string;
150
+ brand: any;
151
+ price: string;
152
+ sale_price: string;
153
+ energy_efficiency_class: string;
154
+ color: string;
155
+ installment: { paybackPeriod: any; monthlyInstallment: any };
156
+ custom_label_0: string;
157
+ }
158
+
140
159
  export interface GoogleXmlItem {
141
160
  id: string;
142
161
  title: string;
@@ -0,0 +1,169 @@
1
+ import { escape, get, join, round, toString, trim } from 'lodash';
2
+ import { CarDescriptionFactory, DescriptionTypeEnum } from '@bytebrand/car-description';
3
+ import { DataSchemes } from '@bytebrand/car-schema';
4
+ import { TranslatorExtended } from '@bytebrand/schema-tools';
5
+ import { de, deVehicleDetailsPage, deVehicleProps } from '@bytebrand/i18n-dictionaries';
6
+ import { Injectable } from '@nestjs/common';
7
+ import { ConfigService, ImagesService, UserService } from '../services';
8
+ import { CriteoXmlItem } from '../interfaces';
9
+
10
+ @Injectable()
11
+ export class CriteoTransformer {
12
+ private translator: TranslatorExtended;
13
+
14
+ constructor(
15
+ private userService: UserService,
16
+ private configService: ConfigService,
17
+ private imageService: ImagesService,
18
+ ) {
19
+ const dict = { ...de, ...deVehicleProps, ...deVehicleDetailsPage };
20
+ this.translator = new TranslatorExtended(dict);
21
+ }
22
+
23
+ async transform({ car, images }: { car: DataSchemes.Car; images: Array<any> }): Promise<CriteoXmlItem> {
24
+ const carId = toString(get(car, '_id', ''));
25
+ const transformedImages = await this.imageService.getImages(
26
+ car,
27
+ images,
28
+ this.configService.config.imageType,
29
+ false,
30
+ );
31
+
32
+ return {
33
+ id: carId,
34
+ title: this.getTitle(car),
35
+ description: await this.getDescription(car),
36
+ google_product_category: this.getBreadcrumbs(car),
37
+ product_type: this.getCategory(car),
38
+ link: this.getLink(car),
39
+ image_link: transformedImages[0],
40
+ condition: this.mapState(car),
41
+ availability: this.getAvailability(car),
42
+ brand: get(car, 'mainData.make', ''),
43
+ price: this.getRetailPrice(car),
44
+ sale_price: this.getPrice(car),
45
+ energy_efficiency_class: this.getEnergyClass(car),
46
+ color: this.getColor(car),
47
+ installment: this.getInstallment(car),
48
+ custom_label_0: `${this.getInstallment(car)} EUR mtl.`,
49
+ };
50
+ }
51
+
52
+ private getTitle(car: DataSchemes.Car): string {
53
+ const model = toString(get(car, 'mainData.model', ''));
54
+
55
+ return trim(escape(join([this.getBrand(car), model], ' ')));
56
+ }
57
+
58
+ private async getDescription(car: DataSchemes.Car): Promise<string> {
59
+ if (this.configService.config.isCriteo) {
60
+ return '0€ Anzahlung möglich. 14 Tage Geld-zurück-Garantie. Lieferung bis vor die Haustür. 1 Jahr Garantie inklusive. Deutschlands großes Autoportal.';
61
+ }
62
+ const dealerId = get(car, 'metaData.ownerId');
63
+ const descriptionSchema = await this.userService.getDefaultSummaryByDealerId(dealerId);
64
+ const descriptionBuilder = CarDescriptionFactory.create(DescriptionTypeEnum.Mobile, car);
65
+
66
+ return descriptionBuilder.transform(descriptionSchema);
67
+ }
68
+
69
+ private getCategory(car: DataSchemes.Car): string {
70
+ return this.translator.translateVal(get(car, 'mainData.category', ''));
71
+ }
72
+
73
+ private getBreadcrumbs(car: DataSchemes.Car): string {
74
+ const make = get(car, 'mainData.make', '');
75
+ const model = get(car, 'mainData.model', '');
76
+
77
+ return `${make} ${model}`;
78
+ }
79
+
80
+ private getLink(car: DataSchemes.Car): string {
81
+ const autodeId = get(car, '_id', '');
82
+ if (this.configService.config.isCriteo) {
83
+ return `https://www.${this.configService.config.domain}/search/vehicle/${autodeId}?source=criteo&medium=cpc&campaign=retargeting`;
84
+ }
85
+ return `https://www.${this.configService.config.domain}/search/vehicle/${autodeId}`;
86
+ }
87
+
88
+ private getRetailPrice(car: DataSchemes.Car): string {
89
+ const retailPrice = get(car, 'recommendedRetailPrice', '');
90
+ if (retailPrice) {
91
+ return `${round(retailPrice, 2)} EUR`;
92
+ }
93
+ return this.getPrice(car);
94
+ }
95
+
96
+ private getPrice(car: DataSchemes.Car): string {
97
+ const price = get(car, 'price.currentSalesPrice', 0);
98
+ return `${round(price, 2)} EUR`;
99
+ }
100
+
101
+ private getColor(car: DataSchemes.Car): string {
102
+ let colorSelection = get(car, 'exterior.exteriorColor', 'other');
103
+ if (!colorSelection || colorSelection === 'selector_unknown') {
104
+ colorSelection = 'other';
105
+ }
106
+ return this.translator.translateVal(colorSelection);
107
+ }
108
+
109
+ private mapState(car: DataSchemes.Car): string {
110
+ const available = {
111
+ selector_condition_new: 'Neu',
112
+ selector_condition_used: 'Gebraucht',
113
+ selector_unknown: 'Gebraucht',
114
+ };
115
+ const condition = get(car, 'mainData.condition', 'selector_unknown');
116
+ return get(available, condition);
117
+ }
118
+
119
+ private getAvailability(car: DataSchemes.Car): string {
120
+ const available = {
121
+ selector_availabilityMode_fromDate: 'Ab einem bestimmten Datum',
122
+ selector_availabilityMode_unknown: 'Ab einem bestimmten Datum',
123
+ selector_availabilityMode_always: 'Sofort',
124
+ };
125
+ const availability = get(car, 'offer.availabilityMode', 'selector_availabilityMode_unknown');
126
+ return get(available, availability);
127
+ }
128
+
129
+ private getEnergyClass(car: DataSchemes.Car): string {
130
+ const available = {
131
+ selector_energyEfficiencyClass_a: 'A',
132
+ selector_energyEfficiencyClass_ap: 'A+',
133
+ selector_energyEfficiencyClass_app: 'A++',
134
+ selector_energyEfficiencyClass_appp: 'A+++',
135
+ selector_energyEfficiencyClass_b: 'B',
136
+ selector_energyEfficiencyClass_c: 'C',
137
+ selector_energyEfficiencyClass_d: 'D',
138
+ selector_energyEfficiencyClass_e: 'E',
139
+ selector_energyEfficiencyClass_f: 'F',
140
+ selector_energyEfficiencyClass_g: 'G',
141
+ selector_unknown: 'default',
142
+ };
143
+ const energyClass = get(car, 'environmentEmissions.energyEfficiencyClass', 'selector_unknown');
144
+ return get(available, energyClass);
145
+ }
146
+
147
+ private getInstallment(car: DataSchemes.Car) {
148
+ const monthlyInstallment = round(get(car, 'rate.monthlyInstallment', null));
149
+ const paybackPeriod = round(get(car, 'rate.paybackPeriod', null));
150
+
151
+ return {
152
+ paybackPeriod,
153
+ monthlyInstallment,
154
+ };
155
+ }
156
+
157
+ private getBrand(car: DataSchemes.Car): string {
158
+ const make = get(car, 'mainData.make', '');
159
+
160
+ switch (make) {
161
+ case 'Mercedes-Benz':
162
+ return 'Mercedes';
163
+ case 'Volkswagen':
164
+ return 'VW';
165
+ default:
166
+ return make;
167
+ }
168
+ }
169
+ }
@@ -27,9 +27,8 @@ export class GoogleTransformer {
27
27
  }
28
28
 
29
29
  async transform({ car, images }: { car: DataSchemes.Car; images: Array<any> }): Promise<GoogleXmlItem> {
30
- if (!this.configService.config.isCriteo)
31
- if (!this.checkCarValidity(car, images))
32
- return null
30
+ if (!this.checkCarValidity(car, images))
31
+ return null
33
32
 
34
33
  const carId = toString(get(car, '_id', ''));
35
34
  const transformedImages = await this.imageService.getImages(
@@ -83,8 +82,8 @@ export class GoogleTransformer {
83
82
  }
84
83
 
85
84
  private checkCarValidity(car: DataSchemes.Car, images: ImboImageResponseInterface[]) {
86
- // if (images.length === 0)
87
- // return false
85
+ if (images.length === 0)
86
+ return false
88
87
 
89
88
  if (!this.mapBodyStyle(car))
90
89
  return false
@@ -413,7 +413,7 @@ export class MobileTransformer {
413
413
  return this.booleanToString(match);
414
414
  }
415
415
 
416
- private dateToString(fieldValue: number, rule: MappingRules): string {
416
+ protected dateToString(fieldValue: number, rule: MappingRules): string {
417
417
  const date = new Date(fieldValue * 1000);
418
418
  if (["['6']", "['7']", "['8']"].includes(rule.importField)) {
419
419
  return `${date.getMonth() + 1}.${date.getFullYear()}`;
@@ -13,6 +13,7 @@ import { isNotEmptyArray } from '../utils';
13
13
  import { ConfigService } from '../services';
14
14
  import { ExportType } from '../enums';
15
15
  import { MobileTransformer } from './mobile.transformer';
16
+ import { Car } from '@bytebrand/car-schema/dist/schema/data';
16
17
 
17
18
  const vinGenerator = require('vin-generator');
18
19
 
@@ -27,6 +28,22 @@ export class SantanderTransformer extends MobileTransformer {
27
28
  super(mappingService, userService, configService, imagesService);
28
29
  }
29
30
 
31
+ protected dateToString(fieldValue: number, rule: MappingRules): string {
32
+ const date = new Date(fieldValue * 1000);
33
+ if (rule.importField === "['8']") {
34
+ if (!fieldValue) {
35
+ const now = new Date()
36
+ const month = String(now.getMonth() + 1);
37
+ const year = now.getFullYear();
38
+ return `${month}.${year}`;
39
+ }
40
+ }
41
+ if (["['6']", "['7']", "['8']"].includes(rule.importField)) {
42
+ return `${date.getMonth() + 1}.${date.getFullYear()}`;
43
+ }
44
+ return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`;
45
+ }
46
+
30
47
  async normalizeCarData(transformedCarData: unknown) {
31
48
  const data = transformedCarData as Array<{ [key: string]: any }>;
32
49