@bytebrand/export-cars 1.21.34 → 1.21.35
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/export.schema.d.ts +2 -2
- package/dist/export.schema.js +1 -1
- package/dist/export.schema.js.map +1 -1
- package/dist/exporters/criteo.exporter.d.ts +12 -0
- package/dist/exporters/criteo.exporter.js +61 -0
- package/dist/exporters/criteo.exporter.js.map +1 -0
- package/dist/exporters/index.d.ts +1 -0
- package/dist/exporters/index.js +1 -0
- package/dist/exporters/index.js.map +1 -1
- package/dist/formats/google-xml.builder.d.ts +2 -2
- package/dist/formats/google-xml.builder.js.map +1 -1
- package/dist/interfaces/export-item.interface.d.ts +21 -0
- package/dist/transformers/criteo.transformer.d.ts +27 -0
- package/dist/transformers/criteo.transformer.js +161 -0
- package/dist/transformers/criteo.transformer.js.map +1 -0
- package/dist/transformers/google.transformer.js +4 -3
- package/dist/transformers/google.transformer.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/export.schema.ts +2 -1
- package/src/exporters/criteo.exporter.ts +46 -0
- package/src/exporters/index.ts +1 -0
- package/src/formats/google-xml.builder.ts +2 -2
- package/src/interfaces/export-item.interface.ts +19 -0
- package/src/transformers/criteo.transformer.ts +169 -0
- package/src/transformers/google.transformer.ts +4 -5
package/package.json
CHANGED
package/src/export.schema.ts
CHANGED
|
@@ -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:
|
|
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
|
+
}
|
package/src/exporters/index.ts
CHANGED
|
@@ -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';
|
|
@@ -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.
|
|
31
|
-
|
|
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
|
-
|
|
87
|
-
|
|
85
|
+
if (images.length === 0)
|
|
86
|
+
return false
|
|
88
87
|
|
|
89
88
|
if (!this.mapBodyStyle(car))
|
|
90
89
|
return false
|