diff --git a/.gitignore b/.gitignore index dbea2fb..ab68b97 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ tmp-dir/ site/data/latest-canonical*.json* site/output copy +patterns diff --git a/patterns.js b/patterns.js new file mode 100644 index 0000000..7d4d4bd --- /dev/null +++ b/patterns.js @@ -0,0 +1,62 @@ +const fs = require("fs"); +const analysis = require("./analysis"); +const knn = require("./site/js/knn"); + +if (!fs.existsSync("patterns")) fs.mkdirSync("patterns"); + +if (!fs.existsSync("patterns/sorted-billa-spar.json")) { + const items = analysis.readJSON("data/latest-canonical.json.br"); + const billaSparItem = items + .filter((item) => item.store == "billa" || item.store == "spar") + .filter((item) => { + return !( + item.name.includes("Clever") || + item.name.includes("S-BUDGET") || + item.name.includes("Ja! Natürlich") || + item.name.includes("SPAR") || + item.name.includes("BILLA") + ); + }); + knn.vectorizeItems(billaSparItem); + const billaItems = billaSparItem.filter((item) => item.store == "billa"); + const sparItems = billaSparItem.filter((item) => item.store == "spar"); + + console.log(billaItems.length + " " + sparItems.length); + const sortedItems = []; + + sparItems.forEach((item) => (item.sorted = false)); + const total = billaItems.length; + while (billaItems.length > 0) { + const refItem = billaItems.shift(); + const similar = knn.findMostSimilarItem(refItem, sparItems); + if (similar.item != null) { + sortedItems.push(refItem); + sortedItems.push(similar.item); + } else { + console.log("No similar item found for " + refItem.name); + } + if (sortedItems.length % 100 == 0) console.log(sortedItems.length / 2 + "/" + total); + } + analysis.writeJSON("patterns/sorted-billa-spar.json", sortedItems); +} + +const sortedItems = analysis.readJSON("patterns/sorted-billa-spar.json"); +const filteredItems = []; +let result = ""; +for (let i = 0; i < sortedItems.length; i += 2) { + const a = sortedItems[i]; + const b = sortedItems[i + 1]; + const similarity = knn.dotProduct(a.vector, b.vector); + result += "billa " + a.name + "\n"; + result += "spar " + b.name + " " + similarity.toFixed(5) + "\n\n"; + + if (b.priceHistory.some((price) => price.price == a.price) && a.quantity == b.quantity) { + filteredItems.push(a); + filteredItems.push(b); + } +} +console.log("Sorted: " + sortedItems.length); +analysis.writeJSON("patterns/sorted-billa-spar-cart.json", { name: "Billa Spar Sortiert", items: sortedItems }); +console.log("Filtered: " + filteredItems.length); +analysis.writeJSON("patterns/sorted-billa-spar-filtered-cart.json", { name: "Billa Spar Sortiert Gefiltert", items: filteredItems }); +fs.writeFileSync("patterns/sorted-billa-spar.txt", result, "utf-8"); diff --git a/site/cart.js b/site/cart.js index 32e20ed..29e9dc9 100644 --- a/site/cart.js +++ b/site/cart.js @@ -133,7 +133,7 @@ function loadCart() { }); cartList.elements.numItemsLabel.innerHTML = "Artikel:"; cartList.elements.enableChart.checked = true; - cartList.elements.chart.elements.sumStores.checked = true; + cartList.elements.chart.elements.sumStores.checked = models.items.length < 2000; if (cart.items.length == 0) { elements.noItems.classList.remove("hidden"); diff --git a/site/js/knn.js b/site/js/knn.js index 9abde06..703eb1b 100644 --- a/site/js/knn.js +++ b/site/js/knn.js @@ -46,14 +46,19 @@ function findMostSimilarItem(refItem, items) { let maxSimilarity = -1; let similarItem = null; let similarItemIdx = -1; - items.forEach((item, idx) => { + for (let idx = 0; idx < items.length; idx++) { + const item = items[idx]; + if (item.sorted) continue; let similarity = dotProduct(refItem.vector, item.vector); - if (similarity > maxSimilarity) { + if (similarity > maxSimilarity || similarity > 0.9999999) { maxSimilarity = similarity; similarItem = item; similarItemIdx = idx; } - }); + if (similarity > 0.9999999) { + break; + } + } return { similarity: maxSimilarity, item: similarItem, @@ -96,16 +101,19 @@ function findMostSimilarItems(refItem, items, k = 5, accept = (ref, item) => tru } exports.findMostSimilarItems = findMostSimilarItems; -function similaritySortItems(items) { +function similaritySortItems(items, progress) { if (items.length == 0) return items; sortedItems = [items.shift()]; let refItem = sortedItems[0]; - while (items.length > 0) { + items.forEach((item) => (item.sorted = false)); + while (items.length != sortedItems.length) { const similarItem = findMostSimilarItem(refItem, items); sortedItems.push(similarItem.item); - items.splice(similarItem.index, 1); + similarItem.item.sorted = true; refItem = similarItem.item; + if (progress) progress(sortedItems, items); } + items.forEach((item) => delete item.sorted); return sortedItems; } exports.similaritySortItems = similaritySortItems; diff --git a/site/model/carts.js b/site/model/carts.js index 3a03d8a..bc3c334 100644 --- a/site/model/carts.js +++ b/site/model/carts.js @@ -36,7 +36,16 @@ class Carts extends Model { } save() { - localStorage.setItem("carts", JSON.stringify(this._carts, null, 2)); + const carts = []; + for (const cart of this._carts) { + carts.push({ + name: cart.name, + items: cart.items.map((item) => { + return { store: item.store, id: item.id }; + }), + }); + } + localStorage.setItem("carts", JSON.stringify(carts, null, 2)); this.notify(); }