def get_hops(self, beerxml: BeerXMLRecipe) -> iter: for beerxml_hop in beerxml.hops: amount = beerxml_hop.amount if amount is not None: amount *= 1000 # convert to grams name = clean_kind(beerxml_hop.name) hop = RecipeHop() hop.kind_raw = name hop.amount = amount hop.use = self.get_hop_use(beerxml_hop) hop.time = beerxml_hop.time hop.type = self.get_hop_type(beerxml_hop.type) hop.form = self.get_hop_form(beerxml_hop.form) hop.alpha = beerxml_hop.alpha hop.beta = beerxml_hop.beta # Extra values hop.set_extra('hsi', beerxml_hop.hsi) hop.set_extra('humulene', beerxml_hop.humulene) hop.set_extra('caryophyllene', beerxml_hop.caryophyllene) hop.set_extra('cohumulone', beerxml_hop.cohumulone) hop.set_extra('myrcene', beerxml_hop.myrcene) hop.set_extra('substitutes', clean_kind(beerxml_hop.substitutes)) yield hop
def get_yeasts(self, bs_recipe: BeerSmithNode) -> iter: for bs_yeast in self.get_ingredients_of_type(bs_recipe, ['yeast']): name = clean_kind(bs_yeast.string_or_none('name')) yeast = RecipeYeast(kind_raw=name) yeast.lab = bs_yeast.string_or_none('lab') yeast.product_id = bs_yeast.string_or_none('product_id') yeast.form = self.get_yeast_form(bs_yeast) yeast.type = self.get_yeast_type(bs_yeast) yeast.min_attenuation = bs_yeast.float_or_none('min_attenuation') yeast.max_attenuation = bs_yeast.float_or_none('max_attenuation') # Extra values notes = bs_yeast.string_or_none('notes') min_temp = bs_yeast.float_or_none('min_temp') max_temp = bs_yeast.float_or_none('max_temp') yeast.set_extra('notes', None if notes is None else notes.strip()) yeast.set_extra( 'min_temperature', None if min_temp is None else fahrenheit_to_celsius(min_temp)) yeast.set_extra( 'max_temperature', None if max_temp is None else fahrenheit_to_celsius(max_temp)) yeast.set_extra('flocculation', self.get_yeast_flocculation(bs_yeast)) yeast.set_extra('best_for', bs_yeast.string_or_none('best_for')) yield yeast
def parse_hops(self, json_data: JsonParser, prefix: str): i = 1 while (kind := json_data.string_or_none("{}_{}_Sorte".format(prefix, i))) is not None: kind = clean_kind(kind) use = RecipeHop.BOIL alpha = json_data.float_or_none("{}_{}_alpha".format(prefix, i)) amount = json_data.float_or_none("{}_{}_Menge".format(prefix, i)) time = json_data.string_or_none("{}_{}_Kochzeit".format(prefix, i)) if time is not None: if time == 'Whirlpool': use = RecipeHop.AROMA time = 0 else: time = json_data.float_or_none("{}_{}_Kochzeit".format( prefix, i)) if time is not None: time = ceil(time) if time < 5: # Assume aroma use when less than 5mins boiled use = RecipeHop.AROMA yield RecipeHop(kind_raw=kind, alpha=alpha, use=use, amount=amount, time=time) i += 1
def get_yeasts(self, beerxml: BeerXMLRecipe) -> iter: for beerxml_yeast in beerxml.yeasts: amount = beerxml_yeast.amount if amount is not None: amount *= 1000 # convert to grams/milliliters name = clean_kind(beerxml_yeast.name) yeast = RecipeYeast(kind_raw=name) # Handle numeric product ids product_id = beerxml_yeast.product_id if isinstance(product_id, int): product_id = str(product_id) if isinstance(product_id, float): if round(product_id) == product_id: product_id = str(round(product_id)) else: product_id = str(product_id).strip() yeast.lab = beerxml_yeast.laboratory yeast.product_id = product_id yeast.form = self.get_yeast_form(beerxml_yeast.form) yeast.type = self.get_yeast_type(beerxml_yeast.type) yeast.amount = amount yeast.amount_is_weight = beerxml_yeast.amount_is_weight yeast.min_attenuation = beerxml_yeast.attenuation yeast.max_attenuation = beerxml_yeast.attenuation yeast.min_temperature = beerxml_yeast.min_temperature yeast.max_temperature = beerxml_yeast.max_temperature yeast.flocculation = self.get_yeast_flocculation( beerxml_yeast.flocculation) yield yeast
def get_fermentables(self, beerxml: BeerXMLRecipe) -> iter: for beerxml_fermentable in beerxml.fermentables: amount = beerxml_fermentable.amount if amount is not None: amount *= 1000 # convert to grams name = clean_kind(beerxml_fermentable.name) fermentable = RecipeFermentable() fermentable.kind_raw = name fermentable.amount = amount fermentable.form = self.get_fermentable_form( beerxml_fermentable.type) fermentable.origin_raw = clean_kind(beerxml_fermentable.origin) fermentable.color_lovibond = beerxml_fermentable.color fermentable._yield = beerxml_fermentable._yield yield fermentable
def parse_recipe(self, recipe: Recipe, bs_recipe: BeerSmithNode) -> None: recipe.name = bs_recipe.string_or_none('name') recipe.author = bs_recipe.string_or_none('brewer') creation_date = bs_recipe.string_or_none('date') if creation_date is None or creation_date == '1969-12-31': creation_date = bs_recipe.string_or_none('last_modified') # Style bs_style = bs_recipe.get_child('style') if bs_style: recipe.style_raw = clean_kind(bs_style.string_or_none('name')) # Recipe recipe.og = bs_recipe.float_or_none('og_measured') recipe.fg = bs_recipe.float_or_none('fg_measured') if recipe.og == 1.046 and recipe.fg == 1.010: # These seem to be default values, so they're meaningless recipe.og = None recipe.fg = None (recipe.mash_water, recipe.sparge_water) = self.get_mash_water(bs_recipe) # Equipment values cast_out_ounces = None bs_equipment = bs_recipe.get_child('equipment') if bs_equipment: if creation_date is None or creation_date == '1969-12-31': creation_date = bs_equipment.string_or_none('last_modified') recipe.boiling_time = bs_equipment.float_or_none('boil_time') recipe.extract_efficiency = bs_equipment.float_or_none( 'efficiency') cast_out_ounces = bs_equipment.float_or_none('batch_vol') if recipe.extract_efficiency is None: recipe.extract_efficiency = bs_recipe.float_or_none( 'old_efficiency') # Boiling if cast_out_ounces is None or cast_out_ounces == 0.0: cast_out_ounces = bs_recipe.float_or_none('volume_measured') if cast_out_ounces is None or cast_out_ounces == 0.0: cast_out_ounces = bs_recipe.float_or_none('final_vol_measured') if cast_out_ounces == 0.0: cast_out_ounces = None recipe.cast_out_wort = fluid_ounces_to_liters( cast_out_ounces) if cast_out_ounces is not None else None # Hopefully we've found a creation date if creation_date is not None and creation_date != '1969-12-31': try: recipe.created = datetime.strptime(creation_date, '%Y-%m-%d') except ValueError: pass
def get_fermentables(self, bs_recipe: BeerSmithNode) -> iter: for bs_fermentable in self.get_ingredients_of_type( bs_recipe, ['grain']): amount = bs_fermentable.float_or_none('amount') if amount is not None: amount = ounces_to_gramms(amount) name = clean_kind(bs_fermentable.string_or_none('name')) fermentable = RecipeFermentable() fermentable.kind_raw = name fermentable.amount = amount fermentable.form = self.get_fermentable_form(bs_fermentable) fermentable.origin_raw = clean_kind( bs_fermentable.string_or_none('origin')) fermentable.color_lovibond = bs_fermentable.float_or_none('color') fermentable._yield = bs_fermentable.float_or_none('yield') fermentable.notes = bs_fermentable.string_or_none('notes') yield fermentable
def get_fermentables(self, json_data: JsonParser) -> iter: i = 1 while (kind := json_data.string_or_none("Malz%d" % i)) is not None: kind = clean_kind(kind) amount = json_data.float_or_none("Malz%d_Menge" % i) if amount is not None: unit = json_data.string_or_none("Malz%d_Einheit" % i) if unit is not None and unit == 'kg': amount *= 1000 yield RecipeFermentable(kind_raw=kind, amount=amount) i += 1
def get_hop_use(self, beerxml_hop: Hop): use_raw = beerxml_hop.use if use_raw is not None: use_raw = clean_kind(use_raw.lower()) if use_raw in self.HOP_USE_MAP: return self.HOP_USE_MAP[use_raw] time = beerxml_hop.time if time is not None: if time <= 5: return RecipeHop.AROMA if time > 24 * 60: return RecipeHop.DRY_HOP return RecipeHop.BOIL
def get_yeast_flocculation(self, value): if value is not None: value = clean_kind(value.lower()) if value in self.YEAST_FLOCCULATION_MAP: return self.YEAST_FLOCCULATION_MAP[value] return None
def get_yeast_type(self, value): if value is not None: value = clean_kind(value.lower()) if value in self.YEAST_TYPE_MAP: return self.YEAST_TYPE_MAP[value] return None
def get_yeast_form(self, value): if value is not None: value = clean_kind(value.lower()) if value in self.YEAST_FORM_MAP: return self.YEAST_FORM_MAP[value] return None
def get_hop_form(self, value): if value is not None: value = clean_kind(value.lower()) if value in self.HOP_FORM_MAP: return self.HOP_FORM_MAP[value] return None
def get_hop_type(self, value): if value is not None: value = clean_kind(value.lower()) if value in self.HOP_TYPE_MAP: return self.HOP_TYPE_MAP[value] return None
def get_fermentable_form(self, value): if value is not None: value = clean_kind(value.lower()) if value in self.FERMENTABLE_FORM_MAP: return self.FERMENTABLE_FORM_MAP[value] return None
def get_hops(self, bs_recipe: BeerSmithNode) -> iter: for bs_hop in self.get_ingredients_of_type(bs_recipe, ['hops']): amount = bs_hop.float_or_none('amount') if amount is not None: amount = ounces_to_gramms(amount) name = clean_kind(bs_hop.string_or_none('name')) hop = RecipeHop() hop.kind_raw = name hop.amount = amount hop.use = self.get_hop_use(bs_hop) hop.type = self.get_hop_type(bs_hop) hop.form = self.get_hop_form(bs_hop) hop.alpha = bs_hop.float_or_none('alpha') hop.beta = bs_hop.float_or_none('beta') # Extra values hop.set_extra('hsi', bs_hop.float_or_none('hsi')) notes = bs_hop.string_or_none('notes') if notes is not None: sub_search = re.search('Subst\\w*:(.+)', notes, flags=re.IGNORECASE) if sub_search: hop.set_extra('substitutes', sub_search.group(1).strip()) aroma_search = re.search('Aroma\\w*:(.+)', notes, flags=re.IGNORECASE) if aroma_search: hop.set_extra('aroma', aroma_search.group(1).strip()) used_for_search = re.search('Used\\s*for\\w*:(.+)', notes, flags=re.IGNORECASE) if used_for_search: hop.set_extra('used_for', used_for_search.group(1).strip()) descr_search = re.search('(.+)\n[\\w\\s]+:', notes, flags=re.IGNORECASE | re.MULTILINE) if descr_search: hop.set_extra('notes', descr_search.group(1).strip()) else: hop.set_extra('notes', notes) # Add an extra attribute to pass the value around hop.ibu_contrib = bs_hop.float_or_none('ibu_contrib') or 0.0 if hop.use != RecipeHop.DRY_HOP: hop.time = bs_hop.float_or_none('boil_time') # Force dry hop use when dry hop time is set dry_hop_time = bs_hop.float_or_none('dry_hop_time') if dry_hop_time is not None and dry_hop_time > 0: hop.use = RecipeHop.DRY_HOP hop.time = dry_hop_time * 24 * 60 # Days to minutes # Force Aroma use when boil time is low if hop.use == RecipeHop.BOIL and hop.time is not None and hop.time <= 5: hop.use = RecipeHop.AROMA yield hop