import readCSV from '/imports/util/csv.js'; //Returns the ID of the created or found object. //This is needed because for some crazy reason upsert only returns the affected document if a document is inserted, otherwise it does not return the ID of the affected document. async function insertOrCreate(collection, selector, object) { let id; let findResult = await collection.findOneAsync(selector, {fields: {_id: 1}}); if(findResult && findResult._id) id = findResult._id; else id = await collection.insertAsync(object); // // let result = collection.upsert(selector, {$setOnInsert: object}, {multi: false}); // // console.log("Upsert result: " + JSON.stringify(result)); // // if(result && result.insertedId) id = result.insertedId; // else id = collection.findOne(selector, {fields: {_id: 1}}); if(!id) { console.log("ERROR: Cannot get the ID for " + JSON.stringify(selector)); } return id; } Meteor.methods({ "importBasics": async function() { const measures = [ {name: "Jar 4oz", postfix: "4oz", order: 0, createdAt: new Date()}, {name: "Jar 8oz", postfix: "8oz", order: 1, createdAt: new Date()}, {name: "Jar 12oz", postfix: "12oz", order: 2, createdAt: new Date()}, {name: "Jar 16oz", postfix: "16oz", order: 3, createdAt: new Date()}, {name: "Jar 32oz", postfix: "32oz", order: 4, createdAt: new Date()}, {name: "Pounds", postfix: "lbs", order: 5, createdAt: new Date()}, {name: "Each", postfix: "", order: 6, createdAt: new Date()}, {name: "Bags", postfix: "bags", order: 7, createdAt: new Date()}, {name: "Dozen Large", postfix: "12/lg", order: 8, createdAt: new Date()}, {name: "Dozen Small", postfix: "12/sm", order: 9, createdAt: new Date()}, {name: "Half Dozen Large", postfix: "6/lg", order: 10, createdAt: new Date()}, {name: "Half Dozen Small", postfix: "6/sm", order: 11, createdAt: new Date()} ]; const venues = [ {name: "Boonville", type: "Farmer's Market", createdAt: new Date()}, {name: "Clement St", type: "Farmer's Market", createdAt: new Date()}, {name: "Ukiah", type: "Farmer's Market", createdAt: new Date()}, {name: "Mendocino", type: "Farmer's Market", createdAt: new Date()}, {name: "Ft Bragg", type: "Farmer's Market", createdAt: new Date()}, {name: "On Farm", type: "Retail", createdAt: new Date()}, {name: "Yorkville Market", type: "Retail", createdAt: new Date()}, {name: "Yorkville Cellars", type: "Retail", createdAt: new Date()}, {name: "Unknown Restaurant", type: "Restaurant", createdAt: new Date()}, {name: "Mail Order", type: "Mail", createdAt: new Date()}, {name: "Website Order", type: "Mail", createdAt: new Date()} ]; await Measures.removeAsync({}); await Venues.removeAsync({}); for(let next of measures) { await Measures.insertAsync(next, function(error, _id) { if(error) console.log("Failed to insert measure: " + JSON.stringify(next) + " ERROR: " + error); }); } for(let next of venues) { await Venues.insertAsync(next, function(error, _id) { if(error) console.log("Failed to insert venue: " + JSON.stringify(next) + " ERROR: " + error); }); } }, "importItems": async function() { let fileName = "importItems.csv"; //The mapping of model attributes to CSV columns. The oz sizes are arrays of columns since there are multiple. let map = {}; //The id's in the db for the letious jar sizes. let measureIdMap = {}; let hasError = false; { let result; result = await Measures.findOneAsync({name: 'Jar 4oz'}, {fields: {_id: 1}}); if(result) measureIdMap['4oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 4oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 8oz'}, {fields: {_id: 1}}); if(result) measureIdMap['8oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 8oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 12oz'}, {fields: {_id: 1}}); if(result) measureIdMap['12oz'] = result._id else {console.log("Error: Couldn't find the _id for Jar 12oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 16oz'}, {fields: {_id: 1}}); if(result) measureIdMap['16oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 16oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 32oz'}, {fields: {_id: 1}}); if(result) measureIdMap['32oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 32oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Each'}, {fields: {_id: 1}}); if(result) measureIdMap['Each'] = result._id; else {console.log("Error: Couldn't find the _id for Each"); hasError = true;} result = await Measures.findOneAsync({name: 'Bags'}, {fields: {_id: 1}}); if(result) measureIdMap['Bags'] = result._id; else {console.log("Error: Couldn't find the _id for Bags"); hasError = true;} } // console.log("MeasureIdMap: " + JSON.stringify(measureIdMap)); //Don't proceed if the necessary measures don't exist. if(!hasError) { //Collect the metadata from the first row of the CSV data - make a mapping. function collectMetadata(firstRow) { const CATEGORY = 'category'; const SUBCATEGORY = 'subcategory'; const ITEM = 'item'; const OZ32 = '32 oz'; const OZ16 = '16 oz'; const OZ12 = '12 oz'; const OZ8 = '8 oz'; const OZ4 = '4 oz'; const BAGS = 'bags'; const EACH = 'each'; map.oz32 = []; map.oz16 = []; map.oz12 = []; map.oz8 = []; map.oz4 = []; map.bags = []; map.each = []; //Data is an array of arrays. firstRow = array of headers. data[1] = first row of data. //Iterate over the columns to create a mapping. for(let i = 0; i < firstRow.length; i++) { let next = firstRow[i]; if(next && next != '') { switch(next.toLowerCase()) { case CATEGORY: map.category = i; break; case SUBCATEGORY: map.subcategory = i; break; case ITEM: map.item = i; break; case OZ32: map.oz32.push(i); break; case OZ16: map.oz16.push(i); break; case OZ12: map.oz12.push(i); break; case OZ8: map.oz8.push(i); break; case OZ4: map.oz4.push(i); break; case BAGS: map.bags.push(i); break; case EACH: map.each.push(i); break; } } } } //Reads a single row of CSV data and adds it to the database. async function readRow(row) { let category = row[map.category].trim(); let subcategory = row[map.subcategory].trim(); let item = row[map.item]; let oz32 = 0; let oz16 = 0; let oz12 = 0; let oz8 = 0; let oz4 = 0; let bags = 0; let each = 0; for(let o = 0; o < map.oz32.length; o++) { oz32 += Math.max(row[map.oz32[o]], 0); } for(let o = 0; o < map.oz16.length; o++) { oz16 += Math.max(row[map.oz16[o]], 0); } for(let o = 0; o < map.oz12.length; o++) { oz12 += Math.max(row[map.oz12[o]], 0); } for(let o = 0; o < map.oz8.length; o++) { oz8 += Math.max(row[map.oz8[o]], 0); } for(let o = 0; o < map.oz4.length; o++) { oz4 += Math.max(row[map.oz4[o]], 0); } for(let o = 0; o < map.bags.length; o++) { bags += Math.max(row[map.bags[o]], 0); } for(let o = 0; o < map.each.length; o++) { each += Math.max(row[map.each[o]], 0); } // categoryId = insertOrCreate(Categories, {name: category}, {name: category, createdAt: new Date()}); let categoryId = await insertOrCreate(ProductTags, {name: category}, {name: category, createdAt: new Date()}); if(categoryId) { // subcategoryId = insertOrCreate(Subcategories, {name: subcategory}, { // name: subcategory, // categoryId: categoryId, // createdAt: new Date() // }); let subcategoryId = await insertOrCreate(ProductTags, {name: subcategory}, {name: subcategory, createdAt: new Date()}); if(subcategoryId) { let weightedMeasures = []; let measures = []; if(oz32 > 0) weightedMeasures.push({name: '32oz', count: oz32}); if(oz16 > 0) weightedMeasures.push({name: '16oz', count: oz16}); if(oz12 > 0) weightedMeasures.push({name: '12oz', count: oz12}); if(oz8 > 0) weightedMeasures.push({name: '8oz', count: oz8}); if(oz4 > 0) weightedMeasures.push({name: '4oz', count: oz4}); if(bags > 0) weightedMeasures.push({name: 'bags', count: bags}); if(each > 0) weightedMeasures.push({name: 'each', count: each}); if(weightedMeasures.length == 0) { measures.push(measureIdMap['Each']); } else { //Place the measure that had the most jars at the front of the list. weightedMeasures.sort(function(a, b) { return a.count >= b.count ? 1 : -1; }); //Build the array of measure id's. for(let i = 0; i < weightedMeasures.length; i++) { measures.push(measureIdMap[weightedMeasures[i].name]); } } //Here we can just insert since we don't care about the resulting ID. let obj = {name: item, tags: [categoryId, subcategoryId], measures: measures, createdAt: new Date()}; await Products.insertAsync(obj, function(error) { if(error) console.log("Could not insert the Product: " + JSON.stringify(obj) + "\n ERROR: " + error); }); } else { console.log("SubcategoryID: " + subcategoryId); } } else { console.log("CategoryID: " + categoryId); } } readCSV(fileName, Meteor.bindEnvironment(async function(error, data) { //csv.read(fileName, function(error, csvData) { if(error) console.log("Unable to read the importItems.csv file:" + error); else { //Collect the mapping data. collectMetadata(data[0]); //Remove everything first. // Items.remove({}); // Subcategories.remove({}); // Categories.remove({}); await Products.removeAsync({}); // console.log("CSV Column Mapping: " + JSON.stringify(map)); // readRow(data[1]); for(let i = 1; i < data.length; i++) { await readRow(data[i]); } } })); } }, "importSales": async function() { let fileName = "importSales.csv"; //The mapping of model attributes to CSV columns. The oz sizes are arrays of columns since there are multiple. let map = {}; //The id's in the db for the letious jar sizes. let measureIdMap = {}; let venueIdMap = {}; let itemIdMap = {}; let hasError = false; let priceMap = {}; let itemAliases = {}; //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}, "3packs": {each: 21}, "espelette, smoked": {each: 1}, "leather": {each: 6}, "leather, unspecified": {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, unspecified": {each: 4}, "spices, basil/corriander": {each: 4}, "spices, fennel": {each: 4}, "spices, other": {each: 4}, "spices, pepper": {each: 4}, "spices, smoked": {each: 4}, "baba ganoush": {bags: 8, oz4: 8}, "dried, pear": {bags: 6}, //Price?? "dried, tomatillo": {bags: 6}, //Price?? "dried, strawberry": {bags: 7.5}, "pesto": {bags: 8, each: 8}, "pesto, cilantro jalapeno": {bags: 8, each: 8}, "pesto, eggplant": {bags: 8, each: 8}, "pesto, mint": {bags: 8, each: 8}, "pesto, jalapeno": {bags: 8, each: 8}, "jalapeno candy": {bags: 5}, "dried, tomato": {bags: 7}, "tomato, dried": {bags: 7}, "dried tomato": {bags: 7}, "chipotle peppers": {each: 5, bags: 5}, //Fresh "persimmons": {lbs: 5, bags: 5}, "persimmon, frozen": {lbs: 5, bags: 5}, "dried, persimmon": {lbs: 5, bags: 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}, "3packs": {each: 18}, "leather": {each: 6}, "leather, unspecified": {each: 6}, "leather, apple": {each: 6}, "leather, grape": {each: 6}, "leather, peach": {each: 6}, "leather, persimmon": {each: 6}, "leather, pumpkin": {each: 6, bags: 6}, "leather, strawberry": {each: 6, bags: 6}, "leather, quince": {each: 6}, "membrillo": {each: 6}, "membrillo candy": {each: 2.5, bags: 2.5}, "popsicles": {each: 5, bags: 5}, "spices": {each: 4}, "spices, unspecified": {each: 4}, "spices, basil/corriander": {each: 4}, "spices, fennel": {each: 4}, "spices, other": {each: 4}, "spices, pepper": {each: 4}, "spices, smoked": {each: 4}, "spices, smoked pepper": {each: 4}, "pesto, mint": {each: 7, bags: 7}, "pesto, garlic scape": {each: 7, bags: 7}, "pesto, eggplant": {each: 7, bags: 7}, "jalapenos": {each: 5, bags: 5}, //? Price guess - only one entry "membrillo truffles": {each: 5, bags: 5}, //? Price guess - only two entries "baba ganoush": {bags: 8, oz4: 8}, "dried, pear": {bags: 6}, //Price?? "dried, tomatillo": {bags: 6}, //Price?? "dried, strawberry": {bags: 7.5, each: 7.5}, "dried, tomato": {bags: 7}, "dried tomato": {bags: 7}, "jalapeno candy": {bags: 5, each: 5}, "eggs": {each: 10}, }; priceMap[2014] = { oz4: 6, oz8: 9, oz12: 13, oz16: 13, oz32: 20, "3packs": {bags: 18, each: 18}, //???? "kimchi": {oz16: 10}, "krauts" : {oz16: 10}, "pickled eggs": {oz16: 13}, "jalapeno candy": {bags: 5}, //? Price guess "eggs": {each: 7} }; itemAliases["3 packs"] = "3packs"; itemAliases["3 pack"] = "3packs"; itemAliases["3pack"] = "3packs"; itemAliases["Salsa Hot"] = "Salsa, Hot"; itemAliases["Sweet Spicy Dipping"] = "Sweet Spicy Dipping Sauce"; itemAliases["Spiced Seckel Jam"] = "Pear Jam, Seckel"; itemAliases["Spiced Seckle Pear Jam"] = "Pear Jam, Seckel"; itemAliases["Spices, Smoked"] = "Spices, Smoked Pepper"; itemAliases["Sweet Sour Cabbage"] = "Sweet Sour Red Cabbage"; itemAliases["Pickled Onion, Pepper"] = "Pickled Onions (sweet)"; itemAliases["Pickled Onion, Spicy"] = "Pickled Onion Dill w/Espelette"; itemAliases["Pickled Onion, hot pep"] = "Pickled Onion Dill w/Espelette"; itemAliases["Shrub Samplers"] = "Shrubs"; itemAliases["Shrub, Anise Hyssop"] = "Shrubs"; itemAliases["Shrub, Basil"] = "Shrubs"; itemAliases["Shrub, Strawberry Bals"] = "Shrubs"; itemAliases["Shrub, Tayberry"] = "Shrubs"; itemAliases["Tomato Sauce"] = "Tomato Sauce, Basil"; itemAliases["Pickled Onion Hot Pepper"] = "Pickled Onion Dill w/Espelette"; itemAliases["Applesauce Honey"] = "Applesauce, Honey"; itemAliases["Applesauce Cinnamon"] = "Applesauce, Cinnamon"; itemAliases["Applesauce Unsweet"] = "Applesauce, Unsweetened"; itemAliases["Applesauce, Unsweet"] = "Applesauce, Unsweetened"; itemAliases["Babcock Ginger Jam"] = "Peach Jam, Babcock w/ Ginger"; itemAliases["Babcock Lavendar Jam"] = "Peach Jam, Babcock w/ Lavender"; itemAliases["Babcock Peach Jam"] = "Peach Jam, Babcock"; itemAliases["Bartlett Pear Jam, Vanilla"] = "Bartlett Vanilla Jam"; itemAliases["Bartlett Pear Vanilla Jam"] = "Bartlett Vanilla Jam"; itemAliases["Blackberry Jam, Mint"] = "Blackberry Mint Jam"; itemAliases["Bloody Mary"] = "Bloody Mary Mix"; itemAliases["Bread Butter Pickles"] = "Pickles, Bread Butter"; itemAliases["Chow Chow Hot"] = "Chow Chow Spicy"; itemAliases["Chow Chow, Mild"] = "Chow Chow Mild"; itemAliases["Chow Chow, Spicy"] = "Chow Chow Spicy"; itemAliases["Chow chow Hot"] = "Chow Chow Spicy"; itemAliases["Comice Sherry Jam"] = "Comice Pear Jam w/ Sherry"; itemAliases["Dill Pickles"] = "Pickles, Dill"; itemAliases["Dried Pear"] = "Dried, Pear"; itemAliases["Dried Tomatillo"] = "Dried, Tomatillo"; itemAliases["Dried, Tomato"] = "Dried Tomato"; itemAliases["Eggplant Pesto"] = "Pesto, Eggplant"; itemAliases["Fennel Pickles"] = "Pickles, Fennel"; itemAliases["Fig Port Jam"] = "Fig Jam, Port"; itemAliases["French Apple Jam"] = "Apple Jam, French"; itemAliases["Garlic Scape Pesto"] = "Pesto, Garlic Scape"; itemAliases["Ginger Garlic Pickles"] = "Pickles, Ginger Garlic"; itemAliases["Green Gage Ginger Jam"] = "Green Gage Jam w/ Ginger"; itemAliases["Jujube Ginger Butter"] = "Jujube & Ginger Butter"; itemAliases["Leather"] = "Leather, Unspecified"; itemAliases["Leek Potato Soup"] = "Leek & Potato Soup"; itemAliases["Mint Pesto"] = "Pesto, Mint"; itemAliases["Napa Kraut Onion"] = "Napa Kraut, Onion"; itemAliases["Peach Jam, Babcock Ginger"] = "Peach Jam, Babcock w/ Ginger"; itemAliases["Peach Jam, Babcock Lavender"] = "Peach Jam, Babcock w/ Lavender"; itemAliases["Pear Dessert Sauce"] = "Pear Dessert Topping"; itemAliases["Pear Ginger Preserve"] = "Pear Ginger Preserves"; itemAliases["Pear Jam, Bosc"] = "Bosc Pear Jam"; itemAliases["Pear Jam, Comice"] = "Comice Pear Jam"; itemAliases["Pear Jam, Sherry"] = "Comice Pear Jam w/ Sherry"; itemAliases["Pepper Relish, Spicy"] = "Spicy Pepper Relish"; itemAliases["Pepper Relish, Sweet"] = "Sweet Pepper Relish"; itemAliases["Pickled Onion Fennel"] = "Pickled Onions Fennel"; itemAliases["Pickled Onion, Fennel"] = "Pickled Onions Fennel"; itemAliases["Pickled Onion, Sweet"] = "Pickled Onions (sweet)"; itemAliases["Pickled Onion, Sweet Pep"] = "Pickled Onions (sweet)"; itemAliases["Pickled Onion, Sweet Pepper"] = "Pickled Onions (sweet)"; itemAliases["Pickles w/ Fennel"] = "Pickles, Fennel"; itemAliases["Plum Jam, Wild"] = "Wild Plum Jam"; itemAliases["Plum Syrup, Wild"] = "Wild Plum Syrup"; itemAliases["Pumpkin Rollup"] = "Leather, Pumpkin"; itemAliases["Rubarb Jam"] = "Rhubarb Jam"; itemAliases["Santa Rosa Plum Jam"] = "Plum Jam, Santa Rosa"; itemAliases["Saturn Peach Jam"] = "Peach Jam, Saturn"; itemAliases["Sauerkraut Red"] = "Sauerkraut, Red"; itemAliases["Seckel Pear Jam"] = "Pear Jam, Seckel"; itemAliases["Spiced Plum Jam"] = "Plum Jam, Spiced"; itemAliases["Spices"] = "Spices, Unspecified"; itemAliases["Spices, Basil/Coriander"] = "Spices, Basil/Corriander"; itemAliases["Spices, Other"] = "Spices, Unspecified"; itemAliases["Strawberry Lavendar Jam"] = "Strawberry Lavender Jam"; itemAliases["Sz Broccoli"] = "Szechuan Broccoli"; itemAliases["Tomato Sauce Basil"] = "Tomato Sauce, Basil"; itemAliases["Tomato Sauce Fennel"] = "Tomato Sauce, Fennel"; itemAliases["Tomato Sauce Oregano"] = "Tomato Sauce, Oregano"; itemAliases["Tomato, Dried"] = "Dried Tomato"; itemAliases["Tomato, dried"] = "Dried Tomato"; itemAliases["White Grape Jam"] = "Grape Jam, White"; itemAliases["Pepper Syrup"] = "Spicy Pepper Syrup"; itemAliases["Apple Prickly Pear"] = "Applesauce, Prickly Pear"; itemAliases["Babcock Peach Chutney"] = "Peach Chutney, Babcock"; itemAliases["Blackberry Conserve"] = "Blackberry Hazelnut Conserve"; itemAliases["Bread Butter Armenian"] = "Pickles, Bread Butter Armenian"; itemAliases["Currant Jam"] = "White Currant Jam"; { //Load the object ids for the measures and venues we will encounter. let result; result = await Measures.findOneAsync({name: 'Jar 4oz'}, {fields: {_id: 1}}); if(result) measureIdMap['4oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 4oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 8oz'}, {fields: {_id: 1}}); if(result) measureIdMap['8oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 8oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 12oz'}, {fields: {_id: 1}}); if(result) measureIdMap['12oz'] = result._id else {console.log("Error: Couldn't find the _id for Jar 12oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 16oz'}, {fields: {_id: 1}}); if(result) measureIdMap['16oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 16oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Jar 32oz'}, {fields: {_id: 1}}); if(result) measureIdMap['32oz'] = result._id; else {console.log("Error: Couldn't find the _id for Jar 32oz"); hasError = true;} result = await Measures.findOneAsync({name: 'Each'}, {fields: {_id: 1}}); if(result) measureIdMap['Each'] = result._id; else {console.log("Error: Couldn't find the _id for Each"); hasError = true;} result = await Measures.findOneAsync({name: 'Bags'}, {fields: {_id: 1}}); if(result) measureIdMap['Bags'] = result._id; else {console.log("Error: Couldn't find the _id for Bags"); hasError = true;} result = await Venues.findOneAsync({name: 'Boonville'}, {fields: {_id: 1}}); if(result) venueIdMap['bv'] = result._id; else {console.log("Error: Couldn't find the _id for Boonville"); hasError = true;} result = await Venues.findOneAsync({name: 'Clement St'}, {fields: {_id: 1}}); if(result) venueIdMap['sf'] = result._id; else {console.log("Error: Couldn't find the _id for Clement St"); hasError = true;} result = await Venues.findOneAsync({name: 'Ukiah'}, {fields: {_id: 1}}); if(result) venueIdMap['uk'] = result._id; else {console.log("Error: Couldn't find the _id for Ukiah"); hasError = true;} result = await Venues.findOneAsync({name: 'Mendocino'}, {fields: {_id: 1}}); if(result) venueIdMap['men'] = result._id; else {console.log("Error: Couldn't find the _id for Mendocino"); hasError = true;} result = await Venues.findOneAsync({name: 'Ft Bragg'}, {fields: {_id: 1}}); if(result) venueIdMap['fb'] = result._id; else {console.log("Error: Couldn't find the _id for Ft Bragg"); hasError = true;} result = await Venues.findOneAsync({name: 'On Farm'}, {fields: {_id: 1}}); if(result) venueIdMap['of'] = result._id; else {console.log("Error: Couldn't find the _id for On Farm"); hasError = true;} result = await Venues.findOneAsync({name: 'Unknown Restaurant'}, {fields: {_id: 1}}); if(result) {venueIdMap['res'] = result._id; venueIdMap['w'] = result._id;} else {console.log("Error: Couldn't find the _id for Unknown Restaurant"); hasError = true;} result = await Venues.findOneAsync({name: 'Yorkville Market'}, {fields: {_id: 1}}); if(result) venueIdMap['ym'] = result._id; else {console.log("Error: Couldn't find the _id for Yorkville Market"); hasError = true;} result = await Venues.findOneAsync({name: 'Yorkville Cellars'}, {fields: {_id: 1}}); if(result) venueIdMap['yc'] = result._id; else {console.log("Error: Couldn't find the _id for Yorkville Cellars"); hasError = true;} result = await Venues.findOneAsync({name: 'Mail Order'}, {fields: {_id: 1}}); if(result) venueIdMap['mo'] = result._id; else {console.log("Error: Couldn't find the _id for Mail Order"); hasError = true;} // result = Items.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetch(); result = await Products.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetchAsync(); for(let i = 0; i < result.length; i++) itemIdMap[result[i].name.toLowerCase()] = result[i]._id; //console.log(JSON.stringify(itemIdMap)); } readCSV(fileName, Meteor.bindEnvironment(async function(error, data) { //csv.read(fileName, function(error, csvData) { //Data is an array of arrays. data[0] = array of headers. data[1] = first row of data. if(error) console.log("Unable to read the importSales.csv file:" + error); else { //Collect the mapping data. collectMetadata(data[0]); //Remove everything first. await Sales.removeAsync({"importTag": "1"}); // console.log("CSV Column Mapping: " + JSON.stringify(map)); // readRow(data[1]); let undefinedItems = {}; for(let i = 1; i < data.length; i++) { await readRow(data[i], undefinedItems); } // let output = ""; // output += "----------------\n"; // output +="Undefined Items:\n"; // output +="Name;32 oz;16 oz;12 oz;8 oz;4 oz;bags;each\n" // // let items = []; // for(let property in undefinedItems) { // items.push(property); // } // items.sort(); // for(let i = 0; i < items.length; i++) { // let next = items[i]; // let sizes = undefinedItems[next]; // // // console.log(JSON.stringify(sizes)); // output += next+";"+sizes['32 oz']+";"+sizes['16 oz']+";"+sizes['12 oz']+";"+sizes['8 oz']+";"+sizes['4 oz']+";"+sizes['bags']+";"+sizes['each'] + "\n"; // } // output += "----------------"; // console.log(output);` } })); //Collect the metadata from the first row of the CSV data - make a mapping. function collectMetadata(row) { let DATE = 'date'; let VENUE = 'vendor'; let ITEM = 'item'; let OZ32 = '32oz'; let OZ16 = '16oz'; let OZ12 = '12oz'; let OZ8 = '8oz'; let OZ4 = '4oz'; let BAGS = 'bags'; let EACH = 'each'; //Iterate over the columns to create a mapping. for(let i = 0; i < row.length; i++) { let next = row[i]; if(next && next != '') { switch(next.toLowerCase()) { case DATE: map.date = i; break; case VENUE: map.venue = 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. async function readRow(row, undefinedItems) { let date = moment(row[map.date], "M/D/YYYY").toDate(); let venue = row[map.venue].toLowerCase(); let item = row[map.item].trim(); item = itemAliases[item] ? itemAliases[item] : item; item = item.toLowerCase(); let oz32 = row[map.oz32] == undefined ? 0 : Number(row[map.oz32]); let oz16 = row[map.oz16] == undefined ? 0 : Number(row[map.oz16]); let oz12 = row[map.oz12] == undefined ? 0 : Number(row[map.oz12]); let oz8 = row[map.oz8] == undefined ? 0 : Number(row[map.oz8]); let oz4 = row[map.oz4] == undefined ? 0 : Number(row[map.oz4]); let bags = row[map.bags] == undefined ? 0 : Number(row[map.bags]); let each = row[map.each] == undefined ? 0 : Number(row[map.each]); let venueId = venueIdMap[venue]; let itemId = itemIdMap[item]; let year = date.getFullYear(); if(venueId == undefined) { console.log("Found an undefined venue: " + venue); } else if(itemId == undefined) { let sizes = undefinedItems[row[map.item]] || {'32 oz' : 0, '16 oz' : 0, '12 oz' : 0, '8 oz' : 0, '4 oz' : 0, 'bags' : 0, 'each' : 0}; sizes['32 oz'] += oz32; sizes['16 oz'] += oz16; sizes['12 oz'] += oz12; sizes['8 oz'] += oz8; sizes['4 oz'] += oz4; sizes['bags'] += bags; sizes['each'] += each; undefinedItems[row[map.item]] = sizes; //Use the capitalized name - will use the mapping back in the calling code to generate a sorted list for sharing with the admin. } else if(priceMap[year] == undefined) { console.log("Unexpected year: " + year); } else { //Split it into multiple sales entries, one for each measure that has a positive value. if(oz32 > 0) { let price = priceMap[year][item]; if(price != undefined) price = price['oz32']; if(price == undefined) price = priceMap[year]['oz32']; if(price) await insertSale({date: date, amount: oz32, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['32oz']}); else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz32"); } if(oz16 > 0) { let price = priceMap[year][item]; if(price != undefined) price = price['oz16']; if(price == undefined) price = priceMap[year]['oz16']; if(price) await insertSale({date: date, amount: oz16, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['16oz']}); else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz16"); } if(oz12 > 0) { let price = priceMap[year][item]; if(price != undefined) price = price['oz12']; if(price == undefined) price = priceMap[year]['oz12']; if(price) await insertSale({date: date, amount: oz12, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['12oz']}); else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz12"); } if(oz8 > 0) { let price = priceMap[year][item]; if(price != undefined) price = price['oz8']; if(price == undefined) price = priceMap[year]['oz8']; if(price) await insertSale({date: date, amount: oz8, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['8oz']}); else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz8"); } if(oz4 > 0) { let price = priceMap[year][item]; if(price != undefined) price = price['oz4']; if(price == undefined) price = priceMap[year]['oz4']; if(price) await insertSale({date: date, amount: oz4, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['4oz']}); else console.log("Could not find a price in the year " + year + " for " + item + " in the size oz4"); } if(bags > 0) { let price = priceMap[year][item]; if(price != undefined) price = price['bags']; if(price == undefined) price = priceMap[year]['bags']; if(price) await insertSale({date: date, amount: bags, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['Bags']}); else { console.log("Could not find a price in the year " + year + " for " + item + " in the size bags"); } } if(each > 0) { let price = priceMap[year][item]; if(price != undefined) price = price['each']; if(price == undefined) price = priceMap[year]['each']; if(price) await insertSale({date: date, amount: each, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['Each']}); else console.log("Could not find a price in the year " + year + " for " + item + " in the size each"); } } } async function insertSale(sale) { sale.createdAt = new Date(); sale.importTag = "1"; await Sales.insertAsync(sale, function(error) { if(error) console.log("Failed to insert the sale: " + JSON.stringify(sale) + "\n ERROR: " + error); }, {bypassCollection2: true}); } }, "importSales2": function() { let fileName = "importSales2.csv"; //The mapping of model attributes to CSV columns. The oz sizes are arrays of columns since there are multiple. let map = {}; //The id's in the db for the letious jar sizes. let measureIdMap = {}; let venueIdMap = {}; let itemIdMap = {}; let hasError = false; { //Load the object ids for the measures and venues we will encounter. let result; result = Measures.findOne({name: 'Pounds'}, {fields: {_id: 1}}); if(result) measureIdMap['lbs'] = result._id; else {console.log("Error: Couldn't find the _id for Lbs"); hasError = true;} result = Measures.findOne({name: 'Each'}, {fields: {_id: 1}}); if(result) measureIdMap['each'] = result._id; else {console.log("Error: Couldn't find the _id for Each"); hasError = true;} result = Measures.findOne({name: 'Dozen Large'}, {fields: {_id: 1}}); if(result) measureIdMap['dozen, large'] = result._id; else {console.log("Error: Couldn't find the _id for Dozen, Large"); hasError = true;} result = Venues.findOne({name: 'Boonville'}, {fields: {_id: 1}}); if(result) venueIdMap['bv'] = result._id; else {console.log("Error: Couldn't find the _id for Boonville"); hasError = true;} result = Venues.findOne({name: 'Clement St'}, {fields: {_id: 1}}); if(result) venueIdMap['sf'] = result._id; else {console.log("Error: Couldn't find the _id for Clement St"); hasError = true;} result = Venues.findOne({name: 'Ukiah'}, {fields: {_id: 1}}); if(result) venueIdMap['uk'] = result._id; else {console.log("Error: Couldn't find the _id for Ukiah"); hasError = true;} result = Venues.findOne({name: 'Mendocino'}, {fields: {_id: 1}}); if(result) venueIdMap['men'] = result._id; else {console.log("Error: Couldn't find the _id for Mendocino"); hasError = true;} result = Venues.findOne({name: 'Ft Bragg'}, {fields: {_id: 1}}); if(result) venueIdMap['fb'] = result._id; else {console.log("Error: Couldn't find the _id for Ft Bragg"); hasError = true;} result = Venues.findOne({name: 'On Farm'}, {fields: {_id: 1}}); if(result) venueIdMap['of'] = result._id; else {console.log("Error: Couldn't find the _id for On Farm"); hasError = true;} result = Venues.findOne({name: 'Unknown Restaurant'}, {fields: {_id: 1}}); if(result) {venueIdMap['res'] = result._id; venueIdMap['w'] = result._id;} else {console.log("Error: Couldn't find the _id for Unknown Restaurant"); hasError = true;} result = Venues.findOne({name: 'Yorkville Market'}, {fields: {_id: 1}}); if(result) venueIdMap['ym'] = result._id; else {console.log("Error: Couldn't find the _id for Yorkville Market"); hasError = true;} result = Venues.findOne({name: 'Yorkville Cellars'}, {fields: {_id: 1}}); if(result) venueIdMap['yc'] = result._id; else {console.log("Error: Couldn't find the _id for Yorkville Cellars"); hasError = true;} result = Venues.findOne({name: 'Mail Order'}, {fields: {_id: 1}}); if(result) venueIdMap['mo'] = result._id; else {console.log("Error: Couldn't find the _id for Mail Order"); hasError = true;} result = Products.find({}, {fields: {_id: 1, name: 1}, sort: {name: 1}}).fetch(); for(let i = 0; i < result.length; i++) itemIdMap[result[i].name.toLowerCase()] = result[i]._id; } readCSV(fileName, Meteor.bindEnvironment(function(error, data) { //Data is an array of arrays. data[0] = array of headers. data[1] = first row of data. if(error) console.log("Unable to read the importSales.csv file:" + error); else { //Collect the mapping data. collectMetadata(data[0]); //Remove everything first. Sales.remove({"importTag": "2"}); let undefinedItems = {}; for(let i = 1; i < data.length; i++) { readRow(data[i], undefinedItems); } } })); //Collect the metadata from the first row of the CSV data - make a mapping. function collectMetadata(row) { let DATE = 'date'; let VENUE = 'vendor'; let ITEM = 'item'; let LBS = 'lbs'; let EACH = 'each'; let DOZ = 'dozen, large'; let TOTAL = 'total'; //Iterate over the columns to create a mapping. for(let i = 0; i < row.length; i++) { let next = row[i]; if(next && next != '') { switch(next.toLowerCase()) { case DATE: map.date = i; break; case VENUE: map.venue = i; break; case ITEM: map.item = i; break; case LBS: map.lbs = i; break; case EACH: map.each = i; break; case DOZ: map.doz = i; break; case TOTAL: map.total = i; break; } } } } //Reads a single row of CSV data and adds it to the database. function readRow(row, undefinedItems) { let date = moment(row[map.date], "M/D/YYYY").toDate(); let venue = row[map.venue] ? row[map.venue].toLowerCase() : undefined; let item = row[map.item] ? row[map.item].trim() : undefined; item = item ? item.toLowerCase() : undefined; let lbs = row[map.lbs] == undefined ? 0 : Number(row[map.lbs]); let each = row[map.each] == undefined ? 0 : Number(row[map.each]); let doz = row[map.doz] == undefined ? 0 : Number(row[map.doz]); let total = row[map.total] == undefined ? 0 : Number(row[map.total]); let venueId = venueIdMap[venue]; let itemId = itemIdMap[item]; let year = date.getFullYear(); if(venueId == undefined) { console.log("Found an undefined venue: " + venue); console.log(row); } else if(itemId == undefined) { console.log("Error: Could not find the item: '" + item + "'"); } else if(total == undefined || total <= 0) { console.log("Error: Invalid total '" + total + "' for the item: '" + item + "'"); } else if(!(lbs > 0 || each > 0 || doz > 0)) { console.log("Error: Invalid measures for the item: '" + item + "'"); } else { //Split it into multiple sales entries, one for each measure that has a positive value. if(lbs > 0) { let price = total / lbs; insertSale({date: date, amount: lbs, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['lbs']}); } if(each > 0) { let price = total / each; insertSale({date: date, amount: each, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['each']}); } if(doz > 0) { let price = total / doz; insertSale({date: date, amount: doz, price: price, venueId: venueId, productId: itemId, measureId: measureIdMap['dozen, large']}); } } } function insertSale(sale) { sale.createdAt = new Date(); sale.importTag = "2"; Sales.insert(sale, function(error) { if(error) console.log("Failed to insert the sale: " + JSON.stringify(sale) + "\n ERROR: " + error); }, {bypassCollection2: true}); } } });