Esempio n. 1
0
def test_get_recipe_with_yield():
    recipe = Recipe(
        title="Test",
        yields=[Amount(factor=Decimal('2'), unit="servings")],
        ingredients=[
            Ingredient(amount=Amount(factor=Decimal('5')), name='Eggs'),
        ],
    )

    result = get_recipe_with_yield(recipe, Amount(factor=Decimal('4'), unit='servings'))
    assert result.yields[0] == Amount(factor=Decimal('4'), unit='servings')
    assert result.ingredients[0].amount == Amount(factor=Decimal('10'))

    # interpreted as "4 recipes", that is multiply by 4
    result_unitless = get_recipe_with_yield(recipe, Amount(factor=Decimal('4')))
    assert result_unitless.yields[0] == Amount(factor=Decimal('8'), unit='servings')
    assert result_unitless.ingredients[0].amount == Amount(factor=Decimal('20'))

    # if recipe has unitless yield, it is preferred to the above interpretation
    recipe_with_unitless_yield = replace(recipe, yields=[Amount(factor=Decimal('4'))])
    result_unitless_from_unitless_yield = get_recipe_with_yield(recipe_with_unitless_yield, Amount(factor=Decimal('4')))
    assert result_unitless_from_unitless_yield.yields[0] == Amount(factor=Decimal('4'))
    assert result_unitless_from_unitless_yield.ingredients[0].amount == Amount(factor=Decimal('5'))

    # try with unit not in recipe yields
    with pytest.raises(StopIteration):
        get_recipe_with_yield(recipe, Amount(factor=Decimal('500'), unit='ml'))
Esempio n. 2
0
def _get_linked_recipe(ingredient: Ingredient,
                       *,
                       base_url: URL,
                       parser: RecipeParser,
                       flatten: bool = True) -> Recipe:
    url = base_url.join(URL(ingredient.link))
    try:
        with urllib.request.urlopen(str(url)) as req:
            encoding = req.info().get_content_charset() or 'UTF-8'
            src = req.read().decode(encoding)
    except Exception as e:
        raise RuntimeError(
            f'''Couldn't find linked recipe for ingredient "{ingredient.name}"'''
        ) from e

    try:
        link_recipe = parser.parse(src)
    except Exception as e:
        raise RuntimeError(
            f'''Couldn't parse linked recipe for ingredient "{ingredient.name}"'''
        ) from e

    if flatten:
        link_recipe = _get_flattened_recipe(link_recipe,
                                            base_url=url,
                                            parser=parser)

    if ingredient.amount:
        try:
            link_recipe = get_recipe_with_yield(link_recipe, ingredient.amount)
        except StopIteration:
            print(_make_missing_yield_warning(link_recipe, ingredient.amount),
                  file=sys.stderr)

    return link_recipe
Esempio n. 3
0
def _process_scaling(r, args):
    if args.required_yield is not None:
        required_yield = RecipeParser.parse_amount(args.required_yield)
        if required_yield is None or required_yield.factor is None:
            print(f'Given yield is not valid', file=sys.stderr)
            exit(1)
        try:
            r = get_recipe_with_yield(r, required_yield)
        except StopIteration:
            print(f'Recipe "{r.title}" does not specify a yield in the unit "{required_yield.unit}". The '
                  f'following units can be used: ' + ", ".join(f'"{y.unit}"' for y in r.yields), file=sys.stderr)
            exit(1)
    elif args.multiply is not None:
        multiply = RecipeParser.parse_amount(args.multiply)
        if multiply is None or multiply.factor is None:
            print(f'Given multiplier is not valid', file=sys.stderr)
            exit(1)
        if multiply.unit is not None:
            print(f'A recipe can only be multiplied with a unitless amount', file=sys.stderr)
            exit(1)
        r = multiply_recipe(r, multiply.factor)
    return r
Esempio n. 4
0
def _create_flattened_substituted_ingredients(ingredients: List[Union[Ingredient, IngredientGroup]],
                                              link_to_recipe: Dict[str, Recipe]) -> List[Ingredient]:
    result_ingredients = []
    result_groups = []
    for ingr in ingredients:
        if isinstance(ingr, IngredientGroup):
            new_group = copy.deepcopy(ingr)
            new_group.children = _create_flattened_substituted_ingredients(new_group.children, link_to_recipe)
            result_groups.append(new_group)
        elif ingr.link in link_to_recipe:
            try:
                link_recipe = get_recipe_with_yield(link_to_recipe[ingr.link], ingr.amount)
            except StopIteration:
                result_ingredients.append(ingr)
            else:
                new_group = IngredientGroup(title=_link_ingredient_title(ingr, link_recipe))
                new_group.children = link_recipe.ingredients
                result_groups.append(new_group)
        else:
            result_ingredients.append(ingr)

    # groups must come after ingredients, see https://github.com/tstehr/RecipeMD/issues/6
    return result_ingredients + result_groups
Esempio n. 5
0
def _process_scaling(r, args):
    """Returns recipes scaled according to --multiply or --yield"""
    if args.required_yield is not None:
        required_yield = RecipeParser.parse_amount(args.required_yield)
        if required_yield is None or required_yield.factor is None:
            print(f'Given yield is not valid', file=sys.stderr)
            exit(1)
        try:
            r = get_recipe_with_yield(r, required_yield)
        except StopIteration:
            print(_make_missing_yield_warning(r, required_yield),
                  file=sys.stderr)
            exit(1)
    elif args.multiply is not None:
        multiply = RecipeParser.parse_amount(args.multiply)
        if multiply is None or multiply.factor is None:
            print(f'Given multiplier is not valid', file=sys.stderr)
            exit(1)
        if multiply.unit is not None:
            print(f'A recipe can only be multiplied with a unitless amount',
                  file=sys.stderr)
            exit(1)
        r = multiply_recipe(r, multiply.factor)
    return r
Esempio n. 6
0
    def download_file(relative_path=''):
        absolute_path = os.path.join(base_folder_path, relative_path)

        if os.path.isdir(absolute_path):
            if not absolute_path.endswith('/'):
                return redirect(f'/{relative_path}/', code=302)

            child_paths = [(ch, os.path.isdir(os.path.join(absolute_path, ch)))
                           for ch in os.listdir(absolute_path)]
            child_paths = [
                (ch, is_dir) for ch, is_dir in child_paths
                if not ch.startswith('.') and (is_dir or ch.endswith('.md'))
            ]
            child_paths = [
                f'{ch}/' if not ch.endswith('/') and is_dir else ch
                for ch, is_dir in child_paths
            ]
            child_paths = sorted(child_paths)
            return render_template("folder.html",
                                   child_paths=child_paths,
                                   path=relative_path)

        if not absolute_path.endswith('.md'):
            return send_from_directory(base_folder_path, relative_path)

        with open(absolute_path, 'r', encoding='UTF-8') as f:
            required_yield_str = request.args.get('yield', '1')
            required_yield = recipe_parser.parse_amount(required_yield_str)
            if required_yield is None:
                required_yield = Amount(factor=Decimal(1))

            src = f.read()

            try:
                recipe = recipe_parser.parse(src)
            except Exception as e:
                return render_template("markdown.html",
                                       markdown=src,
                                       path=relative_path,
                                       errors=[e.args[0]])

            errors = []
            try:
                recipe = get_recipe_with_yield(recipe, required_yield)
            except StopIteration:
                errors.append(
                    f'The recipe does not specify a yield in the unit "{required_yield.unit}". '
                    f'The following units can be used: ' +
                    ", ".join(f'"{y.unit}"' for y in recipe.yields))
            except Exception as e:
                errors.append(str(e))

            return render_template(
                "recipe.html",
                recipe=recipe,
                yields=recipe_serializer._serialize_yields(recipe.yields,
                                                           rounding=2),
                tags=recipe_serializer._serialize_tags(recipe.tags),
                units=list(set(y.unit for y in recipe.yields)),
                default_yield=recipe_serializer._serialize_amount(
                    recipe.yields[0]) if recipe.yields else "1",
                path=relative_path,
                errors=errors)