def test_empty(self, tmp_path: Path) -> None:
     empty_dir = tmp_path / "empty_dir"
     empty_dir.mkdir()
     assert enumerate_recipe_directory(empty_dir) == RecipeDirectoryListing(
         title="Empty dir",
         description_html=None,
         description_source=None,
         subdirectories=[],
         recipes=[],
     )
    def test_recipes(self, tmp_path: Path) -> None:
        path = tmp_path / "test_dir"
        path.mkdir()

        (path / "foo.md").open("w").write("# Foo for 2")
        (path / "bar.md").open("w").write("# Bar for 3")

        d = enumerate_recipe_directory(path)

        assert len(d.recipes) == 2

        assert set(d.recipes) == {path / "foo.md", path / "bar.md"}
    def test_readme(self, tmp_path: Path, readme_filename: str) -> None:
        path = tmp_path / "test_dir"
        path.mkdir()

        readme = path / readme_filename
        readme.open("w").write("# A Directory\nTa-da!")

        assert enumerate_recipe_directory(path) == RecipeDirectoryListing(
            title="A Directory",
            description_html="<p>Ta-da!</p>\n",
            description_source=readme,
            subdirectories=[],
            recipes=[],
        )
Exemple #4
0
    def from_root_directory(
        cls,
        root_directory: Path,
        max_servings: int = 10,
    ) -> "HomePage":
        """
        Create a complete hierarchy of pages from a root directory.

        Parameters
        ==========
        root_directory: Path
            The root directory of the recipe website sources.
        max_servings: int
            The maximum number of servings to scale recipes to. Must be at
            least as high as the largest number of servings a recipe is scaled
            for.
        """
        root = enumerate_recipe_directory(root_directory)

        homepage = cls(
            title=root.title,
            parent=None,
            source_root_directory=root_directory,
            welcome_message_html=root.description_html,
            welcome_message_source=root.description_source,
        )

        recipe_pages: MutableMapping[
            Path, MutableMapping[Optional[int], RecipePage]
        ] = {}
        homepage.scaled_categories = {
            servings: CategoryPage.from_directory(
                servings=servings,
                directory_path=root_directory,
                parent=homepage,
                recipe_pages=recipe_pages,
            )
            for servings in range(1, max_servings + 1)
        }
        homepage.unscaled_categories = CategoryPage.from_directory(
            servings=None,
            directory_path=root_directory,
            parent=homepage,
            recipe_pages=recipe_pages,
        )

        return homepage
Exemple #5
0
    def from_directory(
        cls,
        servings: Optional[int],
        directory_path: Path,
        parent: Union[HomePage, "CategoryPage"],
        recipe_pages: MutableMapping[Path, MutableMapping[Optional[int], "RecipePage"]],
    ) -> "CategoryPage":
        """
        Create a category listing page.

        Parameters
        ==========
        servings : int or None
            The number of servings selected. If None, select the native number
            of servings for recipes.
        directory_path : Path
            The directory containing the recipes/subcategories at this level.
        parent: HomePage or CategoryPage
            The parent page in the hierarchy.
        recipe_pages: {recipe_source_path: {servings: MarkdownRecipe, ...}, ...}
            A dictionary which maps from recipe markdown source filenames and
            serving counts (where 'None' is for unscaled recipes with no
            serving count defined) to :py:class:`RecipePage` objects.

            When scaled category pages are generated (i.e. where servings is
            not None), this dictionary will be populated with RecipePage
            objects for the recipes within that category and the given scale.

            When unscaled category pages are generated (i.e. when servings is
            None), this dictionary is used to find the natively scaled
            RecipePage to refer to.

            As a consequence of the above, scaled category pages must be
            created before unscaled ones.
        """
        directory = enumerate_recipe_directory(directory_path)

        root_category = isinstance(parent, HomePage)

        category_page = cls(
            title=(
                directory.title
                if not root_category
                else f"Recipes for {servings}"
                if servings is not None
                else "Categories"
            ),
            parent=parent,
            servings=servings,
            description_html=(
                directory.description_html if not root_category else None
            ),
            description_source=directory.description_source
            if not root_category
            else None,
            source_directory=directory_path,
        )

        category_page.subcategories = sorted(
            (
                CategoryPage.from_directory(
                    servings=servings,
                    directory_path=subdirectory,
                    parent=category_page,
                    recipe_pages=recipe_pages,
                )
                for subdirectory in directory.subdirectories
            ),
            key=lambda p: p.title,
        )

        if servings is not None:
            for recipe_source in directory.recipes:
                scaled_recipe_pages = recipe_pages.setdefault(recipe_source, {})
                recipe_page = RecipePage.from_recipe_source(
                    servings=servings,
                    recipe_source=recipe_source,
                    parent=category_page,
                    other_scalings=scaled_recipe_pages,
                )
                category_page.recipes.append(recipe_page)
        else:
            for recipe_source in directory.recipes:
                this_recipe_pages = recipe_pages[recipe_source]
                if 1 in this_recipe_pages:  # Scalable recipe
                    recipe_page = this_recipe_pages[1]
                else:  # Unscalable recipe
                    recipe_page = this_recipe_pages[None]
                native_servings = recipe_page.native_servings
                try:
                    recipe_page = recipe_pages[recipe_source][native_servings]
                except KeyError:
                    raise MaxServingsLowerThanLargestRecipeError(
                        f"The maximum number of servings must be at least {native_servings}"
                    )
                category_page.recipes.append(recipe_page)

                # Bodge: For unscaled recipes, the native number of servings
                # will be None. Over-write the parent with this (unscaled)
                # categories page.
                if native_servings is None:
                    recipe_page.parent = category_page

        category_page.recipes.sort(key=lambda recipe_page: recipe_page.title)

        return category_page
 def test_multiple_index_files(self, tmp_path: Path) -> None:
     (tmp_path / "index.md").open("w").write("# Hello")
     (tmp_path / "readme.md").open("w").write("# Hello")
     with pytest.raises(MultipleReadmeError):
         enumerate_recipe_directory(tmp_path)
 def test_not_a_directory(self, tmp_path: Path) -> None:
     path = tmp_path / "not_exists"
     path.open("w").write("")
     with pytest.raises(NotADirectoryError):
         enumerate_recipe_directory(path)
 def test_not_exists(self, tmp_path: Path) -> None:
     path = tmp_path / "not_exists"
     with pytest.raises(NotADirectoryError):
         enumerate_recipe_directory(path)