BjoernLudwigPTB/pyxml2pdf

View on GitHub
src/pyxml2pdf/tables/builder.py

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
"""This module contains the class :class:`TableBuilder` which deals with XML tables."""

import warnings
from typing import List, Optional, Union

from reportlab.platypus import Flowable, Paragraph, Table, TableStyle  # type: ignore

from pyxml2pdf.styles.table_styles import XMLTableStyle
from pyxml2pdf.tables.tables import XMLTable
from ..input.properties import COLUMNS, SUBTABLE_SETTINGS  # type: ignore


class TableBuilder:
    """Takes over all tasks for building and working with the tables created"""

    def __init__(self):
        self._table_style: XMLTableStyle = XMLTableStyle()
        self._stylesheet: TableStyle = self._table_style.custom_styles["stylesheet"]
        self._subtables: List[XMLTable] = self.create_subtables()

    def create_subtables(self) -> List[XMLTable]:
        """Create subtables for all different kinds of rows

        :returns: a list of all subtables
        :rtype: List[XMLTable]
        """
        subtables_list = []
        for subtable in SUBTABLE_SETTINGS:
            subtable_table = XMLTable(subtable.label, subtable.include)
            subtable_table.extend(self.make_header(subtable.label))
            subtables_list.append(subtable_table)
        return subtables_list

    def make_header(self, title: str) -> List[Table]:
        """Build the first two rows of a subtable

        Build the first two rows of a subtable with its title and column headings taken
        from the properties file.

        :param str title: the title of the subtable

        :returns: two line table with title and headings
        :rtype: List[Table]
        """
        # Create first row spanning the full width and title as content.
        title_row = [
            self.create_fixedwidth_table(
                [[Paragraph(title, self._stylesheet["Heading1"])]],
                self._table_style.table_width,
                self._table_style.custom_styles["heading"],
            )
        ]

        # Create row containing one column per heading.
        columns_list = [
            Paragraph(heading, self._stylesheet.get("Heading2"))
            for heading in [column.label for column in COLUMNS]
        ]

        # Concatenate both rows.
        title_row.append(
            self.create_fixedwidth_table(
                [columns_list],
                self._table_style.column_widths,
                self._table_style.custom_styles["sub_heading"],
            )
        )
        return title_row

    @property
    def subtables(self) -> List[Table]:
        """List[Table]: Return all subtables at once"""
        return [element for subtable in self._subtables for element in subtable.rows]

    def distribute_row(self, row):
        """Distribute a row to the subtables according to the related criteria

        :param XMLRow row: row to distribute
        """
        distribution_failed = True
        set_of_crits = set(row.criteria)
        for subtable in self._subtables:
            crit_filters_intersection = [
                set_of_crits.intersection(include_filters)
                for include_filters in subtable.include_filters
            ]
            if all(crit_filters_intersection):
                subtable.append(row.get_table_row(subtable.title))
                distribution_failed = False
        if distribution_failed:
            warnings.warn(
                "XML row identified by "
                + row.identifier
                + " would not be printed, because it does not contain a valid"
                " combination of criteria. Currently, it contains "
                + str(row.criteria)
                + ". If it is supposed to be shown please adapt the tables' "
                "include-filters or adapt the XML tag's content.",
                RuntimeWarning,
            )

    def create_fixedwidth_table(
        self,
        cells: List[List[Flowable]],
        widths: Optional[Union[float, List[float]]] = None,
        style: Optional[XMLTableStyle] = None,
    ) -> Table:
        """Create a table with specified column widths

        Create a table from specified cells with fixed column widths and a specific
        style.

        :param List[List[Flowable]] cells: cells wrapped by a list representing the
            columns wrapped by a list representing the lines
        :param Optional[Union[float, List[float]]] widths: Optional column widths.
            The default results in reasonable settings based on experience.
        :param Optional[XMLTableStyle] style: Optional table's style. The default
            results in reasonable settings based on experience.
        :returns: A table containing specified cells in fixed width, styled columns.
        """
        if widths is None:
            widths = self._table_style.column_widths
        if style is None:
            style = self._table_style.custom_styles["normal"]
        table = Table(cells, colWidths=widths)
        table.setStyle(style)

        return table