diff --git a/stores/hofer.js b/stores/hofer.js index 69337f5..0420bf8 100644 --- a/stores/hofer.js +++ b/stores/hofer.js @@ -3,51 +3,33 @@ const utils = require("./utils"); const conversions = { "": {unit: "stk", factor: 1}, - "Blatt": {unit: "stk", factor: 1}, + "blatt": {unit: "stk", factor: 1}, "g": {unit: "g", factor: 1}, "gg": {unit: "g", factor: 1}, "gramm": {unit: "g", factor: 1}, "kg": {unit: "g", factor: 1000}, - "KG": {unit: "g", factor: 1000}, "cl": {unit: "ml", factor: 100}, "l": {unit: "ml", factor: 1000}, - "L": {unit: "ml", factor: 1000}, "ml": {unit: "ml", factor: 1}, - "Paar": {unit: "stk", factor: 1}, - "Stk.": {unit: "stk", factor: 1}, + "paar": {unit: "stk", factor: 1}, + "stk.": {unit: "stk", factor: 1}, "stück": {unit: "stk", factor: 1}, - "Stück": {unit: "stk", factor: 1}, "er": {unit: "stk", factor: 1}, - "Teebeutel": {unit: "stk", factor: 1}, + "teebeutel": {unit: "stk", factor: 1}, }; exports.getCanonical = function(item, today) { // try to read quantity and unit from product name - let unit, quantity = 1; const name = item.ProductName; - const nameTokens = name.trim().replaceAll('(','').replaceAll(')','').replaceAll(',', '.').split(' '); - const lastToken = nameTokens[nameTokens.length-1]; - const secondLastToken = nameTokens.length > 2 ? nameTokens[nameTokens.length-2] : null; - const regex = /^([0-9.x]+)(.*)$/; - const matches = lastToken.match(regex); - if(matches) { - matches[1].split('x').forEach( (q)=> { - quantity = quantity * parseFloat(q) - }) - unit = matches[2]; - } - else if(secondLastToken !== null && secondLastToken.match(/^([0-9.]+)$/)) { - quantity = parseFloat(secondLastToken) - unit = lastToken; - } - else { + let [quantity, unit] = utils.parseUnitAndQuantityAtEnd(name); + if(conversions[unit] === undefined) { // fallback: use given quantity and unit (including packaging) quantity = item.Unit unit= item.UnitType } return utils.convertUnit({ id: item.ProductID, - name: item.ProductName, + name, price: item.Price, priceHistory: [{ date: today, price: item.Price }], isWeighted: item.IsBulk, diff --git a/stores/mpreis.js b/stores/mpreis.js index 692c34d..64db849 100644 --- a/stores/mpreis.js +++ b/stores/mpreis.js @@ -2,22 +2,41 @@ const axios = require("axios"); const utils = require("./utils"); const conversions = { - 'CM': { unit: 'cm', factor: 1 }, - 'DAG': { unit: 'g', factor: 10 }, - 'DL': { unit: 'ml', factor: 10 }, - 'GRM': { unit: 'g', factor: 1 }, - 'H87': { unit: 'ml', factor: 1000 }, - 'HLT': { unit: 'ml', factor: 1000 }, - 'KGM': { unit: 'g', factor: 1000 }, - 'LTR': { unit: 'ml', factor: 1000 }, - 'MLT': { unit: 'ml', factor: 1 }, - 'MTR': { unit: 'm', factor: 1 }, - 'XRO': { unit: 'stk', factor: 1 }, + 'cm': { unit: 'cm', factor: 1 }, + 'dag': { unit: 'g', factor: 10 }, + 'dl': { unit: 'ml', factor: 10 }, + 'grm': { unit: 'g', factor: 1 }, + 'kgm': { unit: 'g', factor: 1000 }, + 'ltr': { unit: 'ml', factor: 1000 }, + 'mlt': { unit: 'ml', factor: 1 }, + 'mtr': { unit: 'm', factor: 1 }, + 'stk': { unit: 'stk', factor: 1 }, + 'stk.': { unit: 'stk', factor: 1 }, + 'g': { unit: 'g', factor: 1 }, + 'anw': { unit: 'stk', factor: 1 }, + 'l': { unit: 'ml', factor: 1000 }, + 'm': { unit: 'cm', factor: 100 }, + 'ml': { unit: 'ml', factor: 1 }, + 'kg': { unit: 'g', factor: 1000 }, + 'paar': { unit: 'stk', factor: 1 }, + 'stück': { unit: 'stk', factor: 1 }, + 'bl.': { unit: 'stk', factor: 1 }, + 'pkg': { unit: 'stk', factor: 1 }, + 'gr': { unit: 'g', factor: 1 }, + 'er': { unit: 'stk', factor: 1 }, }; exports.getCanonical = function(item, today) { let quantity = item.prices[0].presentationPrice.measurementUnit.quantity - let unit = item.prices[0].presentationPrice.measurementUnit.unitCode + let unit = item.prices[0].presentationPrice.measurementUnit.unitCode.toLowerCase() + if(['xro', 'h87', 'hlt'].indexOf(unit)!=-1) { + const q = utils.parseUnitAndQuantityAtEnd(item.mixins.productCustomAttributes.packagingUnit) + quantity = q[0] ?? quantity; + unit = q[1]; + } + if (!(unit in conversions)) { + unit = 'stk'; + } const isWeighted = (item.mixins.productCustomAttributes?.packagingDescription ?? "").startsWith("Gewichtsware"); return utils.convertUnit({ id: item.code, diff --git a/stores/spar.js b/stores/spar.js index 7960874..ed637d4 100644 --- a/stores/spar.js +++ b/stores/spar.js @@ -3,23 +3,28 @@ const utils = require("./utils"); const HITS = Math.floor(30000 + Math.random() * 2000); const conversions = { - 'G': { unit: 'g', factor: 1 }, - 'KG': { unit: 'g', factor: 1000 }, - 'L': { unit: 'ml', factor: 1000 }, - 'ML': { unit: 'ml', factor: 1 }, - 'STK': { unit: 'stk', factor: 1 }, - 'Stück': { unit: 'stk', factor: 1 }, + 'g': { unit: 'g', factor: 1 }, 'kg': { unit: 'g', factor: 1000 }, 'l': { unit: 'ml', factor: 1000 }, + 'ml': { unit: 'ml', factor: 1 }, + 'stk': { unit: 'stk', factor: 1 }, + 'stück': { unit: 'stk', factor: 1 }, '100ml': { unit: 'ml', factor: 100 }, - 'WG': { unit: 'wg', factor: 1 }, + 'wg': { unit: 'wg', factor: 1 }, '100g': { unit: 'g', factor: 100 }, 'm': { unit: 'cm', factor: 100 }, + 'cm': { unit: 'cm', factor: 100 }, 'ml': { unit: 'ml', factor: 1 }, + 'meter': { unit: 'cm', factor: 100 }, + 'mm': { unit: 'cm', factor: .1 }, + 'stk.': { unit: 'cm', factor: .1 }, + 'cl': { unit: 'ml', factor: 10 }, + 'blatt': { unit: 'stk', factor: 1 }, }; exports.getCanonical = function(item, today) { let price, unit, quantity; + const description = item.masterValues["short-description-3"] ?? item.masterValues["short-description-2"]; if (item.masterValues["quantity-selector"]) { const [str_price, str_unit] = item.masterValues["price-per-unit"].split('/'); price = parseFloat(str_price.replace("€", "")); @@ -27,15 +32,18 @@ exports.getCanonical = function(item, today) { else { price = item.masterValues.price; } - if("short-description-3" in item.masterValues) { - [quantity, unit] = item.masterValues["short-description-3"].replace(" EINWEG", "").replace(" MEHRWEG", "").trim().split(' '); + if(description) { + const s = description.replace(" EINWEG", "").replace(" MEHRWEG", "").trim(); + const q = utils.parseUnitAndQuantityAtEnd(s); + quantity = q[0] + unit = q[1] } - else{ + if(conversions[unit]===undefined) { // use price per unit to calculate quantity (less accurate) let [unitPrice, unit_] = item.masterValues['price-per-unit'].split('/'); unitPrice = parseFloat(unitPrice.replace("€", "")); - quantity = Math.round(price / unitPrice) - unit = unit_; + quantity = parseFloat((price / unitPrice).toFixed(3)); + unit = unit_.toLowerCase(); } return utils.convertUnit({ id: item.masterValues["code-internal"], diff --git a/stores/utils.js b/stores/utils.js index b2919d9..39797fc 100644 --- a/stores/utils.js +++ b/stores/utils.js @@ -1,7 +1,7 @@ exports.convertUnit = function (item, units, store) { if(!(item.unit in units)) { - console.error(`Unknown unit in ${store}: '${item.unit}`); + console.error(`Unknown unit in ${store}: '${item.unit}' in item ${item.name}`); return item; } @@ -18,3 +18,22 @@ exports.convertUnit = function (item, units, store) { } return item; } + +exports.parseUnitAndQuantityAtEnd = function (name) { + let unit, quantity = 1; + const nameTokens = name.trim().replaceAll('(','').replaceAll(')','').replaceAll(',', '.').split(' '); + const lastToken = nameTokens[nameTokens.length-1]; + const secondLastToken = nameTokens.length >= 2 ? nameTokens[nameTokens.length-2] : null; + + const token = parseFloat(lastToken) ? lastToken : secondLastToken + lastToken; + const regex = /^([0-9.x]+)(.*)$/; + const matches = token.match(regex); + if(matches) { + matches[1].split('x').forEach( (q)=> { + quantity = quantity * parseFloat(q) + }) + unit = matches[2]; + return [quantity, unit.toLowerCase()]; + } + return [undefined, undefined]; +}