def main(): # Define Grains pale = Grain(u"Pale Malt (2 Row) US", color=1.8, ppg=37) pale_add = GrainAddition(pale, weight=13.96) crystal = Grain(u"Caramel/Crystal Malt - 20L", color=20.0, ppg=35) crystal_add = GrainAddition(crystal, weight=0.78) grain_additions = [pale_add, crystal_add] # Define Hops centennial = Hop(u"Centennial", percent_alpha_acids=0.14) centennial_add = HopAddition(centennial, weight=0.57, boil_time=60.0) cascade = Hop(u"Cascade (US)", percent_alpha_acids=0.07) cascade_add = HopAddition(cascade, weight=0.76, boil_time=5.0) hop_additions = [centennial_add, cascade_add] # Define Yeast yeast = Yeast(u"Wyeast 1056") # Define Recipe beer = Recipe( u"pale ale", grain_additions=grain_additions, hop_additions=hop_additions, yeast=yeast, brew_house_yield=0.70, # % start_volume=7.0, # G final_volume=5.0, # G ) print(beer.format())
def setUp(self): # A special recipe is needed since the loaders only return # pre-chosen additions self.recipe = Recipe( name=u"pale ale", grain_additions=[pale_add, pale_add], hop_additions=[cascade_add, cascade_add], yeast=yeast, brew_house_yield=0.70, # % start_volume=7.0, # G final_volume=5.0, # G ) self.recipe_data = self.recipe.to_dict() self.cereals_loader = CerealsLoader("./") self.hops_loader = HopsLoader("./") self.yeast_loader = YeastLoader("./")
class TestRecipeParser(unittest.TestCase): def setUp(self): # A special recipe is needed since the loaders only return # pre-chosen additions self.recipe = Recipe(name=u'pale ale', grain_additions=[pale_add, pale_add], hop_additions=[cascade_add, cascade_add], yeast=yeast, percent_brew_house_yield=0.70, # % start_volume=7.0, # G final_volume=5.0, # G ) self.recipe_data = self.recipe.to_dict() self.cereals_loader = CerealsLoader('./') self.hops_loader = HopsLoader('./') self.yeast_loader = YeastLoader('./') def test_parse_recipe(self): out = parse_recipe(self.recipe_data, None, cereals_loader=self.cereals_loader, hops_loader=self.hops_loader, yeast_loader=self.yeast_loader) self.assertEquals(out, self.recipe) def test_parse_recipe_default_loader(self): out = parse_recipe(self.recipe_data, DataLoader('./'), cereals_dir_suffix='/', hops_dir_suffix='/', yeast_dir_suffix='/') self.assertEquals(out, self.recipe)
def main(): # Define Grains pale = Grain(u"Pale Malt (2 Row) US", color=1.8, ppg=37) rye = Grain(u"Rye Malt", color=3.5, ppg=38) crystal = Grain(u"Caramel/Crystal - 60L", color=60, ppg=34) carapils = Grain(u"Carapils/Dextrine Malt", color=1.8, ppg=33) flaked_wheat = Grain(u"Flaked Wheat", color=2, ppg=34) pale_add = GrainAddition(pale, weight=13.96) rye_add = GrainAddition(rye, weight=3.0) crystal_add = GrainAddition(crystal, weight=1.25) carapils_add = GrainAddition(carapils, weight=0.5) flaked_wheat_add = GrainAddition(flaked_wheat, weight=0.5) grain_additions = [ pale_add, rye_add, crystal_add, carapils_add, flaked_wheat_add ] # Define Hops mt_hood = Hop(u"Mount Hood", percent_alpha_acids=0.048) columbus = Hop(u"Columbus", percent_alpha_acids=0.15) mt_hood_add_01 = HopAddition(mt_hood, weight=1.0, boil_time=60.0) columbus_add_01 = HopAddition(columbus, weight=1.0, boil_time=60.0) mt_hood_add_02 = HopAddition(mt_hood, weight=0.5, boil_time=30.0) mt_hood_add_03 = HopAddition(mt_hood, weight=1.5, boil_time=1.0) columbus_add_02 = HopAddition(columbus, weight=1.0, boil_time=0.0) hop_additions = [ mt_hood_add_01, columbus_add_01, mt_hood_add_02, mt_hood_add_03, columbus_add_02 ] # Define Yeast yeast = Yeast(u"Wyeast 1450", percent_attenuation=0.75) # Define Recipe beer = Recipe( u"pale ale", grain_additions=grain_additions, hop_additions=hop_additions, yeast=yeast, brew_house_yield=0.70, # % start_volume=7.5, # G final_volume=5.5, # G ) print(beer.format())
def main(): # Define Grains pale = Grain(u'Pale Malt (2 Row) US', color=1.8, ppg=37) pale_add = GrainAddition(pale, weight=13.96) crystal = Grain(u'Caramel/Crystal Malt - 20L', color=20.0, ppg=35) crystal_add = GrainAddition(crystal, weight=0.78) grain_additions = [pale_add, crystal_add] # Define Hops centennial = Hop(u'Centennial', percent_alpha_acids=0.14) centennial_add = HopAddition(centennial, weight=0.57, boil_time=60.0) cascade = Hop(u'Cascade (US)', percent_alpha_acids=0.07) cascade_add = HopAddition(cascade, weight=0.76, boil_time=5.0) hop_additions = [centennial_add, cascade_add] # Define Yeast yeast = Yeast(u'Wyeast 1056') # Define Recipe beer = Recipe(u'pale ale', grain_additions=grain_additions, hop_additions=hop_additions, yeast=yeast, percent_brew_house_yield=0.70, # % start_volume=7.0, # G final_volume=5.0, # G ) print(beer.format())
def test_hops_units_mismatch_raises(self): hop_additions = [h.change_units() for h in self.hop_additions] with self.assertRaises(RecipeException) as ctx: Recipe( name=u"pale ale", grain_additions=self.grain_additions, hop_additions=hop_additions, yeast=self.yeast, ) self.assertEquals( str(ctx.exception), u"pale ale: Hop addition units must be in 'imperial' not 'metric'", ) # noqa
def test_recipe_matches(self): pale_add = GrainAddition(pale, weight=8.69) crystal_add = GrainAddition(crystal, weight=1.02) pale_ale = Recipe( name=u"pale ale", grain_additions=[pale_add, crystal_add], hop_additions=hop_additions, yeast=yeast, brew_house_yield=0.70, start_volume=7.0, final_volume=5.0, ) out = self.style.recipe_matches(pale_ale) self.assertTrue(out)
def test_unicode(self): recipe = Recipe( name=u"Kölsch Ale", grain_additions=grain_additions, hop_additions=hop_additions, yeast=yeast, brew_house_yield=0.70, start_volume=7.0, final_volume=5.0, ) out = str(recipe) if sys.version_info[0] >= 3: self.assertEquals(out, u"Kölsch Ale") else: self.assertEquals(out, u"Kölsch Ale".encode("utf8"))
def test_get_total_points_dme(self): pale_dme_add = GrainAddition(pale_dme, weight=11.74, grain_type=GRAIN_TYPE_DME, units=IMPERIAL_UNITS) recipe = Recipe( u"dme", grain_additions=[pale_dme_add], hop_additions=self.hop_additions, yeast=self.yeast, units=IMPERIAL_UNITS, ) out = recipe.get_total_points() self.assertEquals(round(out, 1), 516.6)
def setUp(self): # A special recipe is needed since the loaders only return # pre-chosen additions self.recipe = Recipe(name=u'pale ale', grain_additions=[pale_add, pale_add], hop_additions=[cascade_add, cascade_add], yeast=yeast, percent_brew_house_yield=0.70, # % start_volume=7.0, # G final_volume=5.0, # G ) self.recipe_data = self.recipe.to_dict() self.cereals_loader = CerealsLoader('./') self.hops_loader = HopsLoader('./') self.yeast_loader = YeastLoader('./')
def test_get_grain_add_dry_weight_lme(self): pale_lme_add = GrainAddition(pale_lme, weight=14.35, grain_type=GRAIN_TYPE_LME, units=IMPERIAL_UNITS) recipe = Recipe( u"lme", grain_additions=[pale_lme_add], hop_additions=self.hop_additions, yeast=self.yeast, units=IMPERIAL_UNITS, ) out = recipe.get_grain_add_dry_weight(pale_lme_add) self.assertEquals(round(out, 2), 11.74)
def test_recipe_errors_none(self): pale_add = GrainAddition(pale, weight=8.69) crystal_add = GrainAddition(crystal, weight=1.02) pale_ale = Recipe( name=u"pale ale", grain_additions=[pale_add, crystal_add], hop_additions=hop_additions, yeast=yeast, brew_house_yield=0.70, start_volume=7.0, final_volume=5.0, ) out = self.style.recipe_errors(pale_ale) expected = [] self.assertEquals(out, expected)
def test_get_grain_add_cereal_weight_dme(self): pale_dme_add = GrainAddition(pale_dme, weight=11.74, grain_type=GRAIN_TYPE_DME, units=IMPERIAL_UNITS) recipe = Recipe( u"dme", grain_additions=[pale_dme_add], hop_additions=self.hop_additions, yeast=self.yeast, units=IMPERIAL_UNITS, ) out = recipe.get_grain_add_cereal_weight(pale_dme_add, ppg=ppg_pale) self.assertEquals(round(out, 2), 13.96)
class TestRecipeParser(unittest.TestCase): def setUp(self): # A special recipe is needed since the loaders only return # pre-chosen additions self.recipe = Recipe( name=u"pale ale", grain_additions=[pale_add, pale_add], hop_additions=[cascade_add, cascade_add], yeast=yeast, brew_house_yield=0.70, # % start_volume=7.0, # G final_volume=5.0, # G ) self.recipe_data = self.recipe.to_dict() self.cereals_loader = CerealsLoader("./") self.hops_loader = HopsLoader("./") self.yeast_loader = YeastLoader("./") def test_parse_recipe(self): out = parse_recipe( self.recipe_data, None, cereals_loader=self.cereals_loader, hops_loader=self.hops_loader, yeast_loader=self.yeast_loader, ) self.assertEquals(out, self.recipe) def test_parse_recipe_default_loader(self): out = parse_recipe( self.recipe_data, DataLoader("./"), cereals_dir_suffix="/", hops_dir_suffix="/", yeast_dir_suffix="/", ) self.assertEquals(out, self.recipe)
def test_ne_hop_additions(self): recipe1 = Recipe(u"pale ale", hop_additions=hop_additions) recipe2 = Recipe(u"pale ale", hop_additions=[hop_additions[0]]) self.assertTrue(recipe1 != recipe2)
def test_validate(self): data = self.recipe.to_dict() Recipe.validate(data)
def main(): # Constants name = u"Sagefight Imperial IPA" brew_house_yield = 0.70 start_volume = 4.0 final_volume = 5.0 # Grains pale = Grain(u"pale 2-row", color=2.0, ppg=36.0) crystal = Grain(u"crystal 20", color=2.0, ppg=35.0) munich = Grain(u"munich", color=9.0, ppg=37.0) grain_list = [pale, crystal, munich] # Hops # Mild, herbaceous, elements of resin millennium = Hop(name=u"millennium", percent_alpha_acids=0.14) # Spicy, earthy, and lightly floral bravo = Hop(name=u"bravo", percent_alpha_acids=0.15) # Orange Citrus Flavor amarillo = Hop(name=u"amarillo", percent_alpha_acids=0.09) # Floral, with elements of citrus and notes of grapefruit # noqa centennial = Hop(name=u"centennial", percent_alpha_acids=0.10) hop_list = [millennium, bravo, amarillo, centennial] hop_list = hop_list[:3] # Define Recipe Builder builder = RecipeBuilder( name=name, grain_list=grain_list, hop_list=hop_list, target_ibu=75.0, target_og=1.075, brew_house_yield=brew_house_yield, start_volume=start_volume, final_volume=final_volume, ) # Get grain additions grain_percentages = [0.80, 0.10, 0.10] grain_additions = builder.get_grain_additions(grain_percentages) # Get hop additions hop_percentages = [0.50, 0.30, 0.20] hop_boil_times = [90, 15, 5] hop_additions = builder.get_hop_additions(hop_percentages, hop_boil_times) # Determine desired attenuation desired_attenuation = builder.get_yeast_attenuation(0.08) yeast = Yeast("English Ale", percent_attenuation=desired_attenuation) # Convert first grain addition to LME pale_lme = grain_additions[0].convert_to_lme(brew_house_yield=brew_house_yield) new_grain_additions = [pale_lme] + grain_additions[1:] # Create the recipe recipe = Recipe( name=name, grain_additions=new_grain_additions, hop_additions=hop_additions, yeast=yeast, brew_house_yield=brew_house_yield, start_volume=start_volume, final_volume=final_volume, ) print(recipe.format()) print("\nAdjuncts") print("===================================") print("- 0.66 oz Juniper Berries (crushed), 1/3 to boil at 10 min, 2/3 at knockout") print("- 0.25 oz Sage, 2/3 to boil at 10 min, 1/3 at knockout")
cascade = Hop(name=u"cascade", percent_alpha_acids=0.07) hop_list = [centennial, cascade] centennial_add = HopAddition(centennial, boil_time=60.0, weight=0.57) cascade_add = HopAddition(cascade, boil_time=5.0, weight=0.76) hop_additions = [centennial_add, cascade_add] # Define Yeast yeast = Yeast(u"Wyeast 1056") # Define Recipes recipe = Recipe( name=u"pale ale", grain_additions=grain_additions, hop_additions=hop_additions, yeast=yeast, brew_house_yield=BHY, start_volume=7.0, final_volume=5.0, ) recipe_lme = Recipe( name=u"pale ale lme", grain_additions=grain_additions_lme, hop_additions=hop_additions, yeast=yeast, brew_house_yield=0.70, start_volume=7.0, final_volume=5.0, )
def parse_recipe(recipe, loader, cereals_loader=None, hops_loader=None, yeast_loader=None, cereals_dir_suffix='cereals/', hops_dir_suffix='hops/', yeast_dir_suffix='yeast/'): """ Parse a recipe from a python Dict :param dict recipe: A representation of a recipe :param DataLoader loader: A class to load additional information :param DataLoader cereal_loader: A class to load additional information specific to cereals :param DataLoader hops_loader: A class to load additional information specific to hops :param DataLoader yeast_loader: A class to load additional information specific to yeast A recipe must have the following top level attributes: * name (str) * start_volume (float) * final_volume (float) * grains (list(dict)) * hops (list(dict)) * yeast (dict) Additionally the recipe may contain override data in the 'data' attribute with the following keys: * percent_brew_house_yield (float) * units (str) All other fields will be ignored and may be used for other metadata. The dict objects in the grains, hops, and yeast values are required to have the key 'name' and the remaining attributes will be looked up in the data directory if they are not provided. """ # noqa if cereals_loader is None: cereals_loader = loader if hops_loader is None: hops_loader = loader if yeast_loader is None: yeast_loader = loader Recipe.validate(recipe) grain_additions = [] for grain in recipe[u'grains']: grain_additions.append(parse_cereals(grain, cereals_loader, dir_suffix=cereals_dir_suffix)) hop_additions = [] for hop in recipe[u'hops']: hop_additions.append(parse_hops(hop, hops_loader, dir_suffix=hops_dir_suffix)) yeast = parse_yeast(recipe[u'yeast'], yeast_loader, dir_suffix=yeast_dir_suffix) recipe_kwargs = { u'grain_additions': grain_additions, u'hop_additions': hop_additions, u'yeast': yeast, u'start_volume': recipe[u'start_volume'], u'final_volume': recipe[u'final_volume'], } if u'data' in recipe: if u'percent_brew_house_yield' in recipe[u'data']: recipe_kwargs[u'percent_brew_house_yield'] = \ recipe[u'data'][u'percent_brew_house_yield'] if u'units' in recipe[u'data']: recipe_kwargs[u'units'] = recipe[u'data'][u'units'] beer = Recipe(recipe[u'name'], **recipe_kwargs) return beer
def test_ne_final_volume(self): recipe1 = Recipe(u"pale ale", final_volume=6.0) recipe2 = Recipe(u"pale ale", final_volume=5.5) self.assertTrue(recipe1 != recipe2)
def test_ne_units(self): recipe1 = Recipe(u"pale ale", units=IMPERIAL_UNITS) recipe2 = Recipe(u"pale ale", units=SI_UNITS) self.assertTrue(recipe1 != recipe2)
def test_ne_start_volume(self): recipe1 = Recipe(u"pale ale", start_volume=7.0) recipe2 = Recipe(u"pale ale", start_volume=6.5) self.assertTrue(recipe1 != recipe2)
def test_ne_brew_house_yield(self): recipe1 = Recipe(u"pale ale", brew_house_yield=0.70) recipe2 = Recipe(u"pale ale", brew_house_yield=0.65) self.assertTrue(recipe1 != recipe2)
def test_eq(self): recipe1 = Recipe(u"pale ale") recipe2 = Recipe(u"pale ale") self.assertEquals(recipe1, recipe2)
def test_ne_grain_additions(self): recipe1 = Recipe(u"pale ale", grain_additions=grain_additions) recipe2 = Recipe(u"pale ale", grain_additions=[grain_additions[0]]) self.assertTrue(recipe1 != recipe2)
def test_ne_name(self): recipe1 = Recipe(u"pale ale") recipe2 = Recipe(u"ipa") self.assertTrue(recipe1 != recipe2)
def test_ne_yeast(self): recipe1 = Recipe(u"pale ale", yeast=yeast) recipe2 = Recipe(u"pale ale", yeast=None) self.assertTrue(recipe1 != recipe2)