Files

1012 lines
38 KiB
JavaScript
Raw Permalink Normal View History

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});
}
}
});