var fs = require("fs"); var csv = require('./csv.js'); var Promise = require('bluebird'); var fileName = "./importSales.csv"; // module.exports = function(models, softErrors, handler) { // return new Promise(function(resolve, reject) { var models = require("./models"); var softErrors = []; var p = new Promise(function(resolve, reject) { //The CSV data as an array of rows (each row is an array). var data; //The mapping of model attributes to CSV columns. The oz sizes are arrays of columns since there are multiple. var map = {}; //Database maps so we don't have to constantly query for the same data repeatedly. var vendorIdMap = {}; var measureIdMap = {}; var itemIdMap = {}; //Collect the metadata from the first row of the CSV data - make a mapping. function collectMetadata() { var DATE = 'date'; var VENDOR = 'vendor'; var ITEM = 'item'; var OZ32 = '32 oz'; var OZ16 = '16 oz'; var OZ12 = '12 oz'; var OZ8 = '8 oz'; var OZ4 = '4 oz'; var BAGS = 'bags'; var EACH = 'each'; //Data is an array of arrays. data[0] = array of headers. data[1] = first row of data. //Iterate over the columns to create a mapping. for(var i = 0; i < data[0].length; i++) { var next = data[0][i]; if(next && next != '') { switch(next.toLowerCase()) { case DATE: map.date = i; break; case VENDOR: map.vendor = i; break; case ITEM: map.item = i; break; case OZ32: map.oz32 = i; break; case OZ16: map.oz16 = i; break; case OZ12: map.oz12 = i; break; case OZ8: map.oz8 = i; break; case OZ4: map.oz4 = i; break; case BAGS: map.bags = i; break; case EACH: map.each = i; break; } } } } //Reads a single row of CSV data and adds it to the database. function readRow(rowIndex) { if(!data) { console.log("WTF?"); //process.exit(1); reject(new Error("Data is null?!?")); return; } else if(!data[rowIndex]) { console.log("Empty line?"); readRow(rowIndex + 1); return; } var date = data[rowIndex][map.date]; var vendor = data[rowIndex][map.vendor]; var item = data[rowIndex][map.item]; var oz32 = data[rowIndex][map.oz32]; var oz16 = data[rowIndex][map.oz16]; var oz12 = data[rowIndex][map.oz12]; var oz8 = data[rowIndex][map.oz8]; var oz4 = data[rowIndex][map.oz4]; var bags = data[rowIndex][map.bags]; var each = data[rowIndex][map.each]; var vendorId = vendorIdMap[vendor]; var itemId = itemIdMap[item]; var priceMap = {}; var saleQueries = []; //TODO: Trim and lowercase any names. priceMap[2016] = { oz4: 7, oz8: 11, oz12: 13, oz16: 11, oz32: 15, "pickled onion, spicy": {oz16: 12}, "pickled onion, sweet": {oz16: 12}, "pickled onion, fennel": {oz16: 12}, "pickled beets": {oz16: 15}, "prickly pear margarita mix": {oz16: 15}, "prickly pear syrup": {oz16: 15}, "tomato sauce": {oz16: 15}, //TODO: Replace all 'tomato sauce, xxxx' with 'tomato sauce' for price lookup only. "bone broth": {oz16: 20}, //TODO: Replace all 'bone broth, xxxx' with 'bone broth' for price lookup only. "fava bean soup": {oz32: 20}, "pickles, bread butter": {oz16: 11}, "kimchi": {oz16: 11}, "kraut - red & white": {oz16: 11}, "3 pack": {each: 21}, "espelette, smoked": {each: 1}, "leather": {each: 6}, "leather, apple": {each: 6}, "leather, grape": {each: 6}, "leather, peach": {each: 6}, "leather, persimmon": {each: 6}, "leather, pumpkin": {each: 6}, "leather, strawberry": {each: 6}, "leather, quince": {each: 6}, "membrillo": {each: 7}, "membrillo candy": {each: 2.5}, "spices": {each: 4}, "spices, basil/coriander": {each: 4}, "spices, fennel": {each: 4}, "spices, other": {each: 4}, "spices, pepper": {each: 4}, "spices, smoked": {each: 4}, "baba ganoush": {bag: 8, oz4: 8}, "dried, pear": {bag: 6}, //Price?? "dried, tomatillo": {bag: 6}, //Price?? "dried, strawberry": {bag: 7.5}, "dried, tomato": {bag: 7}, "pesto": {bags: 8}, "pesto, cilantro jalapeno": {bags: 8}, "pesto, eggplant": {bags: 8}, "pesto, mint": {bags: 8}, "pesto, jalapeno": {bags: 8}, "jalapeno candy": {bags: 5}, "tomato, dried": {bag: 7}, //Spelling??? Should be Dried, Tomato //Fresh "persimmons": {lbs: 5}, "persimmon, frozen": {lbs: 5}, "dried, persimmon": {lbs: 5}, "eggs": {each: 11} }; priceMap[2015] = { oz4: 6, oz8: 10, oz12: 13, oz16: 10, oz32: 15, "blackberry jam": {oz4: 7, oz8: 12}, "raspberry jam": {oz4: 7, oz8: 12}, "blackberry vanilla": {oz4: 7, oz8: 12}, "bloody mary mix": {oz16: 13}, "pickled beets": {oz16: 15}, "tomato juice cocktail": {oz16: 8}, "fava bean soup": {oz32: 20}, "kimchi": {oz16: 10}, "kraut - red & white": {oz16: 10}, "bread & butter pickles": {oz16: 10}, "pickled eggs": {oz16: 15}, "3 pack": {each: 18}, "leather": {each: 6}, "leather, apple": {each: 6}, "leather, grape": {each: 6}, "leather, peach": {each: 6}, "leather, persimmon": {each: 6}, "leather, pumpkin": {each: 6}, "leather, strawberry": {each: 6}, "leather, quince": {each: 6}, "membrillo": {each: 6}, "membrillo candy": {each: 2.5}, "popsicles": {each: 5}, "spices": {each: 4}, "spices, basil/coriander": {each: 4}, "spices, fennel": {each: 4}, "spices, other": {each: 4}, "spices, pepper": {each: 4}, "spices, smoked": {each: 4}, "baba ganoush": {bag: 8, oz4: 8}, "dried, pear": {bag: 6}, //Price?? "dried, tomatillo": {bag: 6}, //Price?? "dried, strawberry": {bag: 7.5}, "dried, tomato": {bag: 7}, "jalapeno candy": {bag: 5}, "eggs": {each: 10}, }; //Log errors, but keep on chugging. if(vendorId == undefined) { softErrors.push("ERROR: Unexpected vendor: " + vendor); } else if(itemId == undefined) { softErrors.push("ERROR: Unexpected item: " + item); } else { //Split it into multiple sales entries, one for each measure that has a positive value. if(oz32 > 0) { var measureId = measureIdMap['32 oz']; var price = priceMap[year][item]; if(price == undefined) { price = priceMap[year]['oz32']; } saleQueries.push(models.Sale.create({date: date, vendorId: vendorId, itemId: itemId, measureId: measureId, amount: oz32, price: price})); } if(oz16 > 0) { var measureId = measureIdMap['16 oz']; var price = priceMap[year][item]; if(price == undefined) { price = priceMap[year]['oz16']; } saleQueries.push(models.Sale.create({date: date, vendorId: vendorId, itemId: itemId, measureId: measureId, amount: oz32, price: price})); } if(oz12 > 0) { var measureId = measureIdMap['12 oz']; var price = priceMap[year][item]; if(price == undefined) { price = priceMap[year]['oz12']; } saleQueries.push(models.Sale.create({date: date, vendorId: vendorId, itemId: itemId, measureId: measureId, amount: oz32, price: price})); } if(oz8 > 0) { var measureId = measureIdMap['8 oz']; var price = priceMap[year][item]; if(price == undefined) { price = priceMap[year]['oz8']; } saleQueries.push(models.Sale.create({date: date, vendorId: vendorId, itemId: itemId, measureId: measureId, amount: oz32, price: price})); } if(oz4 > 0) { var measureId = measureIdMap['4 oz']; var price = priceMap[year][item]; if(price == undefined) { price = priceMap[year]['oz4']; } saleQueries.push(models.Sale.create({date: date, vendorId: vendorId, itemId: itemId, measureId: measureId, amount: oz32, price: price})); } if(bags > 0) { var measureId = measureIdMap['bags']; var price = priceMap[year][item]; if(price == undefined) { price = priceMap[year]['bags']; } saleQueries.push(models.Sale.create({date: date, vendorId: vendorId, itemId: itemId, measureId: measureId, amount: oz32, price: price})); } if(each > 0) { var measureId = measureIdMap['each']; var price = priceMap[year][item]; if(price == undefined) { price = priceMap[year]['each']; } saleQueries.push(models.Sale.create({date: date, vendorId: vendorId, itemId: itemId, measureId: measureId, amount: oz32, price: price})); } Promise.each(saleQueries, function(value, index, length) { //Ignored. }).then(function(result) { //Recursively read rows until there are no more rows to read. if(rowIndex + 1 < data.length) readRow(rowIndex + 1); else resolve(); }); } } if(fs.existsSync(fileName)) { csv.read(fileName, function(error, csvData) { if(error) console.log(error); else { data = csvData; //Collect the mapping data. collectMetadata(); //Read or create the required models to make this all work: Promise.each([ //Measures 1-6 models.Measure.find({where: {name: 'Jar 32oz'}}), models.Measure.find({where: {name: 'Jar 16oz'}}), models.Measure.find({where: {name: 'Jar 12oz'}}), models.Measure.find({where: {name: 'Jar 8oz'}}), models.Measure.find({where: {name: 'Jar 4oz'}}), models.Measure.find({where: {name: 'Each'}}), models.Measure.find({where: {name: 'Bags'}}), //Vendors 7-16 models.Venue.find({where: {name: 'Boonville'}}), models.Venue.find({where: {name: 'Clement St'}}), models.Venue.find({where: {name: 'Ukiah'}}), models.Venue.find({where: {name: 'Mendocino'}}), models.Venue.find({where: {name: 'Ft Bragg'}}), models.Venue.find({where: {name: 'On Farm'}}), models.Venue.find({where: {name: 'Unknown Restaurant'}}), models.Venue.find({where: {name: 'Yorkville Market'}}), models.Venue.find({where: {name: 'Yorkville Cellars'}}), models.Venue.find({where: {name: 'Mail Order'}}), //Items models.Item.findAll() ], function(value, index, length) { switch(index) { case 0: measureIdMap['32 oz'] = value.id; break; case 1: measureIdMap['16 oz'] = value.id; break; case 2: measureIdMap['12 oz'] = value.id; break; case 3: measureIdMap['8 oz'] = value.id; break; case 4: measureIdMap['4 oz'] = value.id; break; case 5: measureIdMap['each'] = value.id; break; case 6: measureIdMap['bags'] = value.id; break; case 7: vendorIdMap['bv'] = value.id; break; case 8: vendorIdMap['sf'] = value.id; break; case 9: vendorIdMap['uk'] = value.id; break; case 10: vendorIdMap['men'] = value.id; break; case 11: vendorIdMap['fb'] = value.id; break; case 12: vendorIdMap['of'] = value.id; break; case 13: vendorIdMap['res'] = value.id; break; case 14: vendorIdMap['ym'] = value.id; break; case 15: vendorIdMap['yc'] = value.id; break; case 16: vendorIdMap['mo'] = value.id; break; case 17: //Iterate over all the items and setup the itemIdMap to reference the item id given the item name. Item names are unique. for(var i = 0; i < value.length; i++) { itemIdMap[value[i].name] = value[i].id; } } }).then(function() { //Read the first row. readRow(1); }); } }); } else { console.log("Cannot find " + fileName); } }); p.then(function() {console.log("Import Complete!")}).error(function(error) {console.log("Ran into an error: \n" + error)}); // };