Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #5
0
    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
Beispiel #6
0
    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
Beispiel #7
0
    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
Beispiel #8
0
    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
Beispiel #9
0
    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
Beispiel #10
0
 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
Beispiel #11
0
 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
Beispiel #12
0
 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
Beispiel #13
0
 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
Beispiel #14
0
 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
Beispiel #15
0
 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
Beispiel #16
0
    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