Преглед изворни кода

Implement machine learning for individual ingredients.
Update the calculator page for ML predictions.

Lee Morgan пре 5 година
родитељ
комит
542b914e64

+ 55 - 65
views/dashboardPage/bundle.js

@@ -384,6 +384,11 @@ class Merchant{
         return this._transactions;
     }
 
+    //TODO: remove this, just for testing purposes
+    clearTransactions(){
+        this._transactions = [];
+    }
+
     getTransactions(from = 0, to = new Date()){
         if(merchant._transactions.length <= 0){
             return [];
@@ -604,11 +609,7 @@ class Merchant{
         to = end Date
     return: quantity sold in default unit
     */
-    getSingleIngredientSold(ingredient, from = 0, to = new Date()){
-        if(from === 0){
-            from = this._transactions[0].date;
-        }
-
+    getSingleIngredientSold(ingredient, from, to = new Date()){
         const {start, end} = this.getTransactionIndices(from, to);
 
         let total = 0;
@@ -2517,66 +2518,37 @@ module.exports = newTransaction;
 },{}],14:[function(require,module,exports){
 let orderCalculator = {
     display: function(){
-        let calculatorItems = document.getElementById("calculatorItemsBody");
-        let template = document.getElementById("calculatorItem").content.children[0];
-        let calculations = this.mlPredict();
+        let to = new Date();
+        to.setDate(to.getDate() + 7);
+        to.setHours(0, 0, 0, 0);
+        from = new Date();
+        from.setDate(from.getDate() + 1);
+        from.setHours(0, 0, 0, 0);
 
-        // while(calculatorItems.children.length > 0){
-        //     calculatorItems.removeChild(calculatorItems.firstChild);
-        // }
+        document.getElementById("predictDateFrom").valueAsDate = from;
+        document.getElementById("predictDateTo").valueAsDate = to;
+        document.getElementById("predictButton").addEventListener("click", ()=>{this.predict()});
 
-        // for(let i = 0; i < calculations.length; i++){
-        //     let outputString = `${calculations[i].output.toFixed(2)} ${calculations[i].ingredient.unit.toUpperCase()}`;
-        
-        //     let item = template.cloneNode(true);
-        //     item.children[0].innerText = calculations[i].ingredient.name,
-        //     item.children[1].innerText = outputString;
-        //     calculatorItems.appendChild(item);
-        // }
+        let selector = document.getElementById("predictSelector");
+        for(let i = 0; i < merchant.ingredients.length; i++){
+            let option = document.createElement("option");
+            option.innerText = merchant.ingredients[i].ingredient.name;
+            option.value = merchant.ingredients[i].ingredient.id;
+            selector.appendChild(option);
+        }
     },
 
     predict: function(){
-        let now = new Date();
-        let yesterday = new Date();
-        yesterday.setHours(0, 0, 0, 0);
-        let monthAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30);
-        let weekAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
-    
-        let calculations = [];
+        let from = document.getElementById("predictDateFrom").valueAsDate;
+        let to = document.getElementById("predictDateTo").valueAsDate;
+        let ingredient = merchant.getIngredient(document.getElementById("predictSelector").value);
 
-        let month = merchant.getIngredientsSold(monthAgo, yesterday);
-        let week = merchant.getIngredientsSold(weekAgo, yesterday);
-
-        let weights = {
-            month: 0.33,
-            week: 0.67
-        }
-
-        for(let i = 0; i < month.length; i++){
-            for(let j = 0; j < week.length; j++){
-                if(month[i].ingredient.id === week[j].ingredient.id){
-                    let monthAverage = (month[i].quantity / 30) * weights.month;
-                    let weekAverage = (week[i].quantity / 7) * weights.week;
-
-                    let calc = {
-                        ingredient: month[i].ingredient,
-                        output: monthAverage + weekAverage
-                    };
-                    calculations.push(calc);
-                }
-            }
-        }
-
-        return calculations;
-    },
-
-    mlPredict: function(){
         let data = {
             to: new Date(),
             recipes: []
         }
 
-        data.from = new Date(data.to.getFullYear() - 2, data.to.getMonth(), data.to.getDate());
+        data.from = new Date(data.to.getFullYear() - 1, data.to.getMonth(), data.to.getDate());
 
         fetch("/transaction", {
             method: "post",
@@ -2597,24 +2569,41 @@ let orderCalculator = {
 
                     const nn = ml5.neuralNetwork(options);
 
-                    this.createData(nn, response);
+                    this.createData(nn, response, ingredient.ingredient);
                     nn.normalizeData();
                     nn.train(()=>{
-                        nn.predict({
-                            month: 0,
-                            day: 0
-                        }, (err, results)=>{
-                            console.log(results[0].value);
-                        });
+                        let predictors = [];
+                        while(from <= to){
+                            predictors.push(nn.predict({
+                                month: from.getMonth(),
+                                day: from.getDay()
+                            }))
+
+                            from.setDate(from.getDate() + 1);
+                        }
+
+                        Promise.all(predictors)
+                            .then((predictions)=>{
+                                let total = 0
+                                for(let i = 0; i < predictions.length; i++){
+                                    total += predictions[i][0].value;
+                                }
+
+                                if(isNaN(total)) total = 0;
+                                document.getElementById("prediction").innerText = `${total.toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
+                            })
+                            .catch((err)=>{
+                                controller.createBanner("ERROR: UNABLE TO MAKE PREDICTION", "error");
+                            });
                     });
                 }
             })
             .catch((err)=>{
-                controller.createBanner("SOMETHING'S FUCKY...", "error");
+                controller.createBanner("ERROR: UNABLE TO MAKE PREDICTION", "error");
             });
     },
 
-    createData: function(nn, transactions){
+    createData: function(nn, transactions, ingredient){
         let today = new Date(transactions[transactions.length-1].date);
         today.setHours(0, 0, 0, 0);
         let tomorrow = new Date(transactions[transactions.length-1].date);
@@ -2623,7 +2612,6 @@ let orderCalculator = {
         let dailySum = 0;
 
         for(let i = transactions.length - 1; i >= 0; i--){
-        // for(let i = 100; i >= 0; i--){
             transactions[i].date = new Date(transactions[i].date);
 
             if(transactions[i].date >= tomorrow){
@@ -2644,9 +2632,11 @@ let orderCalculator = {
                 for(let k = 0; k < merchant.recipes.length; k++){
                     if(merchant.recipes[k].id === transactions[i].recipes[j].recipe){
                         for(let l = 0; l < merchant.recipes[k].ingredients.length; l++){
-                            if(merchant.recipes[k].ingredients[l].ingredient === merchant.ingredients[5].ingredient){
+                            if(merchant.recipes[k].ingredients[l].ingredient === ingredient){
                                 dailySum += merchant.recipes[k].ingredients[l].quantity * transactions[i].recipes[j].quantity;
                             }
+
+                            break;
                         }
 
                         break;

+ 13 - 15
views/dashboardPage/ejs/sidebars/orderCalculator.ejs

@@ -8,22 +8,20 @@
         </button>
     </div>
 
-    <h1>ORDER CALCULATOR</h1>
+    <h1>ORDER PREDICTION</h1>
 
-    <div class="scroller">
-        <table id="calculatorItems" class="calculatorItems" cellspacing="0" cellpadding="5px">
-            <thead>
-                <th>INGREDIENT</th>
-                <th>USAGE / DAY (PREDICTED)</th>
-            </thead>
-            <tbody id="calculatorItemsBody"></tbody>
-        </table>
+    <div class="dateRange">
+        <h3>Date Range:</h3>
+        <div class="dateRangeInput">
+            <input id="predictDateFrom" type="date">
+            <p> - </p>
+            <input id="predictDateTo" type="date">
+        </div>
     </div>
 
-    <template id="calculatorItem">
-        <tr class="calculatorItem">
-            <td></td>
-            <td class="calcUsage"></td>
-        </tr>
-    </template>
+    <select id="predictSelector"></select>
+
+    <button class="button" id="predictButton">PREDICT</button>
+
+    <h1 id="prediction"></h1>
 </div>

+ 6 - 5
views/dashboardPage/js/classes/Merchant.js

@@ -268,6 +268,11 @@ class Merchant{
         return this._transactions;
     }
 
+    //TODO: remove this, just for testing purposes
+    clearTransactions(){
+        this._transactions = [];
+    }
+
     getTransactions(from = 0, to = new Date()){
         if(merchant._transactions.length <= 0){
             return [];
@@ -488,11 +493,7 @@ class Merchant{
         to = end Date
     return: quantity sold in default unit
     */
-    getSingleIngredientSold(ingredient, from = 0, to = new Date()){
-        if(from === 0){
-            from = this._transactions[0].date;
-        }
-
+    getSingleIngredientSold(ingredient, from, to = new Date()){
         const {start, end} = this.getTransactionIndices(from, to);
 
         let total = 0;

+ 51 - 62
views/dashboardPage/js/sidebars/orderCalculator.js

@@ -1,65 +1,36 @@
 let orderCalculator = {
     display: function(){
-        let calculatorItems = document.getElementById("calculatorItemsBody");
-        let template = document.getElementById("calculatorItem").content.children[0];
-        let calculations = this.mlPredict();
-
-        // while(calculatorItems.children.length > 0){
-        //     calculatorItems.removeChild(calculatorItems.firstChild);
-        // }
-
-        // for(let i = 0; i < calculations.length; i++){
-        //     let outputString = `${calculations[i].output.toFixed(2)} ${calculations[i].ingredient.unit.toUpperCase()}`;
-        
-        //     let item = template.cloneNode(true);
-        //     item.children[0].innerText = calculations[i].ingredient.name,
-        //     item.children[1].innerText = outputString;
-        //     calculatorItems.appendChild(item);
-        // }
+        let to = new Date();
+        to.setDate(to.getDate() + 7);
+        to.setHours(0, 0, 0, 0);
+        from = new Date();
+        from.setDate(from.getDate() + 1);
+        from.setHours(0, 0, 0, 0);
+
+        document.getElementById("predictDateFrom").valueAsDate = from;
+        document.getElementById("predictDateTo").valueAsDate = to;
+        document.getElementById("predictButton").addEventListener("click", ()=>{this.predict()});
+
+        let selector = document.getElementById("predictSelector");
+        for(let i = 0; i < merchant.ingredients.length; i++){
+            let option = document.createElement("option");
+            option.innerText = merchant.ingredients[i].ingredient.name;
+            option.value = merchant.ingredients[i].ingredient.id;
+            selector.appendChild(option);
+        }
     },
 
     predict: function(){
-        let now = new Date();
-        let yesterday = new Date();
-        yesterday.setHours(0, 0, 0, 0);
-        let monthAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30);
-        let weekAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
-    
-        let calculations = [];
-
-        let month = merchant.getIngredientsSold(monthAgo, yesterday);
-        let week = merchant.getIngredientsSold(weekAgo, yesterday);
-
-        let weights = {
-            month: 0.33,
-            week: 0.67
-        }
-
-        for(let i = 0; i < month.length; i++){
-            for(let j = 0; j < week.length; j++){
-                if(month[i].ingredient.id === week[j].ingredient.id){
-                    let monthAverage = (month[i].quantity / 30) * weights.month;
-                    let weekAverage = (week[i].quantity / 7) * weights.week;
-
-                    let calc = {
-                        ingredient: month[i].ingredient,
-                        output: monthAverage + weekAverage
-                    };
-                    calculations.push(calc);
-                }
-            }
-        }
+        let from = document.getElementById("predictDateFrom").valueAsDate;
+        let to = document.getElementById("predictDateTo").valueAsDate;
+        let ingredient = merchant.getIngredient(document.getElementById("predictSelector").value);
 
-        return calculations;
-    },
-
-    mlPredict: function(){
         let data = {
             to: new Date(),
             recipes: []
         }
 
-        data.from = new Date(data.to.getFullYear() - 2, data.to.getMonth(), data.to.getDate());
+        data.from = new Date(data.to.getFullYear() - 1, data.to.getMonth(), data.to.getDate());
 
         fetch("/transaction", {
             method: "post",
@@ -80,24 +51,41 @@ let orderCalculator = {
 
                     const nn = ml5.neuralNetwork(options);
 
-                    this.createData(nn, response);
+                    this.createData(nn, response, ingredient.ingredient);
                     nn.normalizeData();
                     nn.train(()=>{
-                        nn.predict({
-                            month: 0,
-                            day: 0
-                        }, (err, results)=>{
-                            console.log(results[0].value);
-                        });
+                        let predictors = [];
+                        while(from <= to){
+                            predictors.push(nn.predict({
+                                month: from.getMonth(),
+                                day: from.getDay()
+                            }))
+
+                            from.setDate(from.getDate() + 1);
+                        }
+
+                        Promise.all(predictors)
+                            .then((predictions)=>{
+                                let total = 0
+                                for(let i = 0; i < predictions.length; i++){
+                                    total += predictions[i][0].value;
+                                }
+
+                                if(isNaN(total)) total = 0;
+                                document.getElementById("prediction").innerText = `${total.toFixed(2)} ${ingredient.ingredient.unit.toUpperCase()}`;
+                            })
+                            .catch((err)=>{
+                                controller.createBanner("ERROR: UNABLE TO MAKE PREDICTION", "error");
+                            });
                     });
                 }
             })
             .catch((err)=>{
-                controller.createBanner("SOMETHING'S FUCKY...", "error");
+                controller.createBanner("ERROR: UNABLE TO MAKE PREDICTION", "error");
             });
     },
 
-    createData: function(nn, transactions){
+    createData: function(nn, transactions, ingredient){
         let today = new Date(transactions[transactions.length-1].date);
         today.setHours(0, 0, 0, 0);
         let tomorrow = new Date(transactions[transactions.length-1].date);
@@ -106,7 +94,6 @@ let orderCalculator = {
         let dailySum = 0;
 
         for(let i = transactions.length - 1; i >= 0; i--){
-        // for(let i = 100; i >= 0; i--){
             transactions[i].date = new Date(transactions[i].date);
 
             if(transactions[i].date >= tomorrow){
@@ -127,9 +114,11 @@ let orderCalculator = {
                 for(let k = 0; k < merchant.recipes.length; k++){
                     if(merchant.recipes[k].id === transactions[i].recipes[j].recipe){
                         for(let l = 0; l < merchant.recipes[k].ingredients.length; l++){
-                            if(merchant.recipes[k].ingredients[l].ingredient === merchant.ingredients[5].ingredient){
+                            if(merchant.recipes[k].ingredients[l].ingredient === ingredient){
                                 dailySum += merchant.recipes[k].ingredients[l].quantity * transactions[i].recipes[j].quantity;
                             }
+
+                            break;
                         }
 
                         break;