/// <summary> /// Aggregates one or more recipes into a set of aggregated ingredients. This is normally used to create a list of items needed to buy for multiple recipes without having to create a shopping list. /// </summary> /// <param name="recipeIds">A list of recipe IDs to aggregate.</param> /// <returns>A list of IngredientAggregation objects, one per unique ingredient in the set of recipes</returns> public virtual IList <IngredientAggregation> AggregateRecipes(params Guid[] recipeIds) { using (InitLock.ReadLock()) { var ings = new Dictionary <Guid, IngredientAggregation>(); //List of all ingredients and total usage foreach (var id in recipeIds) { //Lookup ingredient through modeler cache var rNode = modeler.FindRecipe(id); if (rNode == null) //Our cache is out of date, skip this result { continue; } foreach (var usage in rNode.Ingredients) { var ingId = usage.Ingredient.IngredientId; var ingName = ingParser.GetIngredientById(ingId); var ing = new Ingredient(ingId, ingName); ing.ConversionType = usage.Ingredient.ConvType; IngredientAggregation agg; if (!ings.TryGetValue(ingId, out agg)) { ings.Add(ingId, agg = new IngredientAggregation(ing) { Amount = new Amount(0, usage.Unit) }); } //TODO: If usage.Unit is different than agg.Amount.Unit then we have a problem, throw an exception if that happens? if (agg.Amount == null) //This aggregation contains an empty amount, so we can't aggregate { continue; } else if (!usage.Amt.HasValue) //This amount is null, cancel aggregation { agg.Amount = null; } else { agg.Amount += usage.Amt.Value; } } } return(ings.Values.ToList()); } }
/// <summary> /// Converts a usage of an ingredient within a recipe to an IngredientAggregation object, suitable for aggregating with other usages of the same ingredient. /// </summary> /// <param name="usage">An IngredientUsage object, usually from a recipe.</param> /// <returns>An IngredientAggregation object, usually to be combined with other uses of that ingredient to form a shopping list.</returns> public virtual IngredientAggregation ConvertIngredientUsage(IngredientUsage usage) { //TODO: Does this method need to be part of the context? Perhaps IngredientUsage should have a method to convert to an aggregation var ing = ReadIngredient(usage.Ingredient.Id); if (ing == null) { throw new IngredientNotFoundException(); } var a = new IngredientAggregation(ing); a.AddUsage(usage); return(a); }