# Reports A **report** is a document of what an analysis found: text, tables and figures. It is saved as a file next to the results, and anyone can later turn it into HTML or LaTeX without having the analyzer that made it installed. Reports live in `plesty.lib.report`. ## Making and reading a report `analyzer.report(outputs)` returns the report for an analyzer's outputs: ```python from plesty.lib.report import REPORT_SUFFIX, Report outputs = analyzer(spectrum=sweep) analyzer.report(outputs).save(run_dir / f"fine_structure{REPORT_SUFFIX}") # *.report.json report = Report.load(run_dir / "fine_structure.report.json") # needs no analyzer report.to_html() # an HTML fragment: tables, equations, figures as inline SVG report.to_latex() # LaTeX: tables for the booktabs package, figures for pgfplots ``` `to_html()` shows equations as MathML with the `report` extra, and as their LaTeX source without it (see [Install](../install.md#extras)). ## The default report Without any code, `report()` makes a table of every output that is a result. Each entry of `output_schema` may say which it is, with a `role`: - `"result"` (the default): shown in the report; - `"intermediate"`: kept in the outputs but left out of the report, such as the single-line fits a final number was computed from. An entry may also give a `title` for its table. A structured result such as a `SpectrumFitData` is tabulated by the columns its type declares (see [Experiment data](../common/data_schemas.md#experiment-data)); a one-dimensional array becomes one column; single numbers share one table. ## Writing your own report Override `report()` in the analyzer to say more: a sentence on the result, a table laid out for a paper, a figure. ```python from plesty.lib.monitor import Line, Scatter from plesty.lib.report import Cell, Figure, Report, Table, Value def report(self, outputs): return Report( [ r"Two of four lines split by $\Delta E = 11.5 \pm 0.3$ µeV.", Table( header=[ [Cell("Line", rowspan=2), Cell("Splitting", colspan=2)], [Cell(r"$\Delta E$ (µeV)"), Cell(r"$\Delta\lambda$ (pm)")], ], rows=[[Cell("775.012 nm"), Value(11.5, err=0.3), Value(5.52, err=0.14)]], ), Figure([Scatter(x=angles, y=centres), Line(x=angles, y=fitted)]), ], title="Fine structure", provenance=self.provenance(), ) ``` A report is a list of blocks, in order: | Block | Holds | |---|---| | a plain string, or `Text` | a paragraph | | `Table(header=..., rows=..., title=, caption=)` | a table; each row is a list of cells | | `Figure([drawables], title=, caption=)` | a plot, made of the same drawables as a viewer's plots (see [The drawables](../viewing/plots.md#the-drawables)) | **Cells.** A `Cell` holds text, which may use `**bold**`, `*italic*`, `` `code` `` and `$math$`. A `Value(number, err=uncertainty)` is shown with as many digits as its uncertainty allows. A cell spans rows or columns with `rowspan=` and `colspan=`: it is written once, where it starts, and the places it covers are left out of the rows below and to its right. Every row must fill the table's width and spans may not overlap; a table that breaks this raises `ReportError` naming the row. For a table these cells cannot express, `Table.raw(html=..., latex=...)` takes finished markup; the HTML is cleaned before it is shown. **Ready-made tables.** `data_table(data)` tabulates a structured result by the columns its type declares, and `array_table(array)` tabulates one array as a single column, headed by its name, unit and description. Both return `None` for something they cannot tabulate, including anything longer than 200 rows (change the limit with `max_rows=`). ## Reports outside analyzers A report is not tied to an analyzer. An experiment can build one in a step and save it in its run folder, and a program that exports runs can collect the reports it finds there and render them.