Add a New Reporting SectionΒΆ
This guide explains how to add a new reporting section in OwlMix end to end. It covers analysis, plotting, registry wiring, params, report section registration, and docs/template integration.
OverviewΒΆ
When you add a new section, all related keys must stay consistent across modules.
For example, if the section key is my_section, reuse the same key in:
ANALYZERS_REGISTRYkey (if analyzer exists)PLOTTERS_REGISTRYkey (if plotter exists)@register_section("my_section")decoratorconfig attribute names such as
my_section_config
Core flow:
Create analysis and/or plotting classes.
Register classes in central registries.
Create params and expose them via
params/__init__.py.Add config wiring in
config_builder.py.Create a reporting section builder under
reporting/sections.Import the section module in
reporting/sections/__init__.py.Optionally add enum and HTML template support.
Step-by-stepΒΆ
1. Pick a section key and scopeΒΆ
Choose one stable key such as my_section and decide if the section is:
analysis-only
plotting-only
both analysis and plotting
2. Add analysis and/or plotting implementationΒΆ
If analysis is required:
Add analyzer class and analyzer params in
src/owlmix/analysis.
If plotting is required:
Add plotter class and plotter params in
src/owlmix/plotting.
3. Register classes in registry.pyΒΆ
Update src/owlmix/registry/registry.py:
Import your analyzer and params class (if any).
Import your plotter and params class (if any).
Add entry to
ANALYZERS_REGISTRYfor analyzer sections.Add entry to
PLOTTERS_REGISTRYfor plotting sections.
Expected dictionary formats:
Analyzer registry entry:
"my_section": { "analyzer": MySectionAnalyzer, "params": MySectionAnalyzerParams, }
Plotter registry entry:
"my_section": { "plotter": MySectionPlotter, "params": MySectionPlotParams, }
4. Create params module and export in params/__init__.pyΒΆ
Create src/owlmix/params/my_section.py with config args/dataclass and a
build(...) path that matches your existing params pattern.
Then update src/owlmix/params/__init__.py:
import
my_sectionmoduleexport config args and params class in
__all__add
Args.my_section = my_sectionstyle wiring
This is required so config builder can call Args.my_section.build(...).
5. Wire config in config_builder.pyΒΆ
Update src/owlmix/config/config_builder.py:
In
_init_configadd:self.my_section_config = Args.my_section.build(...)
In
update_configmapping add:"my_section": self.update_my_section_config,
Add updater method:
def update_my_section_config(self, **kwargs: Unpack[MySectionConfigArgs]) -> Self: self.my_section_config = replace(self.my_section_config, **kwargs) return self
6. Add reporting section builderΒΆ
Create src/owlmix/reporting/sections/my_section.py and register it with
the section key.
Skeleton:
from typing import Any, Dict
from ...registry.registry import register_section, ANALYZERS_REGISTRY, PLOTTERS_REGISTRY
from .protocol_cls import ReportBuilderProtocol
@register_section("my_section")
def build_my_section(report_builder: ReportBuilderProtocol) -> Dict[str, Any]:
config = report_builder.config.my_section_config
# Optional analyzer path
data: Dict[str, Any] = {}
if "my_section" in ANALYZERS_REGISTRY:
analyzer_cls = ANALYZERS_REGISTRY["my_section"]["analyzer"]
analyzer_params_cls = ANALYZERS_REGISTRY["my_section"]["params"]
analyzer_params = analyzer_params_cls(...)
analyzer = analyzer_cls(df=report_builder.df.copy(deep=True), params=analyzer_params)
data = analyzer.compute()
# Optional plotter path
chart = None
if "my_section" in PLOTTERS_REGISTRY:
plotter_cls = PLOTTERS_REGISTRY["my_section"]["plotter"]
plotter_params_cls = PLOTTERS_REGISTRY["my_section"]["params"]
plotter_params = plotter_params_cls(...)
plotter = plotter_cls(df=report_builder.df, params=plotter_params)
path = plotter.plot(output_dir=f"{report_builder.config.output_dir}/charts")
chart = {
"title": "My Section",
"description": "Description for My Section.",
"alt_text": "My section chart",
"image": report_builder.image_to_base64(path),
}
return {"data": data, "chart": chart}
7. Import section module in sections __init__.pyΒΆ
Update src/owlmix/reporting/sections/__init__.py and add:
from . import my_section
This import is mandatory. Without it, decorator execution does not happen and
the section is not added to SECTION_BUILDERS.
8. Optional but recommended: add enum supportΒΆ
If you use enum-based section selection APIs, add the section into
src/owlmix/typing/enums.py under SectionEnum.
Example:
MY_SECTION = ("my_section", "My Section")
9. Optional but recommended: add HTML template renderingΒΆ
If section output must appear in rendered HTML reports:
Create template file under
src/owlmix/reporting/_templates/sections(for examplemy_section.html)Include it in
src/owlmix/reporting/_templates/_default.html
Without template integration, section data may exist in JSON but not in the final HTML page.
10. Validation checklistΒΆ
Before opening PR, verify all items:
New analyzer and/or plotter class added.
Registries updated in
registry.py.Params module created and exported via
params/__init__.py.ConfigBuilderdefault and update wiring added.Reporting section builder added with
@register_section.Module imported in
reporting/sections/__init__.py.SectionEnum updated (if enum selection is used).
HTML template added and included (if HTML rendering is required).
Relevant tests added or updated.
Common pitfallsΒΆ
Inconsistent section key naming across files.
Missing import in
reporting/sections/__init__.py.Config attribute name mismatch (for example, using
config.my_configbut definingself.my_section_config).Registry key mismatch between section builder and
registry.py.Forgetting to expose params in
params/__init__.py.