@bytebrand/export-cars 1.26.45 → 1.26.46
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/constants/fb.constants.js +1 -1
- package/dist/constants/fb.constants.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/marktde/AUDIT.md +84 -84
- package/docs/marktde/PARTNER-QUESTIONS.md +49 -49
- package/docs/marktde/RUNBOOK.md +58 -58
- package/docs/marktde/TRACKING.md +69 -69
- package/package.json +65 -65
- package/src/configs/marktde.config.ts +70 -70
- package/src/constants/fb.constants.ts +34 -34
- package/src/exporters/marktde.exporter.ts +78 -78
- package/src/services/marktde-feed-run.ts +233 -233
- package/src/transformers/marktde.transformer.ts +255 -255
- package/test/marktde-feed-run.test.ts +263 -263
- package/test/marktde-mapping.test.ts +280 -280
package/package.json
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@bytebrand/export-cars",
|
|
3
|
-
"version": "1.26.
|
|
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 && ts-node --transpile-only test/google-feed-item-row.test.ts && ts-node --transpile-only test/publication-gate.test.ts && ts-node --transpile-only test/images-regression.test.ts && ts-node --transpile-only test/clean-images.test.ts && ts-node --transpile-only test/image-url-validator.test.ts && ts-node --transpile-only test/openai-image-fields.test.ts && ts-node --transpile-only test/openai-ads-mapping.test.ts && ts-node --transpile-only test/openai-ads-feed-run.test.ts && ts-node --transpile-only test/openai-ads-finance.test.ts && ts-node --transpile-only test/openai-ads-price-cap.test.ts && ts-node --transpile-only test/meinstadt-mapping.test.ts && ts-node --transpile-only test/meinstadt-feed-run.test.ts && ts-node --transpile-only test/autouncle-mapping.test.ts && ts-node --transpile-only test/autouncle-feed-run.test.ts && ts-node --transpile-only test/marktde-mapping.test.ts && ts-node --transpile-only test/marktde-feed-run.test.ts && ts-node --transpile-only test/criteo-images.test.ts && ts-node --transpile-only test/criteo-mapping.test.ts && ts-node --transpile-only test/criteo-feed-run.test.ts"
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@bytebrand/autode-request": "^1.19.2",
|
|
18
|
-
"@bytebrand/bigquery-logger": "^2.1.2",
|
|
19
|
-
"@bytebrand/car-basic-data-scope": "^9.11.24",
|
|
20
|
-
"@bytebrand/car-description": "^1.17.20",
|
|
21
|
-
"@bytebrand/car-schema": "^3.14.15",
|
|
22
|
-
"@bytebrand/chunkable": "^5.19.8",
|
|
23
|
-
"@bytebrand/cronicle-helper": "^1.21.8",
|
|
24
|
-
"@bytebrand/fe-financing": "^5.14.19",
|
|
25
|
-
"@bytebrand/i18n-dictionaries": "^1.5.0",
|
|
26
|
-
"@bytebrand/import-helper": "^0.45.0",
|
|
27
|
-
"@bytebrand/schema-tools": "^3.18.8",
|
|
28
|
-
"@bytebrand/zipper": "^2.2.0",
|
|
29
|
-
"@nestjs/cache-manager": "^2.1.0",
|
|
30
|
-
"@nestjs/common": "^10.2.7",
|
|
31
|
-
"@nestjs/core": "^10.2.7",
|
|
32
|
-
"adm-zip": "0.5.5",
|
|
33
|
-
"axios": "^1.5.1",
|
|
34
|
-
"bottleneck": "^2.19.5",
|
|
35
|
-
"cache-manager": "^5.2.4",
|
|
36
|
-
"csv-write-stream": "^2.0.0",
|
|
37
|
-
"google-spreadsheet": "^3.3.0",
|
|
38
|
-
"lodash": "^4.17.21",
|
|
39
|
-
"moment": "^2.29.4",
|
|
40
|
-
"nest-commander": "~3.15.0",
|
|
41
|
-
"reflect-metadata": "^0.1.13",
|
|
42
|
-
"vin-generator": "^1.0.1"
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@nestjs/cli": "^10.1.18",
|
|
46
|
-
"@types/google-spreadsheet": "^3.3.0",
|
|
47
|
-
"@types/inquirer": "^8.2.4",
|
|
48
|
-
"@types/lodash": "^4.14.186",
|
|
49
|
-
"@types/node": "^16.18.58",
|
|
50
|
-
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
|
51
|
-
"@typescript-eslint/parser": "^6.8.0",
|
|
52
|
-
"eslint": "^8.0.1",
|
|
53
|
-
"eslint-config-prettier": "^8.3.0",
|
|
54
|
-
"eslint-plugin-prettier": "^4.0.0",
|
|
55
|
-
"gts": "^3.1.1",
|
|
56
|
-
"prettier": "^2.3.2",
|
|
57
|
-
"ts-node": "^10.0.0",
|
|
58
|
-
"tsconfig-paths": "4.1.0",
|
|
59
|
-
"typescript": "^5.2.2"
|
|
60
|
-
},
|
|
61
|
-
"engines": {
|
|
62
|
-
"node": ">=14.0.0"
|
|
63
|
-
},
|
|
64
|
-
"gitHead": "e827f35865032821bf3a081f03a026d3d3f8a540"
|
|
65
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@bytebrand/export-cars",
|
|
3
|
+
"version": "1.26.46",
|
|
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 && ts-node --transpile-only test/google-feed-item-row.test.ts && ts-node --transpile-only test/publication-gate.test.ts && ts-node --transpile-only test/images-regression.test.ts && ts-node --transpile-only test/clean-images.test.ts && ts-node --transpile-only test/image-url-validator.test.ts && ts-node --transpile-only test/openai-image-fields.test.ts && ts-node --transpile-only test/openai-ads-mapping.test.ts && ts-node --transpile-only test/openai-ads-feed-run.test.ts && ts-node --transpile-only test/openai-ads-finance.test.ts && ts-node --transpile-only test/openai-ads-price-cap.test.ts && ts-node --transpile-only test/meinstadt-mapping.test.ts && ts-node --transpile-only test/meinstadt-feed-run.test.ts && ts-node --transpile-only test/autouncle-mapping.test.ts && ts-node --transpile-only test/autouncle-feed-run.test.ts && ts-node --transpile-only test/marktde-mapping.test.ts && ts-node --transpile-only test/marktde-feed-run.test.ts && ts-node --transpile-only test/criteo-images.test.ts && ts-node --transpile-only test/criteo-mapping.test.ts && ts-node --transpile-only test/criteo-feed-run.test.ts"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@bytebrand/autode-request": "^1.19.2",
|
|
18
|
+
"@bytebrand/bigquery-logger": "^2.1.2",
|
|
19
|
+
"@bytebrand/car-basic-data-scope": "^9.11.24",
|
|
20
|
+
"@bytebrand/car-description": "^1.17.20",
|
|
21
|
+
"@bytebrand/car-schema": "^3.14.15",
|
|
22
|
+
"@bytebrand/chunkable": "^5.19.8",
|
|
23
|
+
"@bytebrand/cronicle-helper": "^1.21.8",
|
|
24
|
+
"@bytebrand/fe-financing": "^5.14.19",
|
|
25
|
+
"@bytebrand/i18n-dictionaries": "^1.5.0",
|
|
26
|
+
"@bytebrand/import-helper": "^0.45.0",
|
|
27
|
+
"@bytebrand/schema-tools": "^3.18.8",
|
|
28
|
+
"@bytebrand/zipper": "^2.2.0",
|
|
29
|
+
"@nestjs/cache-manager": "^2.1.0",
|
|
30
|
+
"@nestjs/common": "^10.2.7",
|
|
31
|
+
"@nestjs/core": "^10.2.7",
|
|
32
|
+
"adm-zip": "0.5.5",
|
|
33
|
+
"axios": "^1.5.1",
|
|
34
|
+
"bottleneck": "^2.19.5",
|
|
35
|
+
"cache-manager": "^5.2.4",
|
|
36
|
+
"csv-write-stream": "^2.0.0",
|
|
37
|
+
"google-spreadsheet": "^3.3.0",
|
|
38
|
+
"lodash": "^4.17.21",
|
|
39
|
+
"moment": "^2.29.4",
|
|
40
|
+
"nest-commander": "~3.15.0",
|
|
41
|
+
"reflect-metadata": "^0.1.13",
|
|
42
|
+
"vin-generator": "^1.0.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@nestjs/cli": "^10.1.18",
|
|
46
|
+
"@types/google-spreadsheet": "^3.3.0",
|
|
47
|
+
"@types/inquirer": "^8.2.4",
|
|
48
|
+
"@types/lodash": "^4.14.186",
|
|
49
|
+
"@types/node": "^16.18.58",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
|
51
|
+
"@typescript-eslint/parser": "^6.8.0",
|
|
52
|
+
"eslint": "^8.0.1",
|
|
53
|
+
"eslint-config-prettier": "^8.3.0",
|
|
54
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
55
|
+
"gts": "^3.1.1",
|
|
56
|
+
"prettier": "^2.3.2",
|
|
57
|
+
"ts-node": "^10.0.0",
|
|
58
|
+
"tsconfig-paths": "4.1.0",
|
|
59
|
+
"typescript": "^5.2.2"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=14.0.0"
|
|
63
|
+
},
|
|
64
|
+
"gitHead": "e827f35865032821bf3a081f03a026d3d3f8a540"
|
|
65
|
+
}
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import { toNumber } from 'lodash';
|
|
2
|
-
|
|
3
|
-
import { ExportType, FileType, ImageType } from '../enums';
|
|
4
|
-
import { Params } from '../interfaces/params.interface';
|
|
5
|
-
import { FeedGuardOptions } from '../services/feed-guard';
|
|
6
|
-
import { Config } from './config';
|
|
7
|
-
|
|
8
|
-
const text = (value: unknown): string => (typeof value === 'string' ? value.trim() : '');
|
|
9
|
-
|
|
10
|
-
const positiveNumber = (value: unknown, fallback: number): number => {
|
|
11
|
-
const parsed = toNumber(text(value));
|
|
12
|
-
return text(value) && Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// Guard defaults (same as AutoUncle). The DEV job exports one dealer via the Cronicle rules, so the
|
|
16
|
-
// published count is far below the base snapshot: minPublishedItems only forbids an empty file.
|
|
17
|
-
export const MARKTDE_DEFAULTS = {
|
|
18
|
-
imageConcurrency: 16,
|
|
19
|
-
minSourceItems: 1000,
|
|
20
|
-
minPublishedItems: 1,
|
|
21
|
-
maxDropPercent: 20,
|
|
22
|
-
maxSourceAgeHours: 26,
|
|
23
|
-
maxStaleHours: 48,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* markt.de vehicle feed (ADV-328): the legacy mobile.de-style CSV (mapping sheet `mobile_yescar`,
|
|
28
|
-
* ACF-4370) plus a ZIP of it, with clean ADV-336 images. There is no current markt.de spec yet, so
|
|
29
|
-
* the format (columns, file names, no header row, `;` separator, UTF-8) stays as it was.
|
|
30
|
-
* Dry-run is the default: the files go to resources/marktde-dryrun, which no Cronicle step reads.
|
|
31
|
-
* Only marktdeDryRun=false writes resources/marktde-export (the directory `9c.1 … part 2` uploads
|
|
32
|
-
* from). Each mode has its own guard state.
|
|
33
|
-
*/
|
|
34
|
-
export class MarktdeConfig extends Config {
|
|
35
|
-
dryRun: boolean;
|
|
36
|
-
imageConcurrency: number;
|
|
37
|
-
// 0 = no limit (no partner limit known)
|
|
38
|
-
maxImages: number;
|
|
39
|
-
stateDir: string;
|
|
40
|
-
feedGuard: FeedGuardOptions;
|
|
41
|
-
|
|
42
|
-
constructor(params: Params) {
|
|
43
|
-
super(params);
|
|
44
|
-
this.campaign = ExportType.MARKTDE;
|
|
45
|
-
this.dryRun = text(params.marktdeDryRun).toLowerCase() !== 'false';
|
|
46
|
-
const mode = this.dryRun ? 'dryrun' : 'export';
|
|
47
|
-
this.pathesConfig.localConvertedDirectory = `resources/${ExportType.MARKTDE}-${mode}`;
|
|
48
|
-
this.stateDir = `resources/${ExportType.MARKTDE}-state/${mode}`;
|
|
49
|
-
this.imageUrl = `https://images.${this.domain}/carimage`;
|
|
50
|
-
this.mappingSpreadsheetTitle = 'mobile_yescar';
|
|
51
|
-
// MobileTransformer still builds its legacy `image` column; marktde.transformer replaces it with
|
|
52
|
-
// the validated clean list.
|
|
53
|
-
this.imageType = ImageType.default;
|
|
54
|
-
this.imageConcurrency = positiveNumber(params.marktdeImageConcurrency, MARKTDE_DEFAULTS.imageConcurrency);
|
|
55
|
-
this.maxImages = Math.floor(positiveNumber(params.marktdeMaxImages, 0));
|
|
56
|
-
this.feedGuard = {
|
|
57
|
-
stateDir: this.stateDir,
|
|
58
|
-
minSourceItems: positiveNumber(params.feedGuardMinSourceItems, MARKTDE_DEFAULTS.minSourceItems),
|
|
59
|
-
minPublishedItems: positiveNumber(params.feedGuardMinPublishedItems, MARKTDE_DEFAULTS.minPublishedItems),
|
|
60
|
-
maxDropPercent: positiveNumber(params.feedGuardMaxDropPercent, MARKTDE_DEFAULTS.maxDropPercent),
|
|
61
|
-
maxSourceAgeHours: positiveNumber(params.feedGuardMaxSourceAgeHours, MARKTDE_DEFAULTS.maxSourceAgeHours),
|
|
62
|
-
maxStaleHours: positiveNumber(params.feedGuardMaxStaleHours, MARKTDE_DEFAULTS.maxStaleHours),
|
|
63
|
-
acceptDrop: text(params.feedGuardAcceptDrop).toLowerCase() === 'true',
|
|
64
|
-
};
|
|
65
|
-
this.outputFiles = [
|
|
66
|
-
`${this.pathesConfig.localConvertedDirectory}/api_export_cars_${ExportType.MARKTDE}.${FileType.CSV}`,
|
|
67
|
-
`${this.pathesConfig.localConvertedDirectory}/api_export_cars_${ExportType.MARKTDE}.${FileType.ZIP}`,
|
|
68
|
-
];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
1
|
+
import { toNumber } from 'lodash';
|
|
2
|
+
|
|
3
|
+
import { ExportType, FileType, ImageType } from '../enums';
|
|
4
|
+
import { Params } from '../interfaces/params.interface';
|
|
5
|
+
import { FeedGuardOptions } from '../services/feed-guard';
|
|
6
|
+
import { Config } from './config';
|
|
7
|
+
|
|
8
|
+
const text = (value: unknown): string => (typeof value === 'string' ? value.trim() : '');
|
|
9
|
+
|
|
10
|
+
const positiveNumber = (value: unknown, fallback: number): number => {
|
|
11
|
+
const parsed = toNumber(text(value));
|
|
12
|
+
return text(value) && Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Guard defaults (same as AutoUncle). The DEV job exports one dealer via the Cronicle rules, so the
|
|
16
|
+
// published count is far below the base snapshot: minPublishedItems only forbids an empty file.
|
|
17
|
+
export const MARKTDE_DEFAULTS = {
|
|
18
|
+
imageConcurrency: 16,
|
|
19
|
+
minSourceItems: 1000,
|
|
20
|
+
minPublishedItems: 1,
|
|
21
|
+
maxDropPercent: 20,
|
|
22
|
+
maxSourceAgeHours: 26,
|
|
23
|
+
maxStaleHours: 48,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* markt.de vehicle feed (ADV-328): the legacy mobile.de-style CSV (mapping sheet `mobile_yescar`,
|
|
28
|
+
* ACF-4370) plus a ZIP of it, with clean ADV-336 images. There is no current markt.de spec yet, so
|
|
29
|
+
* the format (columns, file names, no header row, `;` separator, UTF-8) stays as it was.
|
|
30
|
+
* Dry-run is the default: the files go to resources/marktde-dryrun, which no Cronicle step reads.
|
|
31
|
+
* Only marktdeDryRun=false writes resources/marktde-export (the directory `9c.1 … part 2` uploads
|
|
32
|
+
* from). Each mode has its own guard state.
|
|
33
|
+
*/
|
|
34
|
+
export class MarktdeConfig extends Config {
|
|
35
|
+
dryRun: boolean;
|
|
36
|
+
imageConcurrency: number;
|
|
37
|
+
// 0 = no limit (no partner limit known)
|
|
38
|
+
maxImages: number;
|
|
39
|
+
stateDir: string;
|
|
40
|
+
feedGuard: FeedGuardOptions;
|
|
41
|
+
|
|
42
|
+
constructor(params: Params) {
|
|
43
|
+
super(params);
|
|
44
|
+
this.campaign = ExportType.MARKTDE;
|
|
45
|
+
this.dryRun = text(params.marktdeDryRun).toLowerCase() !== 'false';
|
|
46
|
+
const mode = this.dryRun ? 'dryrun' : 'export';
|
|
47
|
+
this.pathesConfig.localConvertedDirectory = `resources/${ExportType.MARKTDE}-${mode}`;
|
|
48
|
+
this.stateDir = `resources/${ExportType.MARKTDE}-state/${mode}`;
|
|
49
|
+
this.imageUrl = `https://images.${this.domain}/carimage`;
|
|
50
|
+
this.mappingSpreadsheetTitle = 'mobile_yescar';
|
|
51
|
+
// MobileTransformer still builds its legacy `image` column; marktde.transformer replaces it with
|
|
52
|
+
// the validated clean list.
|
|
53
|
+
this.imageType = ImageType.default;
|
|
54
|
+
this.imageConcurrency = positiveNumber(params.marktdeImageConcurrency, MARKTDE_DEFAULTS.imageConcurrency);
|
|
55
|
+
this.maxImages = Math.floor(positiveNumber(params.marktdeMaxImages, 0));
|
|
56
|
+
this.feedGuard = {
|
|
57
|
+
stateDir: this.stateDir,
|
|
58
|
+
minSourceItems: positiveNumber(params.feedGuardMinSourceItems, MARKTDE_DEFAULTS.minSourceItems),
|
|
59
|
+
minPublishedItems: positiveNumber(params.feedGuardMinPublishedItems, MARKTDE_DEFAULTS.minPublishedItems),
|
|
60
|
+
maxDropPercent: positiveNumber(params.feedGuardMaxDropPercent, MARKTDE_DEFAULTS.maxDropPercent),
|
|
61
|
+
maxSourceAgeHours: positiveNumber(params.feedGuardMaxSourceAgeHours, MARKTDE_DEFAULTS.maxSourceAgeHours),
|
|
62
|
+
maxStaleHours: positiveNumber(params.feedGuardMaxStaleHours, MARKTDE_DEFAULTS.maxStaleHours),
|
|
63
|
+
acceptDrop: text(params.feedGuardAcceptDrop).toLowerCase() === 'true',
|
|
64
|
+
};
|
|
65
|
+
this.outputFiles = [
|
|
66
|
+
`${this.pathesConfig.localConvertedDirectory}/api_export_cars_${ExportType.MARKTDE}.${FileType.CSV}`,
|
|
67
|
+
`${this.pathesConfig.localConvertedDirectory}/api_export_cars_${ExportType.MARKTDE}.${FileType.ZIP}`,
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
export const FB_MAX_TRIM_LENGTH = 50;
|
|
2
|
-
export const FB_MAX_IMAGE_COUNT_FOR_DEAFULT = 47;
|
|
3
|
-
export const FB_MIN_MILEAGES = 900;
|
|
4
|
-
export const DEFAULT_ZIP = '06796';
|
|
5
|
-
export const DEFAULT_CITY = 'Sandersdorf-Brehna';
|
|
6
|
-
export const DEFAULT_ADDR1 = 'Max-Planck-Str. 19';
|
|
7
|
-
export const TIMESTAMP_FIRST_YEAR = 1970;
|
|
8
|
-
export const META_PUSH_PRIORITY_STANDARD = 'standard';
|
|
9
|
-
export const META_PUSH_PRIORITY_ALLOWED_VALUES = ['dealer_priority_1', 'dealer_priority_2'];
|
|
10
|
-
|
|
11
|
-
// MarketingImageExportConfig channel code for the Meta AIA overlay images (image-generator's
|
|
12
|
-
// MetaAiaImagesService.CHANNEL_CODE) — distinct from this feed's now-disabled legacy "fb" config.
|
|
13
|
-
export const META_AIA_CHANNEL_CODE = 'meta_aia';
|
|
14
|
-
// Only the financing variant exists so far; leasing/other modes will be added once image-generator
|
|
15
|
-
// renders them too (see MetaAiaImagesService's MetaAiaMode).
|
|
16
|
-
export const META_AIA_MODE_FINANCING = 'financing';
|
|
17
|
-
|
|
18
|
-
// Values must match Meta's Automotive Model Ads offer_type enum exactly (lease/finance/cash) —
|
|
19
|
-
// the old 'financing'/'leasing'/'buy' values are not recognized by the AMA Vehicle Offers Feed.
|
|
20
|
-
export enum OfferType {
|
|
21
|
-
financing = 'finance',
|
|
22
|
-
leasing = 'lease',
|
|
23
|
-
buy = 'cash',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const AMOUNT_QUALIFIER_APR = 'APR';
|
|
27
|
-
export const TERM_QUALIFIER_MONTHS = 'months';
|
|
28
|
-
|
|
29
|
-
export const OFFER_DISCLAIMER = 'Für Ihre Finanzierungswünsche stellen wir eine Bonitätsanfrage. Bonität ' +
|
|
30
|
-
'vorausgesetzt, ist dies ein repräsentatives Berechnungsbeispiel gem. der Angaben, welches 2/3 aller Kunden, ' +
|
|
31
|
-
'im Sinne des § 17a Abs. 4 PAngV, erhalten. Dieses freibleibende Angebot der
|
|
32
|
-
'Santander-Platz 1, 41061 Mönchengladbach wird vermittelt durch die auto.de GmbH, Max-Planck-Str. 19, 06796 ' +
|
|
33
|
-
'Sandersdorf-Brehna, die als ungebundener Vermittler nicht beratend tätig ist. Irrtümer vorbehalten. Preise ' +
|
|
34
|
-
'ggf. inkl. MwSt.';
|
|
1
|
+
export const FB_MAX_TRIM_LENGTH = 50;
|
|
2
|
+
export const FB_MAX_IMAGE_COUNT_FOR_DEAFULT = 47;
|
|
3
|
+
export const FB_MIN_MILEAGES = 900;
|
|
4
|
+
export const DEFAULT_ZIP = '06796';
|
|
5
|
+
export const DEFAULT_CITY = 'Sandersdorf-Brehna';
|
|
6
|
+
export const DEFAULT_ADDR1 = 'Max-Planck-Str. 19';
|
|
7
|
+
export const TIMESTAMP_FIRST_YEAR = 1970;
|
|
8
|
+
export const META_PUSH_PRIORITY_STANDARD = 'standard';
|
|
9
|
+
export const META_PUSH_PRIORITY_ALLOWED_VALUES = ['dealer_priority_1', 'dealer_priority_2'];
|
|
10
|
+
|
|
11
|
+
// MarketingImageExportConfig channel code for the Meta AIA overlay images (image-generator's
|
|
12
|
+
// MetaAiaImagesService.CHANNEL_CODE) — distinct from this feed's now-disabled legacy "fb" config.
|
|
13
|
+
export const META_AIA_CHANNEL_CODE = 'meta_aia';
|
|
14
|
+
// Only the financing variant exists so far; leasing/other modes will be added once image-generator
|
|
15
|
+
// renders them too (see MetaAiaImagesService's MetaAiaMode).
|
|
16
|
+
export const META_AIA_MODE_FINANCING = 'financing';
|
|
17
|
+
|
|
18
|
+
// Values must match Meta's Automotive Model Ads offer_type enum exactly (lease/finance/cash) —
|
|
19
|
+
// the old 'financing'/'leasing'/'buy' values are not recognized by the AMA Vehicle Offers Feed.
|
|
20
|
+
export enum OfferType {
|
|
21
|
+
financing = 'finance',
|
|
22
|
+
leasing = 'lease',
|
|
23
|
+
buy = 'cash',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const AMOUNT_QUALIFIER_APR = 'APR';
|
|
27
|
+
export const TERM_QUALIFIER_MONTHS = 'months';
|
|
28
|
+
|
|
29
|
+
export const OFFER_DISCLAIMER = 'Für Ihre Finanzierungswünsche stellen wir eine Bonitätsanfrage. Bonität ' +
|
|
30
|
+
'vorausgesetzt, ist dies ein repräsentatives Berechnungsbeispiel gem. der Angaben, welches 2/3 aller Kunden, ' +
|
|
31
|
+
'im Sinne des § 17a Abs. 4 PAngV, erhalten. Dieses freibleibende Angebot der Openbank Deutschland AG, ' +
|
|
32
|
+
'Santander-Platz 1, 41061 Mönchengladbach wird vermittelt durch die auto.de GmbH, Max-Planck-Str. 19, 06796 ' +
|
|
33
|
+
'Sandersdorf-Brehna, die als ungebundener Vermittler nicht beratend tätig ist. Irrtümer vorbehalten. Preise ' +
|
|
34
|
+
'ggf. inkl. MwSt.';
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
2
|
-
import { ModuleRef } from '@nestjs/core';
|
|
3
|
-
|
|
4
|
-
import { FsChunkInterface } from '@bytebrand/chunkable';
|
|
5
|
-
import { LoggerService } from '@bytebrand/cronicle-helper';
|
|
6
|
-
|
|
7
|
-
import { CarService, RulesService, LoaderService, ConfigService, ImagesService } from '../services';
|
|
8
|
-
import { ImageUrlValidator } from '../services/image-url-validator.service';
|
|
9
|
-
import { FeedGuard } from '../services/feed-guard';
|
|
10
|
-
import { MarktdeFeedRun, MarktdeSourceCar } from '../services/marktde-feed-run';
|
|
11
|
-
import { zipSingleFile } from '../services/meinstadt-feed-run';
|
|
12
|
-
import { MobileTransformer } from '../transformers/mobile.transformer';
|
|
13
|
-
import { toMarktdeImages } from '../transformers/marktde.transformer';
|
|
14
|
-
import { resolveOpenAiImageFields } from '../transformers/openai-image-fields';
|
|
15
|
-
import { MarktdeConfig } from '../configs/marktde.config';
|
|
16
|
-
import { Exporter } from '../interfaces/exporter.interface';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* ExportType.MARKTDE (ADV-328). Was the shared MobileExporter; now wires the Cronicle/Nest services
|
|
20
|
-
* into MarktdeFeedRun, which holds the logic. The row itself still comes from MobileTransformer (same
|
|
21
|
-
* mapping sheet, same columns); only the markt.de columns are replaced. EAUTO / YESCAR / EVIUM keep
|
|
22
|
-
* the MobileExporter unchanged.
|
|
23
|
-
*/
|
|
24
|
-
@Injectable()
|
|
25
|
-
export class MarktdeExporter implements Exporter {
|
|
26
|
-
constructor(
|
|
27
|
-
private carService: CarService,
|
|
28
|
-
private configService: ConfigService,
|
|
29
|
-
private rulesService: RulesService,
|
|
30
|
-
private loaderService: LoaderService,
|
|
31
|
-
private imagesService: ImagesService,
|
|
32
|
-
private moduleRef: ModuleRef,
|
|
33
|
-
) {}
|
|
34
|
-
|
|
35
|
-
async export(): Promise<void> {
|
|
36
|
-
const config = this.configService.config as MarktdeConfig;
|
|
37
|
-
const [csvFile, zipFile] = config.outputFiles;
|
|
38
|
-
const validator = new ImageUrlValidator({ concurrency: config.imageConcurrency });
|
|
39
|
-
const transformer = await this.moduleRef.create(MobileTransformer);
|
|
40
|
-
|
|
41
|
-
const run = new MarktdeFeedRun(
|
|
42
|
-
{
|
|
43
|
-
forEachChunk: fn => this.loaderService.process((chunk: FsChunkInterface) => fn(chunk.data as MarktdeSourceCar[])),
|
|
44
|
-
filterCar: source => this.carService.filterCar(source, this.rulesService.rules),
|
|
45
|
-
resolveImages: async ({ car, images }) => toMarktdeImages(
|
|
46
|
-
await resolveOpenAiImageFields(this.imagesService.getCleanImages(car._id, images), validator),
|
|
47
|
-
config.maxImages,
|
|
48
|
-
),
|
|
49
|
-
legacyRow: async ({ car, images }) => (await transformer.transform(car, images)) as Record<string, unknown>,
|
|
50
|
-
imageStats: () => validator.stats,
|
|
51
|
-
zip: zipSingleFile,
|
|
52
|
-
guard: new FeedGuard(config.feedGuard),
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
runId: config.runId,
|
|
56
|
-
dryRun: config.dryRun,
|
|
57
|
-
domain: config.domain,
|
|
58
|
-
sourceDirectory: config.pathesConfig.localDirectory,
|
|
59
|
-
sourceBaseFileName: config.pathesConfig.baseFileName,
|
|
60
|
-
csvFile,
|
|
61
|
-
zipFile,
|
|
62
|
-
stateDir: config.stateDir,
|
|
63
|
-
},
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const report = await run.run();
|
|
68
|
-
if (report.stale) {
|
|
69
|
-
console.log(`marktde feed STALE: last good file ${report.lastGoodAgeHours?.toFixed(1)} h old`);
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
// main.ts would report a rejected run as {"complete":1,"code":0}; fail the job so the chained
|
|
73
|
-
// upload step does not run (report + lock already handled by the run).
|
|
74
|
-
LoggerService.exitError(`marktde feed not published: ${error?.message ?? error}`);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
|
|
4
|
+
import { FsChunkInterface } from '@bytebrand/chunkable';
|
|
5
|
+
import { LoggerService } from '@bytebrand/cronicle-helper';
|
|
6
|
+
|
|
7
|
+
import { CarService, RulesService, LoaderService, ConfigService, ImagesService } from '../services';
|
|
8
|
+
import { ImageUrlValidator } from '../services/image-url-validator.service';
|
|
9
|
+
import { FeedGuard } from '../services/feed-guard';
|
|
10
|
+
import { MarktdeFeedRun, MarktdeSourceCar } from '../services/marktde-feed-run';
|
|
11
|
+
import { zipSingleFile } from '../services/meinstadt-feed-run';
|
|
12
|
+
import { MobileTransformer } from '../transformers/mobile.transformer';
|
|
13
|
+
import { toMarktdeImages } from '../transformers/marktde.transformer';
|
|
14
|
+
import { resolveOpenAiImageFields } from '../transformers/openai-image-fields';
|
|
15
|
+
import { MarktdeConfig } from '../configs/marktde.config';
|
|
16
|
+
import { Exporter } from '../interfaces/exporter.interface';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ExportType.MARKTDE (ADV-328). Was the shared MobileExporter; now wires the Cronicle/Nest services
|
|
20
|
+
* into MarktdeFeedRun, which holds the logic. The row itself still comes from MobileTransformer (same
|
|
21
|
+
* mapping sheet, same columns); only the markt.de columns are replaced. EAUTO / YESCAR / EVIUM keep
|
|
22
|
+
* the MobileExporter unchanged.
|
|
23
|
+
*/
|
|
24
|
+
@Injectable()
|
|
25
|
+
export class MarktdeExporter implements Exporter {
|
|
26
|
+
constructor(
|
|
27
|
+
private carService: CarService,
|
|
28
|
+
private configService: ConfigService,
|
|
29
|
+
private rulesService: RulesService,
|
|
30
|
+
private loaderService: LoaderService,
|
|
31
|
+
private imagesService: ImagesService,
|
|
32
|
+
private moduleRef: ModuleRef,
|
|
33
|
+
) {}
|
|
34
|
+
|
|
35
|
+
async export(): Promise<void> {
|
|
36
|
+
const config = this.configService.config as MarktdeConfig;
|
|
37
|
+
const [csvFile, zipFile] = config.outputFiles;
|
|
38
|
+
const validator = new ImageUrlValidator({ concurrency: config.imageConcurrency });
|
|
39
|
+
const transformer = await this.moduleRef.create(MobileTransformer);
|
|
40
|
+
|
|
41
|
+
const run = new MarktdeFeedRun(
|
|
42
|
+
{
|
|
43
|
+
forEachChunk: fn => this.loaderService.process((chunk: FsChunkInterface) => fn(chunk.data as MarktdeSourceCar[])),
|
|
44
|
+
filterCar: source => this.carService.filterCar(source, this.rulesService.rules),
|
|
45
|
+
resolveImages: async ({ car, images }) => toMarktdeImages(
|
|
46
|
+
await resolveOpenAiImageFields(this.imagesService.getCleanImages(car._id, images), validator),
|
|
47
|
+
config.maxImages,
|
|
48
|
+
),
|
|
49
|
+
legacyRow: async ({ car, images }) => (await transformer.transform(car, images)) as Record<string, unknown>,
|
|
50
|
+
imageStats: () => validator.stats,
|
|
51
|
+
zip: zipSingleFile,
|
|
52
|
+
guard: new FeedGuard(config.feedGuard),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
runId: config.runId,
|
|
56
|
+
dryRun: config.dryRun,
|
|
57
|
+
domain: config.domain,
|
|
58
|
+
sourceDirectory: config.pathesConfig.localDirectory,
|
|
59
|
+
sourceBaseFileName: config.pathesConfig.baseFileName,
|
|
60
|
+
csvFile,
|
|
61
|
+
zipFile,
|
|
62
|
+
stateDir: config.stateDir,
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const report = await run.run();
|
|
68
|
+
if (report.stale) {
|
|
69
|
+
console.log(`marktde feed STALE: last good file ${report.lastGoodAgeHours?.toFixed(1)} h old`);
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
// main.ts would report a rejected run as {"complete":1,"code":0}; fail the job so the chained
|
|
73
|
+
// upload step does not run (report + lock already handled by the run).
|
|
74
|
+
LoggerService.exitError(`marktde feed not published: ${error?.message ?? error}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|