Skip to content

Section

Sections are the building blocks of report object. Each report is made of sequence of sections, initialized with data object (e.g. Asset, Fleet or Drive), that were provided by Report object, and custom settings if needed.

Section might be reused between the different report types, as long as the attributes available in the report object are compatible with the interface of the section's .create() method.

Settings

The section class has an inner class with settings adopted to the section needs. Every setting has a default value, so even without any adjustment, a section can be included in the report.

However, in many cases the setting customization is needed. It might be applied at different stages of the report preparation.

Section settings are defined as Pydantic models. Thanks to that, input parsing, based on type annotations, is available. This is particularly useful when the settings are provided from outside the Python code, e.g. from the JSON based report variant file. When a section requires a setting that is an integer, it can be provided as a string or float as long as it can be parsed to a valid integer type.

Section defaults in report settings

If you need to redefine the defaults of the section used in the newly created report, you can provide the new defaults in the SECTION_DEFAULTS parameter of the report settings. The provided defaults will replace the original one, while those that were not provided will remain untouched.

In the example below the NewReport class has Settings defined with a SECTION_DEFAULTS parameter, that will use KPI_IDS=["10", "20", "30"] default for the "EquipmentView" section and a custom IMAGE_PATH for the "Title" section.

All other sections used in this report will have a default settings. Also, other settings for the "EquipmentView", and "Title" sections will not be altered.

Example

class NewReport(Report):
    from smartreport.v3.reports.new_report import sections

    class Settings(Report.Settings):
        SECTION_DEFAULTS: Dict[str, ReportSection.Settings] = {
            "EquipmentView": sections.EquipmentViewSection.Settings(KPI_IDS=["10", "20", "30"]),
            "Title": sections.TitleSection.Settings(IMAGE_PATH=STATIC_FOLDER_PATH / "new_report_picture.jpg"),
        }

Section settings in report variant

Quite often we want to define several variants of given report type. Variant are build from the same poll of sections, but they might come in different order or setup.

It is also useful, if the variants can be defined outside the Python code. This way the non-developer users can also contribute to the report defining process. This is allowed by supporting the JSON based variant definition.

In order to enable the section settings customisation from JSON file, the report object builder can get the section settings from the file, and override the section defaults, coming both from the section itself or the SECTION_DEFAULTS setting in the Report settings.

In the example bellow the JSON file that defines the report variant has overriden some settings for "VibrationSignalsFFT" section. The NO_OF_HIGHEST_PEAKS will be equal to 3, and VELOCITY_SPECTRA_LIMIT will be equal to 1200 in that report variant.

Other section will be used with its default values.

Example

{
  "config": {
    "SUB_TITLE": "Instant asset report for motor"
  },
  "sections": [
    {
      "name": "Title"
    },
    {
      "name": "TOC"
    },
    {
      "name": "VibrationSignalsTime"
    },
    {
      "name": "VibrationSignalsFFT",
      "settings": {
        "NO_HIGHEST_PEAKS": 3,
        "VELOCITY_SPECTRA_LIMIT": 1200.0
      }
    }
  ]
}

Section settings in the report generation call

TO BE DONE

Creating new section

To create a new section you have to create a new Python class that inherits from ReportSection class. Usually, it should be placed in a separate file in the sections submodule.

Below you can see a full example of "TOCSection"

from smartreport.engine.translations import _
from smartreport.v3.reports.report import ReportSection


class TOCSection(ReportSection):
    """
    Adds automatic Table of Content to the report.
    """

    class Settings(ReportSection.Settings):
        """
        Attributes:
            TOC_DEPTH (int): Depth of Table of content.
                It means, how many levels of headings should be used
                to create table of content.
        """

        TOC_DEPTH: int = 2

    settings: Settings

    def create(self) -> None:
        self.report_output.add_heading(text=_("Table of Content"), level=1)
        self.report_output.add_toc(depth=self.settings.TOC_DEPTH)
        self.report_output.add_page_break()

In order to enable the for the given report type to be used, it has to be imported in the Report class body.

from smartreport.v3.reports.new_report.sections.visualize_data import VisualizeDataSection
from smartreport.v3.reports.new_report.sections.rul import RemainingUsefullLifetimeSection
class NewReport(Report):
    from smartreport.v3.reports.new_report import sections

Testing

Since the report section is an independent object it can be tested outside the report context. Each section can produce its own piece of report document, that can be verified without the need of generating the full report.

Sections can be tested independently of each other. If needed, the custom settings can be defined on section initialization.

Most suitable data source and report output can be used. e.g. JSON ReportOutput and Mocked Assert ReportSection implements the .test() method that returns a document with this section's content only.

Example

from hypothesis import given, strategies as st

from smartreport.engine.outputs.json_document import JSONReportOutput
from smartreport.v3.reports.common.sections import TitleSection


class TestTitleSectionSection:
    def test_default_section_creation(self):
        ro = JSONReportOutput()
        section = TitleSection(report_output=ro)
        test_out = section.test()

        # Paragraph([Span(ABB_CURSOR_CHAR)])
        assert test_out.document[0].data[0].data == section.settings.ABB_CURSOR_CHAR
        # Paragraph([Span(REPORT_PROVIDER)])
        assert test_out.document[1].data[0].data == section.settings.REPORT_PROVIDER
        # Paragraph([Span(MAIN_TITLE)])
        assert test_out.document[2].data[0].data == section.settings.MAIN_TITLE
        # Paragraph([Span(SUB_TITLE)])
        assert test_out.document[3].data[0].data == section.settings.SUB_TITLE
        # Pictures([Image(path=IMAGE_PATH) as B64 string])
        assert test_out.document[4].data[0] == ro._pic_to_b64(
            picture=section.settings.IMAGE_PATH)

    @given(sub_title=st.text(min_size=1, max_size=200))
    def test_custom_sub_title(self, sub_title):
        section = TitleSection(
            report_output=JSONReportOutput(),
            settings=TitleSection.Settings(SUB_TITLE=sub_title))
        test_out = section.test()

        assert test_out.document[3].data[0].data == section.settings.SUB_TITLE

Documentation

Each section is automatically documented, including settings specification. Based on specific report variant definition a full report documentation can be generated.

This requires from the section creator to provide a comprehensive docstring, and is automatically converted to documentation thanks to mkdocsting package.

Check Sections documentation pages for some examples.