@factorypure/client-helpers 1.0.14 → 1.0.15

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/index.d.ts CHANGED
@@ -28,6 +28,24 @@ export type FilterOptionsType = {
28
28
  search_exclusions_enabled?: boolean;
29
29
  exclude_ignored_keys_enabled?: boolean;
30
30
  };
31
+ export declare const HIDE_REASONS: {
32
+ IGNORED: string;
33
+ HIGH_PRICE_OUTLIER: string;
34
+ LOW_PRICE_OUTLIER: string;
35
+ DATE_OUTLIER: string;
36
+ COMPETITOR_EXCLUSION: string;
37
+ DUPLICATE: string;
38
+ SEARCH_EXCLUSION: string;
39
+ CALCULATED_SKU_MISMATCH: string;
40
+ CRITICAL_SPEC_MISMATCH: string;
41
+ MANUALLY_IGNORED: string;
42
+ OUT_OF_STOCK_ONLINE: string;
43
+ REFURBISHED_USED: string;
44
+ SKIP_SKU: string;
45
+ VENDOR_EXCLUSION: string;
46
+ };
47
+ export declare const TOO_CHEAP_MULTIPLIER = 0.75;
48
+ export declare const TOO_EXPENSIVE_MULTIPLIER = 1.25;
31
49
  export declare const filterScrapeResults: ({ scrapeResults, variant, filters, filterOptions, }: {
32
50
  scrapeResults: ScrapeResultsType[];
33
51
  variant: {
package/dist/index.js CHANGED
@@ -37,8 +37,24 @@ export const scrapeResultsSchema = z.object({
37
37
  store_id: z.number(),
38
38
  match_score: z.number().nullable(),
39
39
  });
40
- const TOO_CHEAP_MULTIPLIER = 0.75;
41
- const TOO_EXPENSIVE_MULTIPLIER = 1.25;
40
+ export const HIDE_REASONS = {
41
+ IGNORED: "Ignored",
42
+ HIGH_PRICE_OUTLIER: "Too Expensive",
43
+ LOW_PRICE_OUTLIER: "Too Cheap",
44
+ DATE_OUTLIER: "Outdated Listing",
45
+ COMPETITOR_EXCLUSION: "Source Excluded",
46
+ DUPLICATE: "Duplicate",
47
+ SEARCH_EXCLUSION: "Search Exclusion",
48
+ CALCULATED_SKU_MISMATCH: "Calculated SKU Mismatch",
49
+ CRITICAL_SPEC_MISMATCH: "Critical Spec Mismatch",
50
+ MANUALLY_IGNORED: "Manually ignored",
51
+ OUT_OF_STOCK_ONLINE: "Out of stock online",
52
+ REFURBISHED_USED: "Refurbished/Used",
53
+ SKIP_SKU: "Skipped SKU",
54
+ VENDOR_EXCLUSION: "Vendor Exclusion",
55
+ };
56
+ export const TOO_CHEAP_MULTIPLIER = 0.75;
57
+ export const TOO_EXPENSIVE_MULTIPLIER = 1.25;
42
58
  const wattages = Array.from({ length: 41 }, (_, i) => (5000 + i * 500).toString());
43
59
  export const filterScrapeResults = ({ scrapeResults, variant, filters, filterOptions = {
44
60
  exclude_linked_variants_enabled: true,
@@ -54,30 +70,33 @@ export const filterScrapeResults = ({ scrapeResults, variant, filters, filterOpt
54
70
  exclude_ignored_keys_enabled: false,
55
71
  }, }) => {
56
72
  let filteredResults = scrapeResults;
57
- if (filterOptions.exclude_linked_variants_enabled) {
58
- filteredResults = filterLinkedElsewhereResults(filteredResults, variant.id);
59
- }
60
- if (filterOptions.found_id_exclusions_enabled) {
61
- filteredResults = filterFoundProductIdExclusions(filteredResults, filters.found_id_exclusions);
62
- }
63
- if (filterOptions.exclude_ignored_keys_enabled) {
64
- filteredResults = filterIgnoredResults(filteredResults);
65
- }
66
- if (filterOptions.price_filter_enabled) {
67
- filteredResults = filterPriceOutliers(filteredResults, variant.price, filters.showHighPriceOutliers, filters.showLowPriceOutliers);
68
- }
69
- if (filterOptions.date_filter_enabled) {
70
- filteredResults = filterDateWindow(filteredResults, filters.dayWindow);
71
- }
72
- if (filterOptions.competitor_filter_enabled) {
73
- filteredResults = filterCompetitors(filteredResults, filters.competitor_exclusions);
74
- }
75
- if (filterOptions.duplicates_filter_enabled) {
76
- filteredResults = filterDuplicateResults(filteredResults);
77
- }
78
- if (filterOptions.search_index_enabled) {
79
- filteredResults = handleIndexSearch(filteredResults, filters, variant, filterOptions);
80
- }
73
+ // if (filterOptions.exclude_linked_variants_enabled) {
74
+ // filteredResults = filterLinkedElsewhereResults(filteredResults, variant.id)
75
+ // }
76
+ // if (filterOptions.found_id_exclusions_enabled) {
77
+ // filteredResults = filterFoundProductIdExclusions(filteredResults, filters.found_id_exclusions)
78
+ // }
79
+ // if (filterOptions.exclude_ignored_keys_enabled) {
80
+ // filteredResults = filterIgnoredResults(filteredResults)
81
+ // }
82
+ // if (filterOptions.price_filter_enabled) {
83
+ filteredResults = filterPriceOutliers(filteredResults, variant.price, filters.showHighPriceOutliers, filters.showLowPriceOutliers);
84
+ // }
85
+ // if (filterOptions.date_filter_enabled) {
86
+ filteredResults = filterDateWindow(filteredResults, filters.dayWindow);
87
+ // }
88
+ // if (filterOptions.competitor_filter_enabled) {
89
+ filteredResults = filterCompetitors(filteredResults, filters.competitor_exclusions);
90
+ // }
91
+ // if (filterOptions.duplicates_filter_enabled) {
92
+ filteredResults = filterDuplicateResults(filteredResults);
93
+ // }
94
+ // if (filterOptions.search_index_enabled) {
95
+ filteredResults = handleExclusionsSearch(filteredResults, filters, variant, filterOptions);
96
+ filteredResults = handleSkipSkuSearch(filteredResults, filters, variant, filterOptions);
97
+ filteredResults = handleVendorExclusions(filteredResults, filters, variant, filterOptions);
98
+ // filteredResults = handleIndexSearch(filteredResults, filters, variant, filterOptions)
99
+ // }
81
100
  return filteredResults;
82
101
  };
83
102
  const handleIndexSearch = (dataToSearch, filters, variant, filterOptions) => {
@@ -104,6 +123,29 @@ const handleIndexSearch = (dataToSearch, filters, variant, filterOptions) => {
104
123
  index.add(id, item.title);
105
124
  });
106
125
  const searchTerms = filters.match_values;
126
+ let final = null;
127
+ searchTerms.forEach((term) => {
128
+ if (final === null) {
129
+ final = index.search(term, {
130
+ resolve: false,
131
+ suggest: true,
132
+ });
133
+ }
134
+ else {
135
+ final = final.or({
136
+ index: index,
137
+ query: term,
138
+ resolve: false,
139
+ suggest: true,
140
+ });
141
+ }
142
+ });
143
+ final = final.and({
144
+ index: index,
145
+ query: sku,
146
+ resolve: false,
147
+ suggest: true,
148
+ });
107
149
  const nots = [];
108
150
  if (filters.skip_skus.length && filterOptions.skip_skus_enabled) {
109
151
  const formatted = filters.skip_skus
@@ -142,10 +184,221 @@ const handleIndexSearch = (dataToSearch, filters, variant, filterOptions) => {
142
184
  if (title.toLowerCase().includes("dual fuel")) {
143
185
  // nots.push('tri fuel', 'trifuel', 'tri-fuel')
144
186
  }
187
+ nots.forEach((term) => {
188
+ final = final.not({
189
+ index: index,
190
+ query: term,
191
+ resolve: false,
192
+ });
193
+ });
194
+ const result = final.resolve({ limit: 1000 });
195
+ const resultsArray = [];
196
+ result.forEach((i) => {
197
+ resultsArray.push(dataToSearch[i]);
198
+ });
199
+ for (let index = 0; index < dataToSearch.length; index++) {
200
+ const element = dataToSearch[index];
201
+ if (!result.includes(index)) {
202
+ if (!element.hide_reasons) {
203
+ element.hide_reasons = [];
204
+ }
205
+ element.hide_reasons.push(HIDE_REASONS.SEARCH_EXCLUSION);
206
+ }
207
+ }
208
+ return dataToSearch;
209
+ };
210
+ const handleVendorExclusions = (dataToSearch, filters, variant, filterOptions) => {
211
+ if (!filters.skip_vendors || filters.skip_vendors.length === 0) {
212
+ return dataToSearch;
213
+ }
214
+ const { title, sku } = variant;
215
+ function customEncoder(content) {
216
+ const tokens = [];
217
+ const str = content.toLowerCase();
218
+ // Remove symbols from the string (keep only letters, numbers, commas, and spaces)
219
+ const cleanedStr = str.replace(/[\/-]/g, " ");
220
+ const cleanedStr2 = cleanedStr.replace(/[^a-z0-9,\/\s]/gi, "");
221
+ const words = cleanedStr2.split(/\s+/);
222
+ for (let word of words) {
223
+ tokens.push(word);
224
+ }
225
+ return tokens;
226
+ }
227
+ const index = new Index({
228
+ charset: EnglishPreset,
229
+ // encoder: encoder,
230
+ encode: customEncoder,
231
+ tokenize: "strict",
232
+ });
233
+ dataToSearch.forEach((item, id) => {
234
+ index.add(id, item.title);
235
+ });
236
+ const searchTerms = filters.match_values;
145
237
  let final = null;
146
- let urlFinal = null;
147
238
  searchTerms.forEach((term) => {
148
- if (final === null && urlFinal === null) {
239
+ if (final === null) {
240
+ final = index.search(term, {
241
+ resolve: false,
242
+ suggest: true,
243
+ });
244
+ }
245
+ else {
246
+ final = final.or({
247
+ index: index,
248
+ query: term,
249
+ resolve: false,
250
+ suggest: true,
251
+ });
252
+ }
253
+ });
254
+ final = final.and({
255
+ index: index,
256
+ query: sku,
257
+ resolve: false,
258
+ suggest: true,
259
+ });
260
+ const nots = [];
261
+ if (filters.skip_vendors.length && filterOptions.skip_vendors_enabled) {
262
+ const formatted = filters.skip_vendors
263
+ .filter((vendor) => vendor.toLowerCase() !== variant.vendor.toLowerCase())
264
+ .map((vendor) => ` ${vendor} `);
265
+ nots.push(...formatted);
266
+ }
267
+ nots.forEach((term) => {
268
+ final = final.not({
269
+ index: index,
270
+ query: term,
271
+ resolve: false,
272
+ });
273
+ });
274
+ const result = final.resolve({ limit: 1000 });
275
+ const resultsArray = [];
276
+ result.forEach((i) => {
277
+ resultsArray.push(dataToSearch[i]);
278
+ });
279
+ for (let index = 0; index < dataToSearch.length; index++) {
280
+ const element = dataToSearch[index];
281
+ if (!result.includes(index)) {
282
+ if (!element.hide_reasons) {
283
+ element.hide_reasons = [];
284
+ }
285
+ element.hide_reasons.push(HIDE_REASONS.VENDOR_EXCLUSION);
286
+ }
287
+ }
288
+ return dataToSearch;
289
+ };
290
+ const handleSkipSkuSearch = (dataToSearch, filters, variant, filterOptions) => {
291
+ // if (
292
+ // (!filters.skip_skus || filters.skip_skus.length === 0) &&
293
+ // (!filters.vendor_search_exclusions || filters.vendor_search_exclusions.length === 0)
294
+ // ) {
295
+ // return dataToSearch
296
+ // }
297
+ const { title, sku } = variant;
298
+ function customEncoder(content) {
299
+ const tokens = [];
300
+ const str = content.toLowerCase();
301
+ // Remove symbols from the string (keep only letters, numbers, commas, and spaces)
302
+ const cleanedStr = str.replace(/[\/-]/g, " ");
303
+ const cleanedStr2 = cleanedStr.replace(/[^a-z0-9,\/\s]/gi, "");
304
+ const words = cleanedStr2.split(/\s+/);
305
+ for (let word of words) {
306
+ tokens.push(word);
307
+ }
308
+ return tokens;
309
+ }
310
+ const index = new Index({
311
+ charset: EnglishPreset,
312
+ // encoder: encoder,
313
+ encode: customEncoder,
314
+ tokenize: "strict",
315
+ });
316
+ dataToSearch.forEach((item, id) => {
317
+ index.add(id, item.title);
318
+ });
319
+ const searchTerms = filters.match_values;
320
+ let final = null;
321
+ searchTerms.forEach((term) => {
322
+ if (final === null) {
323
+ final = index.search(term, {
324
+ resolve: false,
325
+ suggest: true,
326
+ });
327
+ }
328
+ else {
329
+ final = final.or({
330
+ index: index,
331
+ query: term,
332
+ resolve: false,
333
+ suggest: true,
334
+ });
335
+ }
336
+ });
337
+ final = final.and({
338
+ index: index,
339
+ query: sku,
340
+ resolve: false,
341
+ suggest: true,
342
+ });
343
+ const nots = [];
344
+ const formatted = filters.skip_skus
345
+ .filter((sku) => sku.toLowerCase() !== variant.sku.toLowerCase())
346
+ .map((sku) => ` ${sku} `);
347
+ nots.push(...formatted);
348
+ nots.push(...filters.vendor_search_exclusions.filter((sku) => sku.toLowerCase() !== variant.sku.toLowerCase()));
349
+ nots.forEach((term) => {
350
+ final = final.not({
351
+ index: index,
352
+ query: term,
353
+ resolve: false,
354
+ });
355
+ });
356
+ const result = final.resolve({ limit: 1000 });
357
+ const resultsArray = [];
358
+ result.forEach((i) => {
359
+ resultsArray.push(dataToSearch[i]);
360
+ });
361
+ for (let index = 0; index < dataToSearch.length; index++) {
362
+ const element = dataToSearch[index];
363
+ if (!result.includes(index)) {
364
+ if (!element.hide_reasons) {
365
+ element.hide_reasons = [];
366
+ }
367
+ element.hide_reasons.push(HIDE_REASONS.SKIP_SKU);
368
+ }
369
+ }
370
+ return dataToSearch;
371
+ };
372
+ const handleExclusionsSearch = (dataToSearch, filters, variant, filterOptions) => {
373
+ if (!filters.search_exclusions || filters.search_exclusions.length === 0) {
374
+ return dataToSearch;
375
+ }
376
+ const { title, sku } = variant;
377
+ function customEncoder(content) {
378
+ const tokens = [];
379
+ const str = content.toLowerCase();
380
+ // Remove symbols from the string (keep only letters, numbers, commas, and spaces)
381
+ const cleanedStr = str.replace(/[\/-]/g, " ");
382
+ const cleanedStr2 = cleanedStr.replace(/[^a-z0-9,\/\s]/gi, "");
383
+ const words = cleanedStr2.split(/\s+/);
384
+ for (let word of words) {
385
+ tokens.push(word);
386
+ }
387
+ return tokens;
388
+ }
389
+ const index = new Index({
390
+ charset: EnglishPreset,
391
+ // encoder: encoder,
392
+ encode: customEncoder,
393
+ tokenize: "strict",
394
+ });
395
+ dataToSearch.forEach((item, id) => {
396
+ index.add(id, item.title);
397
+ });
398
+ const searchTerms = filters.match_values;
399
+ let final = null;
400
+ searchTerms.forEach((term) => {
401
+ if (final === null) {
149
402
  final = index.search(term, {
150
403
  resolve: false,
151
404
  suggest: true,
@@ -160,19 +413,14 @@ const handleIndexSearch = (dataToSearch, filters, variant, filterOptions) => {
160
413
  });
161
414
  }
162
415
  });
163
- // final = final.and({
164
- // index: index,
165
- // query: 'Chipper',
166
- // resolve: false,
167
- // // suggest: true,
168
- // })
169
- // MUST INCLUDE SKU???
170
416
  final = final.and({
171
417
  index: index,
172
418
  query: sku,
173
419
  resolve: false,
174
420
  suggest: true,
175
421
  });
422
+ const nots = [];
423
+ nots.push(...filters.search_exclusions);
176
424
  nots.forEach((term) => {
177
425
  final = final.not({
178
426
  index: index,
@@ -185,7 +433,16 @@ const handleIndexSearch = (dataToSearch, filters, variant, filterOptions) => {
185
433
  result.forEach((i) => {
186
434
  resultsArray.push(dataToSearch[i]);
187
435
  });
188
- return resultsArray;
436
+ for (let index = 0; index < dataToSearch.length; index++) {
437
+ const element = dataToSearch[index];
438
+ if (!result.includes(index)) {
439
+ if (!element.hide_reasons) {
440
+ element.hide_reasons = [];
441
+ }
442
+ element.hide_reasons.push(HIDE_REASONS.SEARCH_EXCLUSION);
443
+ }
444
+ }
445
+ return dataToSearch;
189
446
  };
190
447
  const filterLinkedElsewhereResults = (results, variantId) => {
191
448
  const filtered = results.filter((result) => {
@@ -220,48 +477,88 @@ const filterFoundProductIdExclusions = (results, found_id_exclusions) => {
220
477
  return filtered;
221
478
  };
222
479
  const filterIgnoredResults = (results) => {
223
- const filtered = results.filter((item) => {
224
- return item.ignore_result ? false : true;
480
+ results.forEach((item) => {
481
+ if (!item.hide_reasons) {
482
+ item.hide_reasons = [];
483
+ }
484
+ if (item.ignore_result) {
485
+ item.hide_reasons.push(HIDE_REASONS.IGNORED);
486
+ }
225
487
  });
226
- return filtered;
488
+ // const filtered = results.filter((item) => {
489
+ // return item.ignore_result ? false : true
490
+ // })
491
+ return results;
227
492
  };
228
493
  const filterPriceOutliers = (results, variantPrice, showHighPriceOutliers, showLowPriceOutliers) => {
229
- const filtered = results.filter((item) => {
230
- const showMoreExpensive = showHighPriceOutliers === true;
494
+ results.forEach((item) => {
495
+ if (!item.hide_reasons) {
496
+ item.hide_reasons = [];
497
+ }
231
498
  const isMoreExpensive = item.extracted_price > variantPrice * TOO_EXPENSIVE_MULTIPLIER;
232
- const showTooCheap = showLowPriceOutliers === true;
233
499
  const isTooCheap = item.extracted_price < variantPrice * TOO_CHEAP_MULTIPLIER;
234
- if (isMoreExpensive && !showMoreExpensive) {
235
- return false;
500
+ if (isMoreExpensive) {
501
+ item.hide_reasons.push(HIDE_REASONS.HIGH_PRICE_OUTLIER);
236
502
  }
237
- if (isTooCheap && !showTooCheap) {
238
- return false;
503
+ if (isTooCheap) {
504
+ item.hide_reasons.push(HIDE_REASONS.LOW_PRICE_OUTLIER);
239
505
  }
240
- return true;
241
506
  });
507
+ const filtered = results;
508
+ // const filtered = results.filter((item) => {
509
+ // const showMoreExpensive = showHighPriceOutliers === true
510
+ // const isMoreExpensive = item.extracted_price > variantPrice * TOO_EXPENSIVE_MULTIPLIER
511
+ // const showTooCheap = showLowPriceOutliers === true
512
+ // const isTooCheap = item.extracted_price < variantPrice * TOO_CHEAP_MULTIPLIER
513
+ // if (isMoreExpensive && !showMoreExpensive) {
514
+ // return false
515
+ // }
516
+ // if (isTooCheap && !showTooCheap) {
517
+ // return false
518
+ // }
519
+ // return true
520
+ // })
242
521
  return filtered;
243
522
  };
244
523
  const filterDateWindow = (results, dayWindow) => {
245
- const filtered = results.filter((item) => {
246
- if (!dayWindow)
247
- return true;
524
+ results.forEach((item) => {
525
+ if (!item.hide_reasons) {
526
+ item.hide_reasons = [];
527
+ }
248
528
  const itemDate = new Date(item.created_at);
249
- const variantDate = startOfDay(subDays(new Date(), Number(dayWindow) || 7));
250
- return itemDate >= variantDate;
529
+ const variantDate = startOfDay(subDays(new Date(), Number(dayWindow) || 3));
530
+ if (itemDate < variantDate) {
531
+ item.hide_reasons.push(HIDE_REASONS.DATE_OUTLIER);
532
+ }
251
533
  });
252
- return filtered;
534
+ // const filtered = results.filter((item) => {
535
+ // if (!dayWindow) return true
536
+ // const itemDate = new Date(item.created_at)
537
+ // const variantDate = startOfDay(subDays(new Date(), Number(dayWindow) || 7))
538
+ // return itemDate >= variantDate
539
+ // })
540
+ return results;
253
541
  };
254
542
  const filterCompetitors = (results, competitor_exclusions) => {
255
- const filtered = results.filter((item) => {
543
+ results.forEach((item) => {
544
+ if (!item.hide_reasons) {
545
+ item.hide_reasons = [];
546
+ }
256
547
  const lowerSource = item.source.toLowerCase();
257
- for (const exclusion of competitor_exclusions) {
258
- if (exclusion && lowerSource === exclusion.toLowerCase()) {
259
- return false;
260
- }
548
+ if (competitor_exclusions.some((exclusion) => exclusion && lowerSource === exclusion.toLowerCase())) {
549
+ item.hide_reasons.push(HIDE_REASONS.COMPETITOR_EXCLUSION);
261
550
  }
262
- return true;
263
551
  });
264
- return filtered;
552
+ // const filtered = results.filter((item) => {
553
+ // const lowerSource = item.source.toLowerCase()
554
+ // for (const exclusion of competitor_exclusions) {
555
+ // if (exclusion && lowerSource === exclusion.toLowerCase()) {
556
+ // return false
557
+ // }
558
+ // }
559
+ // return true
560
+ // })
561
+ return results;
265
562
  };
266
563
  const filterDuplicateResults = (results) => {
267
564
  const filteredUniqueResultsMap = {};
@@ -273,12 +570,24 @@ const filterDuplicateResults = (results) => {
273
570
  else {
274
571
  const existingItem = filteredUniqueResultsMap[key];
275
572
  if (new Date(item.created_at) > new Date(existingItem.created_at)) {
573
+ const foundResult = results.find((res) => res.id === existingItem.id);
574
+ if (foundResult) {
575
+ if (!foundResult.hide_reasons) {
576
+ foundResult.hide_reasons = [];
577
+ }
578
+ foundResult.hide_reasons.push(HIDE_REASONS.DUPLICATE);
579
+ }
276
580
  filteredUniqueResultsMap[key] = item;
277
581
  }
582
+ else {
583
+ if (!item.hide_reasons) {
584
+ item.hide_reasons = [];
585
+ }
586
+ item.hide_reasons.push(HIDE_REASONS.DUPLICATE);
587
+ }
278
588
  }
279
589
  });
280
- const filtered = Object.values(filteredUniqueResultsMap);
281
- return filtered;
590
+ return results;
282
591
  };
283
592
  export const calculateIgnoreStatus = (result, variant, result_ignore_keys) => {
284
593
  let ignore = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factorypure/client-helpers",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",