public static Amount GetWeightForUsage(IngredientUsage usage) { if (Unit.GetConvType(usage.Form.FormUnitType) == UnitType.Weight) //Already there, just convert to grams { return UnitConverter.Convert(usage.Amount, Units.Gram); } if (usage.Ingredient.ConversionType == UnitType.Weight) //Ingredient is sold in weight, so we can use its native amount { var amt = GetNativeAmountForUsage(usage.Ingredient, usage); return UnitConverter.Convert(amt, Units.Gram); } if (usage.Ingredient.ConversionType == UnitType.Unit && usage.Ingredient.UnitWeight > 0) //Ingredient sold in units, but we know weight of each { var amt = GetNativeAmountForUsage(usage.Ingredient, usage); amt.Unit = Units.Gram; amt *= usage.Ingredient.UnitWeight; return amt; } if (Unit.GetConvType(usage.Form.FormAmount.Unit) == UnitType.Weight && usage.Form.FormAmount.SizeHigh > 0) //This form has a gram weight { var amt = UnitConverter.Convert(usage.Amount, usage.Form.FormUnitType); return new Amount(amt.SizeHigh*usage.Form.FormAmount.SizeHigh, Units.Gram); } return null; }
public IngredientAdder AddIngredient(Ingredient ingredient, Amount amount, string prepNote = null) { var usage = new IngredientUsage(ingredient, null, amount, prepNote); var addedIngredient = this.AddIngredientUsage(usage); return addedIngredient; }
public void TestFormConverter() { //Form conversions (Unit ingredients) var unitIng = new Ingredient() {ConversionType = UnitType.Unit, UnitWeight = 200}; //Ingredient sold by units (unit weighs 200g) var unitIng_UnitForm = new IngredientForm() {FormAmount = new Amount(50, Units.Gram), ConversionMultiplier = 1, FormUnitType = Units.Unit}; //Form expressed in units (unit in this form weighs 50g) var unitIng_WeightForm = new IngredientForm() {ConversionMultiplier = 1, FormUnitType = Units.Ounce}; //Form expressed by weight var unitIng_VolForm = new IngredientForm() {FormUnitType = Units.Cup, ConversionMultiplier = 1, FormAmount = new Amount(20, Units.Gram)}; //Each cup weighs 20g) var unitIng_UnitUsage = new IngredientUsage() {Amount = new Amount(4, Units.Unit), Form = unitIng_UnitForm, Ingredient = unitIng}; var unitIng_WeightUsage = new IngredientUsage() {Amount = new Amount(300, Units.Gram), Form = unitIng_WeightForm, Ingredient = unitIng}; var unitIng_VolUsage = new IngredientUsage() {Amount = new Amount(160, Units.Tablespoon), Form = unitIng_VolForm, Ingredient = unitIng}; //10 cups var unitIng_UnitAmt = FormConversion.GetNativeAmountForUsage(unitIng, unitIng_UnitUsage); var unitIng_WeightAmt = FormConversion.GetNativeAmountForUsage(unitIng, unitIng_WeightUsage); var unitIng_VolAmt = FormConversion.GetNativeAmountForUsage(unitIng, unitIng_VolUsage); Assert.AreEqual(1.0f, unitIng_UnitAmt.SizeHigh); //4 units in this form should convert to 1 unit of ingredient Assert.AreEqual(Units.Unit, unitIng_UnitAmt.Unit); Assert.AreEqual(2.0f, unitIng_WeightAmt.SizeHigh); //300g of this form should convert to 1.5 units of ingredient, however we round up to whole units Assert.AreEqual(Units.Unit, unitIng_WeightAmt.Unit); //TODO: Fix //Assert.AreEqual(1.0f, unitIng_VolAmt.SizeHigh); //10 cups of this form should convert to 1 unit of ingredient //Assert.AreEqual(Units.Unit, unitIng_VolAmt.Unit); //Form conversions (Volume ingredients) var volIng = new Ingredient() {ConversionType = UnitType.Volume}; //Ingredient sold by volume var volIng_UnitForm = new IngredientForm() {FormUnitType = Units.Unit, ConversionMultiplier = 1, FormAmount = new Amount(5, Units.Teaspoon)}; var volIng_WeightForm = new IngredientForm() {FormUnitType = Units.Ounce, ConversionMultiplier = 1, FormAmount = new Amount(2, Units.Teaspoon)}; var volIng_UnitUsage = new IngredientUsage() {Amount = new Amount(2, Units.Unit), Form = volIng_UnitForm, Ingredient = volIng}; var volIng_WeightUsage = new IngredientUsage() {Amount = new Amount(0.25f, Units.Pound), Form = volIng_WeightForm, Ingredient = volIng}; //4oz var volIng_UnitAmt = FormConversion.GetNativeAmountForUsage(volIng, volIng_UnitUsage); var volIng_WeightAmt = FormConversion.GetNativeAmountForUsage(volIng, volIng_WeightUsage); Assert.AreEqual(10.0f, volIng_UnitAmt.SizeHigh); Assert.AreEqual(Units.Teaspoon, volIng_UnitAmt.Unit); Assert.AreEqual(8.0f, volIng_WeightAmt.SizeHigh); Assert.AreEqual(Units.Teaspoon, volIng_WeightAmt.Unit); //Form conversions (Weight ingredients) var weightIng = new Ingredient() {ConversionType = UnitType.Weight}; //Ingredient sold by weight var weightIng_UnitForm = new IngredientForm() {ConversionMultiplier = 1, FormUnitType = Units.Unit, FormAmount = new Amount(100, Units.Gram)}; var weightIng_VolForm = new IngredientForm() {ConversionMultiplier = 1, FormUnitType = Units.Cup, FormAmount = new Amount(50, Units.Gram)}; var weightIng_UnitUsage = new IngredientUsage() {Amount = new Amount(5, Units.Unit), Form = weightIng_UnitForm, Ingredient = weightIng}; var weightIng_VolUsage = new IngredientUsage() {Amount = new Amount(144, Units.Teaspoon), Form = weightIng_VolForm, Ingredient = weightIng}; //3 cups var weightIng_UnitAmt = FormConversion.GetNativeAmountForUsage(weightIng, weightIng_UnitUsage); var weightIng_VolAmt = FormConversion.GetNativeAmountForUsage(weightIng, weightIng_VolUsage); Assert.AreEqual(500.0f, weightIng_UnitAmt.SizeHigh); Assert.AreEqual(Units.Gram, weightIng_UnitAmt.Unit); Assert.AreEqual(150.0f, weightIng_VolAmt.SizeHigh); Assert.AreEqual(Units.Gram, weightIng_VolAmt.Unit); }
public override IngredientAggregation AddUsage(IngredientUsage usage) { if (Ingredient == null) throw new ArgumentException("Cannot add usage to a non-resolved shopping list item. Create a new shopping list based on an IngredientUsage."); return base.AddUsage(usage); }
public Recipe(Guid id, RecipeTags tags, IngredientUsage[] ingredients) { this.ingredients = new IngredientUsageCollection(); this.Id = id; this.Tags = tags; this.Ingredients = ingredients; }
public IngredientAdder AddIngredientUsage(IngredientUsage usage) { if (!String.IsNullOrWhiteSpace(section)) usage.Section = section; recipe.AddIngredient(usage); return this; }
public IngredientAdder AddIngredientUsage(IngredientUsage usage) { if (!string.IsNullOrWhiteSpace(this.section)) { usage.Section = this.section; } this.recipe.AddIngredient(usage); return this; }
public void TestDiet() { var recepy = Mock.Recipes.MockRecipe("Gluten Free Juice", "No Gluten Trust me!"); var ingredient = new IngredientUsage { Ingredient = Mock.Ingredients.SALT }; recepy.AddIngredient(ingredient); var categorization = this.engine.Categorize(recepy); Assert.IsTrue(categorization.DietGlutenFree, "Recipe should be classified as Gluten Free."); Assert.IsTrue(categorization.DietNoAnimals, "Recipe should be classified as No Animals."); Assert.IsTrue(categorization.DietNoMeat, "Recipe should be classified as No Meat."); Assert.IsTrue(categorization.DietNoPork, "Recipe should be classified as No Pork."); Assert.IsTrue(categorization.DietNoRedMeat, "Recipe should be classified as No Red Meat."); }
public void RecipeCreatorSetIngredients_ShouldSetRecipeIngredients() { var recipeCreator = new RecipeCreator(this.Context); var recipeCreator2 = new RecipeCreator(this.Context); var ingredients = new IngredientUsage[] { new IngredientUsage() }; var section = "Section"; recipeCreator.SetIngredients(ingredients); recipeCreator2.SetIngredients(section, ingredients); CollectionAssert.AreEqual(ingredients, recipeCreator.Recipe.Ingredients); CollectionAssert.AreEqual(ingredients, recipeCreator2.Recipe.Ingredients); }
/// <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); }
public virtual IngredientAggregation AddUsage(IngredientUsage ingredient) { if (ingredient.Ingredient.Id != this.Ingredient.Id) throw new ArgumentException("Can only call IngredientAggregation::AddUsage() on original ingredient."); //Calculate new total if (this.Amount.Unit == ingredient.Amount.Unit || UnitConverter.CanConvert(this.Amount.Unit, ingredient.Amount.Unit)) //Just add { this.Amount += ingredient.Amount; } else //Find a conversion path between Ingredient and Form { var amount = FormConversion.GetNativeAmountForUsage(this.Ingredient, ingredient); this.Amount += amount; } return this; // Allows AddUsage calls to be chained together }
public IngredientAggregation ConvertIngredientUsage(IngredientUsage usage) { throw new NotImplementedException(); }
/// <summary>Assembles a Result baesd on NLP match data for a given template.</summary> /// <param name="template">A passing NLP template that yieled match data</param> /// <param name="input">The original input string that was parsed</param> /// <param name="matchdata">Match data generated from the specified NLP template</param> /// <returns>Result describing the generated KitchenPC IngredientUsage or error code if usage could not be generated</returns> public static Result BuildResult(Template template, string input, MatchData matchdata) { var result = new IngredientUsage(); //Use this to hold partial matching data, but throw away if not a complete match. var ingName = (matchdata.Ingredient.Parent == null) ? matchdata.Ingredient.IngredientName : matchdata.Ingredient.Parent.IngredientName; result.Ingredient = new Ingredient(matchdata.Ingredient.Id, ingName); result.Ingredient.ConversionType = matchdata.Ingredient.ConversionType; result.Ingredient.UnitWeight = matchdata.Ingredient.UnitWeight; result.Amount = matchdata.Amount; result.PrepNote = matchdata.Preps.HasValue ? matchdata.Preps.ToString() : template.DefaultPrep; var pairings = matchdata.Ingredient.Pairings; NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Ingredient: {0}", matchdata.Ingredient.IngredientName); if (matchdata.Ingredient.Parent != null) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Re-Link to Root Ingredient: {0}", matchdata.Ingredient.Parent.IngredientName); } if (template.AllowPartial && matchdata.Amount == null) //No amount, any forms are now irrelevant { return new PartialMatch(input, result.Ingredient, result.PrepNote); } //If we parsed a custom unit (heads of lettuce), check if there's a mapping for that unit name to the ingredient and use that form if (matchdata.Unit is CustomUnitNode) { IngredientForm form; if (UnitSynonyms.TryGetFormForIngredient(matchdata.Unit.Name, matchdata.Ingredient.Id, out form)) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Based on unit name {0}, linking to form id {1}", matchdata.Unit.Name, form.FormId); result.Form = form; } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Unable to find link between unit '{0}' and ingredient '{1}'.", matchdata.Unit.Name, result.Ingredient.Name); return new NoMatch(input, MatchResult.UnknownUnit); //User specified a custom form that is not in any way linked to this ingredient, this is an error condition } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] No custom unit found, so cannot get form based on unit."); } //If we parsed a form alias (shredded), lookup the FormID from the formname/ingredient map (will override unit alias) if (matchdata.Form != null) { IngredientForm form; if (FormSynonyms.TryGetFormForIngredient(matchdata.Form.FormName, matchdata.Ingredient.Id, out form)) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Based on reference to form {0}, linking to form id {1}", matchdata.Form.FormName, form.FormId); result.Form = form; } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Unable to find link between form '{0}' and ingredient '{1}.", matchdata.Form.FormName, result.Ingredient.Name); return new NoMatch(input, MatchResult.UnknownForm); //User specified a form that is not in any way linked to this ingredient, this is an error condition } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] No known form found, so cannot get form based on form synonym."); } if (result.Form == null) //Load default form for parsed unit type { if (matchdata.Unit == null || matchdata.Unit.Unit == Units.Unit) //TODO: Is second part necessary? Only Units.Unit would be custom form types, and we'd have errored out already if that didn't match { result.Form = pairings.Unit; NlpTracer.ConditionalTrace(pairings.HasUnit, TraceLevel.Debug, "[BuildResult] Linking to default Unit paired form {0}", pairings.Unit); } else { switch (Unit.GetConvType(matchdata.Unit.Unit)) { case UnitType.Volume: result.Form = pairings.Volume; NlpTracer.ConditionalTrace(pairings.HasVolume, TraceLevel.Debug, "[BuildResult] Linking to default paired Volume form {0}", pairings.Volume); break; case UnitType.Weight: result.Form = pairings.Weight; NlpTracer.ConditionalTrace(pairings.HasWeight, TraceLevel.Debug, "[BuildResult] Linking to default paired Weight form {0}", pairings.Weight); break; } } if (result.Form == null && result.Amount.Unit == Units.Ounce && pairings.HasVolume) //Try as FluidOunces because so many recipes use oz when they mean fl oz { result.Form = pairings.Volume; result.Amount.Unit = Units.FluidOunce; NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Interpretting reference to Ounces as Fluid Ounces and linking to volumetric form {0}", pairings.Volume); } if (result.Form == null) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Could not find any default pairing for the unit type: {0}", result.Amount.Unit); } } //If we've loaded a form type and they're compatible, return match var parsedType = Unit.GetConvType(result.Amount.Unit); if (result.Form != null && parsedType == Unit.GetConvType(result.Form.FormUnitType)) { NlpTracer.Trace(TraceLevel.Info, "[BuildResult] SUCCESS: Linked form is compatible with usage reference."); return new Match(input, result); } // ************************** // *** ANOMALOUS PARSING **** // ************************** NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Running anomalous parsing."); // Prep to form fall-through: Allow prep to clarify form with volumetric usages if no default pairing is known, eg: 3 cups apples, chopped --> apples (chopped) : 3 cups // TODO: If matchdata has multiple prep notes, we either need to only parse the user entered one or avoid duplicate matches if (parsedType == UnitType.Volume && matchdata.Preps.HasValue) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Checking for form matching prep note: {0}", matchdata.Preps); IngredientForm form; if (FormSynonyms.TryGetFormForPrep(matchdata.Preps, matchdata.Ingredient, true, out form)) { result.Form = form; if (parsedType == Unit.GetConvType(result.Form.FormUnitType)) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] SUCCESS: Found matching volumetric form, allowing prep to form fall-through."); result.PrepNote = matchdata.Preps.ToString(); return new AnomalousMatch(input, AnomalousResult.Fallthrough, result); } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Found matching form, but form is not compatible with volumetric usage."); } } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Could not clarify form through prep note, since unit type is not volumetric or there is no prep note."); } // Auto Form Conversion: If we can make a valid assumption about a form even if unit type is incompatible, we can convert to another form, eg: 5oz shredded cheese --> cheese: 5oz (shredded) if (result.Form != null) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Form and unit incompatible - attempting to auto-convert form {0}", result.Form); var formType = Unit.GetConvType(result.Form.FormUnitType); if (parsedType == UnitType.Weight && formType == UnitType.Volume && pairings.HasWeight) //Something like 3oz shredded cheddar cheese, we need to use a weight form and set prep note { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] SUCCESS: Converting to default weight pairing, and setting prep note to: {0}", result.Form.FormDisplayName); result.PrepNote = result.Form.FormDisplayName; result.Form = pairings.Weight; return new AnomalousMatch(input, AnomalousResult.AutoConvert, result); } else if (parsedType == UnitType.Unit && formType == UnitType.Volume) //Something like 3 mashed bananas { if (pairings.HasUnit && (matchdata.Unit == null || String.IsNullOrEmpty(matchdata.Unit.Name))) //No custom unit, just use default pairing { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] SUCCESS: Converting to default unit pairing, and setting prep note to: {0}", result.Form.FormDisplayName); result.PrepNote = result.Form.FormDisplayName; result.Form = pairings.Unit; return new AnomalousMatch(input, AnomalousResult.AutoConvert, result); } if (matchdata.Unit != null && false == String.IsNullOrEmpty(matchdata.Unit.Name)) //We have a custom unit { IngredientForm form; NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Attempting to convert volumetric usage to unit form for custom unit: {0}", matchdata.Unit.Name); if (UnitSynonyms.TryGetFormForIngredient(matchdata.Unit.Name, matchdata.Ingredient.Id, out form)) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] SUCCESS: Converting to custom unit pairing, and setting prep note to: {0}", result.Form.FormDisplayName); result.PrepNote = result.Form.FormDisplayName; result.Form = form; return new AnomalousMatch(input, AnomalousResult.AutoConvert, result); } } } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Could not auto-convert form since there is no form to convert."); } //Error out if (result.Form == null) //Still have not found a form, we give up now { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Anomalous parsing could still not find a form for this usage."); return new NoMatch(input, MatchResult.NoForm); //No default form pairings, so return NoForm } NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Anomalous parsing could not fix form/unit incompatibility."); return new NoMatch(input, MatchResult.IncompatibleForm); }
/// <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 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; }
public static Amount GetNativeAmountForUsage(Ingredient ingredient, IngredientUsage usage) { var amount = new Amount(); var usageConvType = Unit.GetConvertedType(usage.Form.FormUnitType); // This is the type we must convert to switch (ingredient.ConversionType) { case UnitType.Unit: amount.Unit = Units.Unit; // Unit to unit version if (usageConvType == UnitType.Unit) { // Grams this form is equivelent to var equivelentGrams = UnitConverter.Convert(usage.Form.FormAmount, Units.Gram); amount.SizeHigh = (float)Math.Ceiling((equivelentGrams.SizeHigh * usage.Amount.SizeHigh) / ingredient.UnitWeight); return amount; } // Weight to unit conversion if (usageConvType == UnitType.Weight) { var grams = UnitConverter.Convert(usage.Amount, Units.Gram); amount.SizeHigh = (float)Math.Ceiling(grams.SizeHigh / ingredient.UnitWeight); return amount; } // Volume to unit conversion if (usageConvType == UnitType.Volume) { var likeAmount = UnitConverter.Convert(usage.Amount, usage.Form.FormUnitType); // Round up when dealing with whole units amount.SizeHigh = (float)Math.Ceiling((likeAmount.SizeHigh * usage.Form.FormAmount.SizeHigh) / usage.Ingredient.UnitWeight); return amount; } break; case UnitType.Weight: amount.Unit = Units.Gram; // Unit to weight conversion if (usageConvType == UnitType.Unit) { // NOTE: FormAmount will always be in Grams when Ingredient ConvType is weight amount.SizeHigh = usage.Amount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } // Volume to weight conversion if (usageConvType == UnitType.Volume) { var likeAmount = UnitConverter.Convert(usage.Amount, usage.Form.FormUnitType); // NOTE: FormAmount will always be in Grams when Ingredient ConvType is weight amount.SizeHigh = likeAmount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } break; case UnitType.Volume: amount.Unit = Units.Teaspoon; // Unit to volume conversion if (usageConvType == UnitType.Unit) { // NOTE: FormAmount will always be in tsp when Ingredient ConvType is volume amount.SizeHigh = usage.Amount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } // Weight to volume conversion if (usageConvType == UnitType.Weight) { var likeAmount = UnitConverter.Convert(usage.Amount, usage.Form.FormUnitType); // NOTE: FormAmount will always be in teaspoons when Ingredient ConvType is Volume amount.SizeHigh = likeAmount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } break; } throw new Exception("Cannot convert an IngredientUsage into its native form."); }
private static void LoadDefaultForm(MatchData matchdata, IngredientUsage result, DefaultPairings pairings) { if (matchdata.Unit == null || matchdata.Unit.Unit == Units.Unit) //TODO: Is second part necessary? Only Units.Unit would be custom form types, and we'd have errored out already if that didn't match { result.Form = pairings.Unit; NlpTracer.ConditionalTrace(pairings.HasUnit, TraceLevel.Debug, "[BuildResult] Linking to default Unit paired form {0}", pairings.Unit); } else { switch (Unit.GetConvType(matchdata.Unit.Unit)) { case UnitType.Volume: result.Form = pairings.Volume; NlpTracer.ConditionalTrace(pairings.HasVolume, TraceLevel.Debug, "[BuildResult] Linking to default paired Volume form {0}", pairings.Volume); break; case UnitType.Weight: result.Form = pairings.Weight; NlpTracer.ConditionalTrace(pairings.HasWeight, TraceLevel.Debug, "[BuildResult] Linking to default paired Weight form {0}", pairings.Weight); break; } } if (result.Form == null && result.Amount.Unit == Units.Ounce && pairings.HasVolume) //Try as FluidOunces because so many recipes use oz when they mean fl oz { result.Form = pairings.Volume; result.Amount.Unit = Units.FluidOunce; NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Interpretting reference to Ounces as Fluid Ounces and linking to volumetric form {0}", pairings.Volume); } if (result.Form == null) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Could not find any default pairing for the unit type: {0}", result.Amount.Unit); } }
private static bool ProcessMatchDataNullForm(string input, MatchData matchdata, IngredientUsage result, out Result buildResultNullForm) { IngredientForm form; if (FormSynonyms.TryGetFormForIngredient(matchdata.Form.FormName, matchdata.Ingredient.Id, out form)) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Based on reference to form {0}, linking to form id {1}", matchdata.Form.FormName, form.FormId); result.Form = form; } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Unable to find link between form '{0}' and ingredient '{1}.", matchdata.Form.FormName, result.Ingredient.Name); { buildResultNullForm = new NoMatch(input, MatchResult.UnknownForm); return true; } } buildResultNullForm = null; return false; }
public IngredientUsageCreator(IngredientUsage usage) { this.Usage = usage; }
public ProfileCreator AddPantryItem(IngredientUsage usage) { this.pantry.Add(new PantryItem(usage)); return(this); }
/// <summary>Assembles a Result baesd on NLP match data for a given template.</summary> /// <param name="template">A passing NLP template that yieled match data</param> /// <param name="input">The original input string that was parsed</param> /// <param name="matchdata">Match data generated from the specified NLP template</param> /// <returns>Result describing the generated KitchenPC IngredientUsage or error code if usage could not be generated</returns> public static Result BuildResult(Template template, string input, MatchData matchdata) { var result = new IngredientUsage(); //Use this to hold partial matching data, but throw away if not a complete match. var ingName = (matchdata.Ingredient.Parent == null) ? matchdata.Ingredient.IngredientName : matchdata.Ingredient.Parent.IngredientName; result.Ingredient = new Ingredient(matchdata.Ingredient.Id, ingName); result.Ingredient.ConversionType = matchdata.Ingredient.ConversionType; result.Ingredient.UnitWeight = matchdata.Ingredient.UnitWeight; result.Amount = matchdata.Amount; result.PrepNote = matchdata.Preps.HasValue ? matchdata.Preps.ToString() : template.DefaultPrep; var pairings = matchdata.Ingredient.Pairings; NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Ingredient: {0}", matchdata.Ingredient.IngredientName); if (matchdata.Ingredient.Parent != null) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Re-Link to Root Ingredient: {0}", matchdata.Ingredient.Parent.IngredientName); } if (template.AllowPartial && matchdata.Amount == null) //No amount, any forms are now irrelevant { return new PartialMatch(input, result.Ingredient, result.PrepNote); } //If we parsed a custom unit (heads of lettuce), check if there's a mapping for that unit name to the ingredient and use that form if (matchdata.Unit is CustomUnitNode) { Result noMatch; bool hasReturned = ProcessCustimUnitNode(input, matchdata, result, out noMatch); if (hasReturned) { return noMatch; } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] No custom unit found, so cannot get form based on unit."); } //If we parsed a form alias (shredded), lookup the FormID from the formname/ingredient map (will override unit alias) if (matchdata.Form != null) { Result MatchDataFormResult; bool hasReturned = ProcessMatchDataNullForm(input, matchdata, result, out MatchDataFormResult); if (hasReturned) { return MatchDataFormResult; } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] No known form found, so cannot get form based on form synonym."); } if (result.Form == null) //Load default form for parsed unit type { LoadDefaultForm(matchdata, result, pairings); } //If we've loaded a form type and they're compatible, return match var parsedType = Unit.GetConvType(result.Amount.Unit); if (result.Form != null && parsedType == Unit.GetConvType(result.Form.FormUnitType)) { NlpTracer.Trace(TraceLevel.Info, "[BuildResult] SUCCESS: Linked form is compatible with usage reference."); return new Match(input, result); } // ************************** // *** ANOMALOUS PARSING **** // ************************** NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Running anomalous parsing."); // Prep to form fall-through: Allow prep to clarify form with volumetric usages if no default pairing is known, eg: 3 cups apples, chopped --> apples (chopped) : 3 cups // TODO: If matchdata has multiple prep notes, we either need to only parse the user entered one or avoid duplicate matches if (parsedType == UnitType.Volume && matchdata.Preps.HasValue) { Result anomalousMatch; bool hasReturned = ProcessAnomalousMatch(input, matchdata, result, parsedType, out anomalousMatch); if (hasReturned) { return anomalousMatch; } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Could not clarify form through prep note, since unit type is not volumetric or there is no prep note."); } // Auto Form Conversion: If we can make a valid assumption about a form even if unit type is incompatible, we can convert to another form, eg: 5oz shredded cheese --> cheese: 5oz (shredded) if (result.Form != null) { Result AutoConversionResult; bool wasConverted = AutoFormConvert(input, matchdata, result, parsedType, pairings, out AutoConversionResult); if (wasConverted) { return AutoConversionResult; } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Could not auto-convert form since there is no form to convert."); } //Error out if (result.Form == null) //Still have not found a form, we give up now { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Anomalous parsing could still not find a form for this usage."); return new NoMatch(input, MatchResult.NoForm); //No default form pairings, so return NoForm } NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Anomalous parsing could not fix form/unit incompatibility."); return new NoMatch(input, MatchResult.IncompatibleForm); }
public static Amount GetNativeAmountForUsage(Ingredient ingredient, IngredientUsage usage) { var amount = new Amount(); var usageConvertionType = Unit.GetConvertionType(usage.Form.FormUnitType); switch (ingredient.ConversionType) { case UnitType.Unit: { amount.Unit = Units.Unit; switch (usageConvertionType) { case UnitType.Unit: { // Unit to unit version var equivalentGrams = UnitConverter.Convert(usage.Form.FormAmount, Units.Gram); amount.SizeHigh = (float)Math.Ceiling((equivalentGrams.SizeHigh * usage.Amount.SizeHigh) / ingredient.UnitWeight); return amount; } case UnitType.Volume: { // Volume to unit conversion var likeAmount = UnitConverter.Convert(usage.Amount, usage.Form.FormUnitType); amount.SizeHigh = (float)Math.Ceiling((likeAmount.SizeHigh * usage.Form.FormAmount.SizeHigh) / usage.Ingredient.UnitWeight); return amount; } case UnitType.Weight: { // Weight to unit conversion var grams = UnitConverter.Convert(usage.Amount, Units.Gram); amount.SizeHigh = (float)Math.Ceiling(grams.SizeHigh / ingredient.UnitWeight); return amount; } } break; } case UnitType.Weight: { amount.Unit = Units.Gram; switch (usageConvertionType) { case UnitType.Unit: { // Unit to weight conversion amount.SizeHigh = usage.Amount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } case UnitType.Volume: { // Volume to weight conversion var likeAmount = UnitConverter.Convert(usage.Amount, usage.Form.FormUnitType); amount.SizeHigh = likeAmount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } } break; } case UnitType.Volume: { amount.Unit = Units.Teaspoon; switch (usageConvertionType) { case UnitType.Unit: { // Unit to volume conversion amount.SizeHigh = usage.Amount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } case UnitType.Weight: { // Weight to volume conversion var likeAmount = UnitConverter.Convert(usage.Amount, usage.Form.FormUnitType); amount.SizeHigh = likeAmount.SizeHigh * usage.Form.FormAmount.SizeHigh; return amount; } } break; } } throw new IngredientAggregationDatabaseException(string.Format("Cannot convert an IngredientUsage into its native form.", ingredient, usage)); }
public static Recipe MockRecipe(string title, string desc) { var ret = new Recipe { Id = Guid.NewGuid(), Title = title, Description = desc, Tags = new RecipeTags(RecipeTag.Breakfast) }; ret.Method = "This is a mock recipe."; ret.OwnerAlias = "Fake Owner"; ret.OwnerId = Guid.NewGuid(); ret.Permalink = "http://www.kitchenpc.com/123"; ret.ServingSize = 5; var ingredient = new IngredientUsage { Ingredient = Mock.Ingredients.SALT }; ret.Ingredients = new IngredientUsage[1] { ingredient }; return ret; }
/// <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 ingredient = this.ReadIngredient(usage.Ingredient.Id); if (ingredient == null) { throw new IngredientNotFoundException(); } var aggregation = new IngredientAggregation(ingredient); aggregation.AddUsage(usage); return aggregation; }
public Match(string input, IngredientUsage usage) : base(input) { this.usage = usage; }
/// <summary> /// Creates a new shopping list for the current user. /// </summary> /// <param name="name">The name of the new shopping list.</param> /// <param name="recipes">Zero or more recipes to add to this list.</param> /// <param name="ingredients">Zero or more ingredients to add to this list.</param> /// <param name="usages">Zero or more ingredient usages to add to this list.</param> /// <param name="items">Zero or more raw usages. Raw usages will be parsed using NLP, and unsuccessful matches will be added to the list as raw items.</param> /// <returns>A fully aggregated shopping list, with like items combined and forms normalized.</returns> public virtual ShoppingListResult CreateShoppingList( string name, Recipe[] recipes, Ingredient[] ingredients, IngredientUsage[] usages, string[] items) { var parsedIngredients = Parser.ParseAll(items).ToList(); var recipeAggregation = this.AggregateRecipes(recipes.Select(r => r.Id).ToArray()); var ingredientsAggregation = ingredients.Select(i => new IngredientAggregation(i, null)); var ingredientUsages = this.AggregateIngredients(usages); var parsedUsages = this.AggregateIngredients(parsedIngredients.Where(u => u is Match).Select(u => u.Usage).ToArray()); var rawInputs = parsedIngredients.Where(u => u is NoMatch).Select(u => new ShoppingListItem(u.Input)); var allItems = recipeAggregation .Concat(ingredientsAggregation) .Concat(ingredientUsages) .Concat(parsedUsages) .Concat(rawInputs); var list = new ShoppingList(null, name, allItems); return this.CreateShoppingList(list); }
private static bool ProcessCustimUnitNode(string input, MatchData matchdata, IngredientUsage result, out Result noMatch) { IngredientForm form; if (UnitSynonyms.TryGetFormForIngredient(matchdata.Unit.Name, matchdata.Ingredient.Id, out form)) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Based on unit name {0}, linking to form id {1}", matchdata.Unit.Name, form.FormId); result.Form = form; } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] ERROR: Unable to find link between unit '{0}' and ingredient '{1}'.", matchdata.Unit.Name, result.Ingredient.Name); { noMatch = new NoMatch(input, MatchResult.UnknownUnit); return true; } } noMatch = null; return false; }
public void AddIngredient(IngredientUsage ingredient) { _ingredients.Add(ingredient); }
private static bool ProcessAnomalousMatch(string input, MatchData matchdata, IngredientUsage result, UnitType parsedType, out Result anomalousMatch) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Checking for form matching prep note: {0}", matchdata.Preps); IngredientForm form; if (FormSynonyms.TryGetFormForPrep(matchdata.Preps, matchdata.Ingredient, true, out form)) { result.Form = form; if (parsedType == Unit.GetConvType(result.Form.FormUnitType)) { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] SUCCESS: Found matching volumetric form, allowing prep to form fall-through."); result.PrepNote = matchdata.Preps.ToString(); { anomalousMatch = new AnomalousMatch(input, AnomalousResult.Fallthrough, result); return true; } } else { NlpTracer.Trace(TraceLevel.Debug, "[BuildResult] Found matching form, but form is not compatible with volumetric usage."); } } anomalousMatch = null; return false; }
public ShoppingListResult CreateShoppingList(string name, Recipe[] recipes, Ingredient[] ingredients, IngredientUsage[] usages, string[] items) { throw new NotImplementedException(); }
public AnomalousMatch(string input, AnomalousResult anomaly, IngredientUsage usage) : base(input, usage) { this.anomaly = anomaly; }
public ProfileCreator AddPantryItem(IngredientUsage usage) { pantry.Add(new PantryItem(usage)); return this; }
public ShoppingListAddAction AddUsage(IngredientUsage usage) { adder.Usages.Add(usage); return this; }