示例#1
0
 def test_all_edge_kinds(self) -> None:
     assert set_border_around_table(
         Table([
             [Cell(100), Cell(101), Cell(102)],
             [Cell(110), Cell(111), Cell(112)],
             [Cell(120), Cell(121), Cell(122)],
         ]),
         BorderType.none,
     ) == Table([
         [
             Cell(100,
                  border_left=BorderType.none,
                  border_top=BorderType.none),
             Cell(101, border_top=BorderType.none),
             Cell(102,
                  border_top=BorderType.none,
                  border_right=BorderType.none),
         ],
         [
             Cell(110, border_left=BorderType.none),
             Cell(111),
             Cell(112, border_right=BorderType.none),
         ],
         [
             Cell(120,
                  border_left=BorderType.none,
                  border_bottom=BorderType.none),
             Cell(121, border_bottom=BorderType.none),
             Cell(122,
                  border_bottom=BorderType.none,
                  border_right=BorderType.none),
         ],
     ])
示例#2
0
 def test_validation_extended_cell_in_wrong_place(self) -> None:
     # Top left corner
     with pytest.raises(CellExpectedError):
         Table([[ExtendedCell(Cell(123), 0, 0)]])
     # Elsewhere
     description: List[List[Union[Cell[int], ExtendedCell[int]]]] = [[
         Cell(123), ExtendedCell(Cell(123), 0, 1)
     ]]
     with pytest.raises(CellExpectedError):
         Table(description)
示例#3
0
    def test_validation_extended_cell_bad_coordinate(self) -> None:
        cell = Cell(123, 1, 2)
        description: List[List[Union[Cell[int], ExtendedCell[int]]]]

        description = [[cell, ExtendedCell(cell, 1, 1)]]
        with pytest.raises(ExtendedCellCoordinateError):
            Table(description)

        description = [[cell, ExtendedCell(cell, 0, 0)]]
        with pytest.raises(ExtendedCellCoordinateError):
            Table(description)
示例#4
0
    def test_expand_single_cell(self) -> None:
        t1 = Table.from_dict({(0, 0): Cell(123)})
        assert right_pad_table(t1, 2) == Table.from_dict({
            (0, 0):
            Cell(123, columns=2)
        })

        t2 = Table.from_dict({(0, 0): Cell(123, columns=2)})
        assert right_pad_table(t2, 4) == Table.from_dict({
            (0, 0):
            Cell(123, columns=4)
        })
示例#5
0
    def test_already_wide_enough(self) -> None:
        # Two cells
        t1 = Table([[Cell(123), Cell(123)]])
        assert right_pad_table(t1, 2) == t1

        # A wide cell
        t2 = Table.from_dict({(0, 0): Cell(123, columns=2)})
        assert right_pad_table(t2, 2) == t2

        # Wider than needed
        t3 = Table.from_dict({(0, 0): Cell(123, columns=3)})
        assert right_pad_table(t3, 2) == t3
示例#6
0
 def test_single_cell(self) -> None:
     assert set_border_around_table(
         Table.from_dict({(0, 0): Cell(123)}),
         BorderType.none,
     ) == Table.from_dict({
         (0, 0):
         Cell(
             123,
             border_left=BorderType.none,
             border_right=BorderType.none,
             border_top=BorderType.none,
             border_bottom=BorderType.none,
         ),
     })
示例#7
0
 def test_expand_multiple_cells_including_rows_with_only_extended_cells(
     self, ) -> None:
     t1 = Table.from_dict({
         (0, 0): Cell(123, columns=2, rows=2),
         (2, 0): Cell(123),
         (2, 1): Cell(123),
     })
     assert right_pad_table(t1, 4) == Table.from_dict({
         (0, 0):
         Cell(123, columns=4, rows=2),
         (2, 0):
         Cell(123),
         (2, 1):
         Cell(123, columns=3),
     })
示例#8
0
 def test_validation_extended_cell_bad_reference(self) -> None:
     description: List[List[Union[Cell[int], ExtendedCell[int]]]] = [[
         Cell(123, columns=2),
         ExtendedCell(Cell(123), 0, 1)
     ]]
     with pytest.raises(ExtendedCellReferenceError):
         Table(description)
示例#9
0
    def test_mismatched_shapes(self) -> None:
        with pytest.raises(MissingCellError):
            combine_tables(
                [
                    Table.from_dict({(0, 0): Cell(123, columns=2)}),
                    Table.from_dict({(0, 0): Cell(123)}),
                ],
                axis=0,
            )

        with pytest.raises(MissingCellError):
            combine_tables(
                [
                    Table.from_dict({(0, 0): Cell(123, rows=2)}),
                    Table.from_dict({(0, 0): Cell(123)}),
                ],
                axis=1,
            )
示例#10
0
 def test_to_dict(self) -> None:
     c10 = Cell("1,0")
     c11 = Cell("1,1")
     c0x = Cell("0,x", 1, 2)
     d = {
         (0, 0): c0x,
         (1, 0): c10,
         (1, 1): c11,
     }
     assert Table.from_dict(d).to_dict() == d
示例#11
0
def test_multiple_output_sub_recipe() -> None:
    ingredient_0 = Ingredient(SVS("spam"))
    ingredient_1 = Ingredient(SVS("eggs"))
    ingredient_2 = Ingredient(SVS("more spam"))
    step = Step(SVS("fry"), (ingredient_0, ingredient_1, ingredient_2))
    sub_recipe = SubRecipe(step, (SVS("output 0"), SVS("output 1")))

    assert recipe_tree_to_table(sub_recipe) == combine_tables(
        cast(
            List[Table[RecipeTreeNode]],
            [
                set_border_around_table(
                    Table.from_dict(
                        cast(
                            Mapping[Tuple[int, int], Cell[RecipeTreeNode]],
                            {
                                (0, 0): Cell(ingredient_0),
                                (1, 0): Cell(ingredient_1),
                                (2, 0): Cell(ingredient_2),
                                (0, 1): Cell(step, rows=3),
                            },
                        )),
                    BorderType.sub_recipe,
                ),
                Table.from_dict({
                    (0, 0):
                    Cell(
                        sub_recipe,
                        rows=3,
                        border_top=BorderType.none,
                        border_right=BorderType.none,
                        border_bottom=BorderType.none,
                    )
                }),
            ],
        ),
        axis=1,
    )
示例#12
0
    def test_indexing(self) -> None:
        # Minimal case
        c00 = Cell("0,0")
        t = Table([[c00]])
        assert t[0, 0] == c00
        assert t.rows == 1
        assert t.columns == 1

        # Plain cells only
        c01 = Cell("0,1")
        c10 = Cell("1,0")
        c11 = Cell("1,1")
        t = Table([[c00, c01], [c10, c11]])
        assert t[0, 0] == c00
        assert t[0, 1] == c01
        assert t[1, 0] == c10
        assert t[1, 1] == c11
        assert t.rows == 2
        assert t.columns == 2

        # With extended cells
        c0x = Cell("1,x", 1, 2)
        t = Table([[c0x, ExtendedCell(c0x, 0, 1)], [c10, c11]])
        assert t[0, 0] == c0x
        assert t[0, 1] == ExtendedCell(c0x, 0, 1)
        assert t[1, 0] == c10
        assert t[1, 1] == c11
        assert t.rows == 2
        assert t.columns == 2

        # With asymmetry
        c0x = Cell("1,x", 1, 2)
        t = Table([[c0x, ExtendedCell(c0x, 0, 1)]])
        assert t[0, 0] == c0x
        assert t[0, 1] == ExtendedCell(c0x, 0, 1)
        assert t.rows == 1
        assert t.columns == 2
示例#13
0
def test_single_output_sub_recipe_hidden() -> None:
    ingredient = Ingredient(SVS("spam"))
    step = Step(SVS("fry"), (ingredient, ))
    sub_recipe = SubRecipe(step, (SVS("out"), ), show_output_names=False)

    assert recipe_tree_to_table(sub_recipe) == set_border_around_table(
        Table.from_dict(
            cast(
                Mapping[Tuple[int, int], Cell[RecipeTreeNode]],
                {
                    (0, 0): Cell(ingredient),
                    (0, 1): Cell(step)
                },
            )),
        BorderType.sub_recipe,
    )
示例#14
0
    def test_from_dict(self) -> None:
        # Minimal case
        c00 = Cell("0,0")
        assert Table.from_dict({(0, 0): c00}) == Table([[c00]])

        # Plain cells only
        c01 = Cell("0,1")
        c10 = Cell("1,0")
        c11 = Cell("1,1")
        assert Table.from_dict({
            (0, 0): c00,
            (0, 1): c01,
            (1, 0): c10,
            (1, 1): c11
        }) == Table([[c00, c01], [c10, c11]])

        # With Simple extended cell
        cxx = Cell("0,x", 2, 3)
        description: List[List[Union[Cell[str], ExtendedCell[str]]]] = [
            [cxx, ExtendedCell(cxx, 0, 1),
             ExtendedCell(cxx, 0, 2)],
            [
                ExtendedCell(cxx, 1, 0),
                ExtendedCell(cxx, 1, 1),
                ExtendedCell(cxx, 1, 2),
            ],
        ]
        assert Table.from_dict({(0, 0): cxx}) == Table(description)

        # With normal and extended cells
        c0x = Cell("0,x", 1, 2)
        description = [[c0x, ExtendedCell(c0x, 0, 1)], [c10, c11]]
        assert Table.from_dict({
            (0, 0): c0x,
            (1, 0): c10,
            (1, 1): c11
        }) == Table(description)
示例#15
0
def test_step() -> None:
    input_0 = Ingredient(SVS("input 0"))
    input_1 = Ingredient(SVS("input 1"))

    input_2_ingredient = Ingredient(SVS("input 2"))
    input_2 = Step(SVS("chopped"), (input_2_ingredient, ))
    step = Step(SVS("combine"), (input_0, input_1, input_2))

    assert recipe_tree_to_table(step) == set_border_around_table(
        Table.from_dict(
            cast(
                Mapping[Tuple[int, int], Cell[RecipeTreeNode]],
                {
                    (0, 0): Cell(input_0, columns=2),
                    (1, 0): Cell(input_1, columns=2),
                    (2, 0): Cell(input_2_ingredient),
                    (2, 1): Cell(input_2),
                    (0, 2): Cell(step, rows=3),
                },
            )),
        BorderType.sub_recipe,
    )
示例#16
0
def test_render_table(id: Optional[str], exp_attrs: str) -> None:
    assert render_table(
        Table.from_dict({
            (0, 0):
            Cell(Ingredient(SVS("spam"))),
            (1, 0):
            Cell(Ingredient(SVS("eggs"))),
            (0, 1):
            Cell(
                Step(SVS("fry"),
                     (Ingredient(SVS("spam")), Ingredient(SVS("eggs")))),
                rows=2,
            ),
        }),
        id=id,
    ) == (f'<table class="rg-table"{exp_attrs}>\n'
          "  <tr>\n"
          '    <td class="rg-ingredient">spam</td>\n'
          '    <td class="rg-step" rowspan="2">fry</td>\n'
          "  </tr>\n"
          '  <tr><td class="rg-ingredient">eggs</td></tr>\n'
          "</table>")
示例#17
0
    def test_multiple_tables(self) -> None:
        t1 = Table.from_dict({(0, 0): Cell(123, columns=2)})
        t2 = Table.from_dict({(0, 0): Cell(123), (0, 1): Cell(123)})
        assert combine_tables([t1, t2], axis=0) == Table.from_dict({
            (0, 0):
            Cell(123, columns=2),
            (1, 0):
            Cell(123),
            (1, 1):
            Cell(123)
        })

        t3 = Table.from_dict({(0, 0): Cell(123, rows=2)})
        t4 = Table.from_dict({(0, 0): Cell(123), (1, 0): Cell(123)})
        assert combine_tables([t3, t4], axis=1) == Table.from_dict({
            (0, 0):
            Cell(123, rows=2),
            (0, 1):
            Cell(123),
            (1, 1):
            Cell(123)
        })
示例#18
0
def test_ingredient() -> None:
    ingredient = Ingredient(SVS("spam"))
    assert recipe_tree_to_table(ingredient) == set_border_around_table(
        Table.from_dict({(0, 0): Cell(ingredient)}), BorderType.sub_recipe)
示例#19
0
def recipe_tree_to_table(
    recipe_tree: RecipeTreeNode, _root: bool = True
) -> Table[RecipeTreeNode]:
    """
    Convert a recipe tree into tabular form.

    To display the resulting :py:class:`~Table`, cell contents should be
    rendered as indicated below.

    Firstly, the obvious cases:

    * :py:class:`~Ingredient` is shown as the ingredient quantity and name etc.
    * :py:class:`~Reference` is shown as its label (and quantity/proportion
      etc.)
    * :py:class:`~Step` is shown as its description. The cells immediately to
      the left in the generated table will contain the inputs to this step.

    Finally, cells containing a :py:class:`~SubRecipe` may appear in the table
    for one of two purposes:

    * For single-output sub recipes, the cell containing the
      :py:class:`SubRecipe` will be located above the cells containing the body
      of the sub recipe. This cell should be rendered in the style of a heading
      and containing the output name as the text.
    * For multiple-output sub recipes, thec cell containing the
      :py:class:`SubRecipe` will be immediately to the right of the cells
      defining the sub-recipe and should be rendered as a list all of the
      output names for the sub recipe, for example as a bulleted list.

      .. note::

          This cell will have all but its left border style set to be
          :py:attr:`BorderType.none` to give the appearance of the output list
          being located out to the right of the rest of the table.

    Unless otherwise stated, all cells will have all borders set to
    :py:attr:`BorderType.normal` with the exception of those borders of cells
    near the edges of a sub recipe block. These will have a style of
    :py:attr:`BorderType.sub_recipe`.

    .. note::

        The ``_root`` argument is for internal use and must be left unspecified
        when called by external users. Internally, it indicates if the node
        passed to this function is the root node of its recipe tree.
    """
    if isinstance(recipe_tree, (Ingredient, Reference)):
        table: Table[RecipeTreeNode] = Table([[Cell(recipe_tree)]])
        if _root:
            return set_border_around_table(table, BorderType.sub_recipe)
        else:
            return table

    elif isinstance(recipe_tree, Step):
        input_tables = [
            recipe_tree_to_table(input_tree, False) for input_tree in recipe_tree.inputs
        ]

        # Pad all input tables to same width and stack them up
        input_columns = max(table.columns for table in input_tables)
        input_tables = [right_pad_table(table, input_columns) for table in input_tables]
        combined_input_tables = combine_tables(input_tables, axis=0)

        # Add step to RHS of inputs
        table = combine_tables(
            [
                combined_input_tables,
                Table.from_dict(
                    {(0, 0): Cell(recipe_tree, rows=combined_input_tables.rows)}
                ),
            ],
            axis=1,
        )

        if _root:
            return set_border_around_table(table, BorderType.sub_recipe)
        else:
            return table
    elif isinstance(recipe_tree, SubRecipe):
        sub_tree_table = recipe_tree_to_table(recipe_tree.sub_tree, False)

        if len(recipe_tree.output_names) == 1:
            if recipe_tree.show_output_names:
                return set_border_around_table(
                    combine_tables(
                        [
                            Table.from_dict(
                                {
                                    (0, 0): Cell(
                                        cast(RecipeTreeNode, recipe_tree),
                                        columns=sub_tree_table.columns,
                                    ),
                                }
                            ),
                            sub_tree_table,
                        ],
                        axis=0,
                    ),
                    BorderType.sub_recipe,
                )
            else:
                return set_border_around_table(sub_tree_table, BorderType.sub_recipe)
        else:
            return combine_tables(
                [
                    set_border_around_table(sub_tree_table, BorderType.sub_recipe),
                    Table.from_dict(
                        {
                            (0, 0): Cell(
                                cast(RecipeTreeNode, recipe_tree),
                                rows=sub_tree_table.rows,
                                border_top=BorderType.none,
                                border_right=BorderType.none,
                                border_bottom=BorderType.none,
                            ),
                        }
                    ),
                ],
                axis=1,
            )
    else:
        raise NotImplementedError(type(recipe_tree))
示例#20
0
 def test_validation_ragged_rows(self) -> None:
     with pytest.raises(MissingCellError):
         Table([[Cell(123)], [Cell(123), Cell(123)]])
     with pytest.raises(MissingCellError):
         Table([[Cell(123), Cell(123)], [Cell(123)]])
示例#21
0
 def test_validation_extended_cell_is_missing(self, rows: int,
                                              columns: int) -> None:
     with pytest.raises(MissingCellError):
         Table([[Cell(123, rows, columns)]])
示例#22
0
 def test_validation_extended_cell_is_cell(self, rows: int,
                                           columns: int) -> None:
     with pytest.raises(ExtendedCellExpectedError):
         Table([[Cell(123, rows, columns),
                 Cell(123)], [Cell(123), Cell(123)]])
示例#23
0
 def test_from_dict_validation_empty(self) -> None:
     with pytest.raises(EmptyTableError):
         Table.from_dict({})
示例#24
0
 def test_validation_empty_table(self) -> None:
     with pytest.raises(EmptyTableError):
         Table([])
     with pytest.raises(EmptyTableError):
         Table([[]])
示例#25
0
 def test_from_dict_validation_missing_cells(self, row: int,
                                             column: int) -> None:
     with pytest.raises(MissingCellError):
         Table.from_dict({(0, 0): Cell(123), (row, column): Cell(123)})
示例#26
0
 def test_single_table(self) -> None:
     orig = Table.from_dict({(0, 0): Cell(123, columns=2)})
     assert combine_tables([orig], axis=0) == orig
     assert combine_tables([orig], axis=1) == orig
示例#27
0
def test_reference() -> None:
    sub_recipe = SubRecipe(Ingredient(SVS("spam")), (SVS("out"), ))
    reference = Reference(sub_recipe, 0)
    assert recipe_tree_to_table(reference) == set_border_around_table(
        Table.from_dict({(0, 0): Cell(reference)}), BorderType.sub_recipe)