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;
        }
示例#3
0
        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);
        }
示例#5
0
        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;
        }
示例#8
0
        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.");
        }
示例#9
0
        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);
        }
示例#10
0
        /// <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 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);
        }
示例#13
0
 public IngredientAggregation ConvertIngredientUsage(IngredientUsage usage)
 {
     throw new NotImplementedException();
 }
示例#14
0
        /// <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);
        }
示例#15
0
        /// <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.");
        }
示例#17
0
 public IngredientAggregation ConvertIngredientUsage(IngredientUsage usage)
 {
     throw new NotImplementedException();
 }
示例#18
0
        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);
               }
        }
示例#19
0
 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;
 }
示例#21
0
 public ProfileCreator AddPantryItem(IngredientUsage usage)
 {
     this.pantry.Add(new PantryItem(usage));
     return(this);
 }
示例#22
0
        /// <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));
        }
示例#24
0
        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;
        }
示例#25
0
        /// <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;
        }
示例#26
0
 public Match(string input, IngredientUsage usage)
     : base(input)
 {
     this.usage = usage;
 }
示例#27
0
        /// <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);
        }
示例#28
0
 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);
 }
示例#30
0
        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;
        }
示例#31
0
 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;
 }
示例#34
0
 public ShoppingListAddAction AddUsage(IngredientUsage usage)
 {
     adder.Usages.Add(usage);
      return this;
 }