angular.module('homepage').
factory('FoodNutritionSrvc', function (NutritionDataJSON) {
// Store list of foods with their nutrition details
var FoodsDataStore = {
foods: [],
drinks: []
};
/**
* Calculate matchindex based on the number of words matched against the foodname passed.
* @param {Array} titles List of titles all describing one food title
* @param {String} foodname Title of food entered by user
* @return {Object} bestmatch Best match matchindex and titleindex
*/
// TODO This doesnt account for errorneous names entered by user
// TODO This doesnt account for plural names
function matchFoodByName(titles, foodname) {
// Number of words matched against the foodname passed
var matchindex = 0; // Stores the current iteration matchindex
var bestmatch = { // Stores info about the past best match
matchindex: 0,
titleindex: -1,
lengthmatch: false
};
// match against all titles until matchindex and word count match for one title
for(var index in titles) {
var title = titles[index];
var words = title.split(" ");
var parts = foodname.split(" ");
// match words
for(var jndex in parts) {
var part = parts[jndex];
// TODO add polyfill added in ES6 though is an ES5 specification
// Array.indexOf() case insensitive
var isMatch = words.some(function(element) {
return part.toLowerCase() === element.toLowerCase();
});
if (isMatch) {
matchindex++;
}
}
// store in bestmatch if better than last match
if (bestmatch.matchindex < matchindex) {
bestmatch.matchindex = matchindex;
bestmatch.titleindex = parseInt(index);
}
// end search if matchindex and word count match
if(matchindex === parts.length) {
bestmatch.lengthmatch = true;
break;
}
}
return bestmatch;
}
/**
* Match foodname with all items titles in the list
* @param {Array} list [description]
* @param {String} foodname Title of food entered by user
* @return {Object} bestmatch Best match matchindex and titleindex
*/
function matchNames(list, foodname) {
var bestmatch = { // Stores info about the past best match
matchindex: 0,
titleindex: -1,
lengthmatch: false
};
for(var index in list) {
var titles = list[index].title;
var matchinfo = matchFoodByName(titles, foodname);
if(bestmatch.matchindex < matchinfo.matchindex) {
bestmatch.matchindex = matchinfo.matchindex;
bestmatch.titleindex = matchinfo.titleindex;
bestmatch.lengthmatch = matchinfo.lengthmatch;
if(bestmatch.lengthmatch === true) {
bestmatch.details = list[index];
break;
}
}
}
return bestmatch;
}
// Based on type array, look for foodname
// If not found in that array, look for other array(s)
function findMatch(foodtype, foodname) {
var bestmatch = { // Stores info about the past best match
matchindex: 0,
titleindex: -1
};
if(foodtype === 'FOOD') {
bestmatch = matchNames(FoodsDataStore.foods, foodname);
if (bestmatch.matchindex === 0) {
bestmatch = matchNames(FoodsDataStore.drinks, foodname);
}
}
else {
bestmatch = matchNames(FoodsDataStore.drinks, foodname);
if (bestmatch.matchindex === 0) {
bestmatch = matchNames(FoodsDataStore.foods, foodname);
}
}
return bestmatch;
}
/**
* Return conversion index based on the type entered by user w.r.t type in JSON
* possible values:
* FOOD - PCE, TSP, BWL, ONC, GRM
* DRNK - CUP, GLS, BTL, PTR, TWR, LTR
* @param {String} jsontype Type from the json
* @param {String} usertype Type entered by the user
* @return {Number} conversionindex conversion index
*/
function getConversionIndex(jsontype, usertype) {
var weightmap = {
'PCE': 10,
'TSP': 10,
'BWL': 100,
'ONC': 0.035,
'GRM': 1,
'CUP': 100,
'GLS': 200,
'BTL': 500,
'PTR': 1890,
'TWR': 3000,
'LTR': 1000
};
var conversionIndex = weightmap[usertype] / weightmap[jsontype];
return conversionIndex;
}
function calculateCalories(foodname, foodtype, quantity, qtytype) {
// Based on type array, look for foodname
// If not found in that array, look for other array(s)
// If no match found return X
var bestmatch = findMatch(foodtype, foodname);
// Once match is found, check if quantity type matches
// If it doesnt, use convertor to convert to nutrition type in json
// If it matches, calculate total calories, based on amount entered by user
if(bestmatch.matchindex > 0 && bestmatch.details !== undefined) {
var conversionIndex = getConversionIndex(bestmatch.details.qty.type, qtytype);
bestmatch.calories = conversionIndex * quantity * bestmatch.details.nutrition.calories;
}
else {
bestmatch.calories = bestmatch.details === undefined ? 0 : bestmatch.details.nutrition.calories;
}
return bestmatch;
}
/**
* Get Nutrition details from the food name
* @param {String} foodname [description]
* @param {String} foodtype [description]
* @param {Number} quantity [description]
* @param {String} qtytype [description]
* @return {Object} bestmatch object with bestmatch info and calorie count
*/
function getNutritionInfo(foodname, foodtype, quantity, qtytype) {
if(FoodsDataStore.foods.length === 0 || FoodsDataStore.drinks.length === 0) {
FoodsDataStore.foods = NutritionDataJSON.nutritionInfo.foods;
FoodsDataStore.drinks = NutritionDataJSON.nutritionInfo.drinks;
}
if(foodname === undefined) {
return { 'calories': 0 };
}
else {
return calculateCalories(foodname, foodtype, quantity, qtytype);
}
}
return {
getNutritionInfo: getNutritionInfo
};
}).
factory('NutritionDataJSON', function () {
// Keeping JSON inline so that there is no asyn call to be made, as it is problematic for implementing filters that use this service on load
// Stateful filter implementation is not changing data after first stable data, even though the filter is called with different params
var nutritionInfo = {"foods":[{"title":["Egg","Eggs","Egg boiled"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"80"}},{"title":["Egg Poached","Eggs Poached"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"80"}},{"title":["Egg Fried","Eggs Fried","Egg Fry"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"110"}},{"title":["Egg Omelet","Omelet","Omlate","Omelette","Egg Omelette"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"120"}},{"title":["Bread slice","Bread"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"45"}},{"title":["Bread slice with butter"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"90"}},{"title":["Chapati"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"60"}},{"title":["Puri"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"75"}},{"title":["Paratha"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"150"}},{"title":["Veggie"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"150"}},{"title":["Idli"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"100"}},{"title":["Dosa","Plain Dosa"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"120"}},{"title":["Masala Dosa"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"250"}},{"title":["Sambar","Sambhar","Dal"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"150"}},{"title":["Rajma Dal"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"140"}},{"title":["Rice","Plain Rice"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"120"}},{"title":["Fried Rice"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"150"}},{"title":["Nan"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"150"}},{"title":["Curd"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"100"}},{"title":["Veg Curry","Vegetable Curry"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":""}},{"title":["Salad"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"100"}},{"title":["Papad"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"45"}},{"title":["Cutlet"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"75"}},{"title":["Pickle"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"15"}},{"title":["Gobi Manchurian"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"60"}},{"title":["Soup"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"75"}},{"title":["Fruit Bowl","Fruit Salad","Fruits"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"BWL"},"nutrition":{"calories":"150"}},{"title":["Gulab Jamun"],"foodtype":{"type":"FOOD","region":"INDN"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"140"}},{"title":["Cucumber","Kheera","Kakhadi"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"8"}},{"title":["Carrot","Gajar"],"foodtype":{"type":"FOOD","region":"GLBL"},"qty":{"amount":"1","type":"PCE"},"nutrition":{"calories":"41"}}],"drinks":[{"title":["Tea"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"CUP"},"nutrition":{"calories":"45"}},{"title":["Black Tea"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"CUP"},"nutrition":{"calories":"15"},"description":"Without milk and sugar"},{"title":["Coffee"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"CUP"},"nutrition":{"calories":"45"}},{"title":["Black Coffee"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"CUP"},"nutrition":{"calories":"15"},"description":"Without milk and sugar"},{"title":["Milk"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"GLS"},"nutrition":{"calories":"65"}},{"title":["Protein Milk","Protein Milk Shake"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"CUP"},"nutrition":{"calories":"125"}},{"title":["Horlicks Milk"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"GLS"},"nutrition":{"calories":"120"}},{"title":["Fruit Juice"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"GLS"},"nutrition":{"calories":"120"}},{"title":["Soft Drink"],"foodtype":{"type":"DRNK","region":"INDN"},"qty":{"amount":"1","type":"GLS"},"nutrition":{"calories":"90"}},{"title":["Diet Coke"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"GLS"},"nutrition":{"calories":"0"}},{"title":["Maaza"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"CUP"},"nutrition":{"calories":"54"}},{"title":["Beer"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"BTL"},"nutrition":{"calories":"200"}},{"title":["Soda"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"BTL"},"nutrition":{"calories":"20"}},{"title":["Milk Shake"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"GLS"},"nutrition":{"calories":"200"}},{"title":["Mosambi Juice","Sweet Lime Juice"],"foodtype":{"type":"DRNK","region":"GLBL"},"qty":{"amount":"1","type":"GLS"},"nutrition":{"calories":"100"}}]};
return {
nutritionInfo: nutritionInfo
};
});