<commit_before>"""
DrinkRecipe class encapsulates how a drink recipe is calculated and formulated,
can provide itself as a dict/json, tuple of values, do conversions, etc.
Just generally make it better OOP
"""
import re
from fractions import Fraction
from recordtype import recordtype
import itertools
import string

from . import util

# water volume added by preperation method for ABV estimate
WATER_BY_PREP = {'shake': 1.6, 'stir': 1.3, 'build': 1.0, 'pour': 1.0}
WATER_BY_ICE = {'cubed': 1.1, 'crushed': 1.4, 'neat': 1.0}

class RecipeError(Exception):
    pass

class DrinkRecipe(object):
    """ Initialize a drink with a handle to the available stock data and its recipe json
    """
    RecipeExample = recordtype('RecipeExample', [('kinds', []), ('cost', 0), ('abv', 0), ('std_drinks', 0), ('volume', 0)])
    RecipeStats = recordtype('RecipeStats', 'min_cost,max_cost,min_abv,max_abv,min_std_drinks,max_std_drinks,avg_abv,avg_cost,avg_std_drinks,volume', default=RecipeExample)

    @util.default_initializer
    def __init__(self, name, recipe_dict, stock_df=None):
        # from recipe dict pull out other info and set defaults
        self.info      =  recipe_dict.get('info', '')
        self.style     =  recipe_dict.get('style', '')
        self.tag       =  recipe_dict.get('tag', '')
        self.iba_info  =  recipe_dict.get('IBA_description', '')
        self.origin    =  recipe_dict.get('origin', '')
        self.unit      =  recipe_dict.get('unit', 'oz') # cL, mL, tsp, dash, drop
        self.prep      =  recipe_dict.get('prep', 'shake') # build, stir, blend
        self.ice       =  recipe_dict.get('ice', 'cubed') # crushed, neat
        self.glass     =  recipe_dict.get('glass', 'cocktail') # rocks, martini, flute, collins, highball
        self.variants  =  recipe_dict.get('variants',  [])
        self.max_cost     =  0
        self.examples     =  []
        self.ingredients  =  []
        self.stats = None
        for type_str, quantity in recipe_dict.get('ingredients', {}).items():
            self.ingredients.append(QuantizedIngredient(type_str, quantity, self.unit))
        for type_str, quantity in recipe_dict.get('optional', {}).items():
            self.ingredients.append(OptionalIngredient(type_str, quantity, self.unit))
        if recipe_dict.get('misc'):
            self.ingredients.append(Ingredient(recipe_dict.get('misc')))
        if recipe_dict.get('garnish'):
            self.ingredients.append(Garnish(recipe_dict.get('garnish')))

        self.show_examples = False

    def __str__(self):
        """ Drink recipe basic plain text output format
        """
        lines = []
        lines.append(self.name)
        lines.extend([i.str() for i in self.ingredients])
        if self.variants:
            lines.append("\tVariants:")
            lines.extend(['\t'+v for v in self.variants])
        if self.show_examples:
            lines.append("\tExamples:")
            lines.extend(['\t'+v for v in self.examples])
        lines.append('')
        return '\n'.join(lines)

    def __repr__(self):
        return "{}:{}".format(self.__class__.__name__, self.name)

    def prep_line(self, extended=True, caps=True):
        case_fn = str.upper if caps else str.lower
        layout = "{{}} glass | {{}}{} | {{}}".format("" if self.ice == 'neat' else " ice") if extended else "{} | {} | {}"
        return case_fn(layout.format(self.glass, self.ice, self.prep))

    @property
    def can_make(self):
        return bool(self.examples)

    def convert(self, to_unit, rounded=True, convert_nonstandard=False):
        """ Convert the main unit of this recipe
        """
        if self.unit == to_unit:
            return
        for ingredient in self.ingredients:
            ingredient.recipe_unit = to_unit
            if ingredient.unit in ['ds', 'drop'] and not convert_nonstandard:
                continue
            try:
                ingredient.convert(to_unit, rounded=rounded)
            except NotImplementedError:
                pass
        self.unit = to_unit

    def generate_examples(self, barstock, stats=False):
        """ Given a Barstock, calculate examples drinks from the data
        e.g. For every dry gin and vermouth in Barstock, generate every Martini
        that can be made, along with the cost,abv,std_drinks from the ingredients
        """
        ingredients = self._get_quantized_ingredients()
        example_kinds = barstock.get_all_kind_combinations((i.specifier for i in ingredients))
        del self.examples # need to make possible to run again
        self.examples = []
        for kinds in example_kinds:
            # TODO refactor to generate IngredientSpecifiers for the kind lists
            example = DrinkRecipe.RecipeExample(); example.kinds = []
            for kind, ingredient in zip(kinds, ingredients):
                if ingredient.unit == 'literal':
                    continue
                example.cost       += ingredient.get_cost(kind, barstock)
                example.std_drinks += ingredient.get_std_drinks(kind, barstock)
                example.volume     += ingredient.get_amount_as(self.unit, rounded=False, single_value=True)
                # remove juice and such from the kinds listed
                if barstock.get_kind_category(util.IngredientSpecifier(ingredient.specifier.ingredient, kind)) in ['Vermouth', 'Liqueur', 'Bitters', 'Spirit', 'Wine']:
                    example.kinds.append(kind)
            example.kinds = ', '.join(example.kinds);
            example.volume *= WATER_BY_PREP.get(self.prep, 1.0)
            example.volume *= WATER_BY_ICE.get(self.ice, 1.0)
            example.abv = util.calculate_abv(example.std_drinks, example.volume, self.unit)
            self.max_cost = max(self.max_cost, example.cost)
            self.examples.append(example)
        if stats and self.examples:
            self.calculate_stats()
            # attempting to use an average here instead of max_cost
            self.max_cost = self.stats.avg_cost
        return self # so it can be used when chained
<commit_msg>Apply a limit on the number of examples displayed<commit_after>"""
DrinkRecipe class encapsulates how a drink recipe is calculated and formulated,
can provide itself as a dict/json, tuple of values, do conversions, etc.
Just generally make it better OOP
"""
import re
from fractions import Fraction
from recordtype import recordtype
import itertools
import string

from . import util

# water volume added by preperation method for ABV estimate
WATER_BY_PREP = {'shake': 1.6, 'stir': 1.3, 'build': 1.0, 'pour': 1.0}
WATER_BY_ICE = {'cubed': 1.1, 'crushed': 1.4, 'neat': 1.0}

EXAMPLE_LIMIT = 3

class RecipeError(Exception):
    pass

class DrinkRecipe(object):
    """ Initialize a drink with a handle to the available stock data and its recipe json
    """
    RecipeExample = recordtype('RecipeExample', [('kinds', []), ('cost', 0), ('abv', 0), ('std_drinks', 0), ('volume', 0)])
    RecipeStats = recordtype('RecipeStats', 'min_cost,max_cost,min_abv,max_abv,min_std_drinks,max_std_drinks,avg_abv,avg_cost,avg_std_drinks,volume', default=RecipeExample)

    @util.default_initializer
    def __init__(self, name, recipe_dict, stock_df=None):
        # from recipe dict pull out other info and set defaults
        self.info      =  recipe_dict.get('info', '')
        self.style     =  recipe_dict.get('style', '')
        self.tag       =  recipe_dict.get('tag', '')
        self.iba_info  =  recipe_dict.get('IBA_description', '')
        self.origin    =  recipe_dict.get('origin', '')
        self.unit      =  recipe_dict.get('unit', 'oz') # cL, mL, tsp, dash, drop
        self.prep      =  recipe_dict.get('prep', 'shake') # build, stir, blend
        self.ice       =  recipe_dict.get('ice', 'cubed') # crushed, neat
        self.glass     =  recipe_dict.get('glass', 'cocktail') # rocks, martini, flute, collins, highball
        self.variants  =  recipe_dict.get('variants',  [])
        self.max_cost     =  0
        self.examples     =  []
        self.ingredients  =  []
        self.stats = None
        for type_str, quantity in recipe_dict.get('ingredients', {}).items():
            self.ingredients.append(QuantizedIngredient(type_str, quantity, self.unit))
        for type_str, quantity in recipe_dict.get('optional', {}).items():
            self.ingredients.append(OptionalIngredient(type_str, quantity, self.unit))
        if recipe_dict.get('misc'):
            self.ingredients.append(Ingredient(recipe_dict.get('misc')))
        if recipe_dict.get('garnish'):
            self.ingredients.append(Garnish(recipe_dict.get('garnish')))

        self.show_examples = False

    def __str__(self):
        """ Drink recipe basic plain text output format
        """
        lines = []
        lines.append(self.name)
        lines.extend([i.str() for i in self.ingredients])
        if self.variants:
            lines.append("\tVariants:")
            lines.extend(['\t'+v for v in self.variants])
        if self.show_examples:
            lines.append("\tExamples:")
            lines.extend(['\t'+v for v in self.examples])
        lines.append('')
        return '\n'.join(lines)

    def __repr__(self):
        return "{}:{}".format(self.__class__.__name__, self.name)

    def prep_line(self, extended=True, caps=True):
        case_fn = str.upper if caps else str.lower
        layout = "{{}} glass | {{}}{} | {{}}".format("" if self.ice == 'neat' else " ice") if extended else "{} | {} | {}"
        return case_fn(layout.format(self.glass, self.ice, self.prep))

    @property
    def can_make(self):
        return bool(self.examples)

    def convert(self, to_unit, rounded=True, convert_nonstandard=False):
        """ Convert the main unit of this recipe
        """
        if self.unit == to_unit:
            return
        for ingredient in self.ingredients:
            ingredient.recipe_unit = to_unit
            if ingredient.unit in ['ds', 'drop'] and not convert_nonstandard:
                continue
            try:
                ingredient.convert(to_unit, rounded=rounded)
            except NotImplementedError:
                pass
        self.unit = to_unit

    def generate_examples(self, barstock, stats=False):
        """ Given a Barstock, calculate examples drinks from the data
        e.g. For every dry gin and vermouth in Barstock, generate every Martini
        that can be made, along with the cost,abv,std_drinks from the ingredients
        """
        ingredients = self._get_quantized_ingredients()
        example_kinds = barstock.get_all_kind_combinations((i.specifier for i in ingredients))
        del self.examples # need to make possible to run again
        self.examples = []
        for kinds in example_kinds:
            # TODO refactor to generate IngredientSpecifiers for the kind lists
            example = DrinkRecipe.RecipeExample(); example.kinds = []
            for kind, ingredient in zip(kinds, ingredients):
                if ingredient.unit == 'literal':
                    continue
                example.cost       += ingredient.get_cost(kind, barstock)
                example.std_drinks += ingredient.get_std_drinks(kind, barstock)
                example.volume     += ingredient.get_amount_as(self.unit, rounded=False, single_value=True)
                # remove juice and such from the kinds listed
                if barstock.get_kind_category(util.IngredientSpecifier(ingredient.specifier.ingredient, kind)) in ['Vermouth', 'Liqueur', 'Bitters', 'Spirit', 'Wine']:
                    example.kinds.append(kind)
            example.kinds = ', '.join(example.kinds);
            example.volume *= WATER_BY_PREP.get(self.prep, 1.0)
            example.volume *= WATER_BY_ICE.get(self.ice, 1.0)
            example.abv = util.calculate_abv(example.std_drinks, example.volume, self.unit)
            self.max_cost = max(self.max_cost, example.cost)
            self.examples.append(example)
        if stats and self.examples:
            self.calculate_stats()
            # attempting to use an average here instead of max_cost
            self.max_cost = self.stats.avg_cost
        # Apply a limit on the number of examples used
        if len(self.examples) > EXAMPLE_LIMIT:
            # TODO grabbing spaced indicies
            self.examples = [self.examples[0],
                    self.examples[(len(self.examples)-1)//2],
                    self.examples[len(self.examples)-1]]
        return self # so it can be used when chained

{'fraction_non_alphanumeric': 0.11703230953330675, 'fraction_numerical': 0.004068607897885919, 'mean_word_length': 2.5452488687782804, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1953238', 'n_tokens_mistral': 3832, 'n_tokens_neox': 3685, 'n_words': 934}