YOUSEFUL / DELPHI / COMPONENTS
TAtlasReportDefinition
A serializable report definition: a detail projection or a grouped summary over a TAtlasTable, run into a table, an HTML page or a print document.
Description
A non-visual component on the Atlas Analytics palette page that describes a report and runs it over the current view of a TAtlasTable.
A definition has one of two shapes:
- Detail:
SelectFieldslists the columns to return (an empty list returns every column). A new definition is a detail report of every column. - Summary:
GroupBynames zero or more dimension columns and one or more TAtlasMeasure values; the result has one row per distinct combination of dimension values, in first-seen order.
SetFilters adds conjunctive TAtlasFilter conditions that apply before grouping. Execute returns the result as a new table, RenderHTML as a self-contained HTML page, and PreparePrint as a paginated TAtlasPrintDocument. The work is done by TAtlasAnalytics, so filters and measures behave exactly as described there. The source table is never modified.
Field names are not checked when you define the report or load it from JSON; they are checked every time it runs, against the table you pass. Only Title and Subtitle are published, so a report dropped on a form stores just those two properties: set the fields, groups and filters in code or load them with LoadJSON. No property reads back the fields, dimensions or measures; ToJSON shows them.
The unit also declares function AtlasEscapeHTML(const Text: string): string;, which replaces &, <, >, " and ' with HTML entities. RenderHTML uses it for every title, caption and value.
Reports are deterministic. Not implemented: a visual band designer, XLSX or PDF export, scripting, a database query language and AI-assisted reporting (pending a provider decision; no service is called). The class has no locking; do not change the source table from another thread while a report runs.
Members
| Member | Kind | What it does |
|---|---|---|
Create | constructor | Creates an empty definition: Title is 'Atlas report', Subtitle is empty, and there are no fields, groups or filters, so Execute returns a copy of every column and row of the source's current view. |
SelectFields | method | Makes the definition a detail report that returns the listed fields in this order; an empty array returns every field in source order. |
GroupBy | method | Makes the definition a summary report: the result has one column per dimension, then one ackNumber column per measure named by its caption, and one row per distinct combination of dimension values in first-seen order. |
SetFilters | method | Replaces the filters. |
Execute | method | Runs the definition over the current view of Source (the table's own filter and sort apply) and returns a new, unowned TAtlasTable that the caller must free. |
RenderHTML | method | Runs Execute and returns a complete HTML5 page with no external resources: Title in <title> and <h1>, Subtitle in a paragraph, a table with one <th scope="col"> per result column, numeric cells right-aligned with class="number", and a final N result rows paragraph. |
PreparePrint | method | Runs Execute and returns a new paginated TAtlasPrintDocument that the caller must free. |
ToJSON | method | Returns the definition as a version 1 JSON object with title, subtitle, fields, dimensions, measures (each field, caption, and kind as the ordinal of TAtlasMeasureKind) and filters (each field, value, and operator as the ordinal of TAtlasFilterOperator). |
LoadJSON | method | Replaces the whole definition with one written by ToJSON, after validating all of it: the text must be a JSON object with version 1, string title and subtitle, arrays fields and dimensions of non-blank strings, measures objects with a string field, a non-blank caption and a kind from 0 to 4, and filters objects with string field and value and an operator from 0 to 6. |
Filters | property | A copy of the current filters. |
Title | property | The report heading: the HTML <title> and <h1>, and the title of the document from PreparePrint. |
Subtitle | property | A paragraph below the heading in the HTML page (line breaks are kept). |
Constructors
Create constructor
constructor Create(AOwner: TComponent); override;Creates an empty definition: Title is 'Atlas report', Subtitle is empty, and there are no fields, groups or filters, so Execute returns a copy of every column and row of the source's current view. Pass nil and free the definition yourself, or pass an owner such as a form.
Report := TAtlasReportDefinition.Create(nil);Properties
Filters property
property Filters: TArray<TAtlasFilter> read GetFilters;A copy of the current filters. Changing the returned array does not change the report; call SetFilters instead.
for Filter in Report.Filters do
Writeln(Filter.Field, ' ', Filter.Value);Title property
property Title: string read FTitle write FTitle;The report heading: the HTML <title> and <h1>, and the title of the document from PreparePrint. Defaults to 'Atlas report'. Published, so it is stored with the form.
Subtitle property
property Subtitle: string read FSubtitle write FSubtitle;A paragraph below the heading in the HTML page (line breaks are kept). Empty by default. Not used by PreparePrint. Published.
Methods
SelectFields method
procedure SelectFields(const Fields: array of string);Makes the definition a detail report that returns the listed fields in this order; an empty array returns every field in source order. Clears any dimensions and measures set by GroupBy; filters are kept. Names match columns without regard to case and are checked when the report runs: an unknown name raises EAtlasAnalyticsError Unknown field: ..., and a field listed twice raises EAtlasDataError Duplicate column name: ....
Report.SelectFields(['Booked', 'Product', 'Revenue']);GroupBy method
procedure GroupBy(const Dimensions: array of string; const Measures: array of TAtlasMeasure);Makes the definition a summary report: the result has one column per dimension, then one ackNumber column per measure named by its caption, and one row per distinct combination of dimension values in first-seen order. Grouping uses the exact cell text, so East and east are separate groups. An empty Dimensions array gives one total row. Clears the field list set by SelectFields; filters are kept. Raises EAtlasAnalyticsError A grouped report needs a measure when Measures is empty, and leaves the definition unchanged. Dimension names and measure captions must all differ (compared without case), or running the report raises EAtlasDataError Duplicate column name: ....
Report.GroupBy(['Region'], [TAtlasMeasure.Create('Revenue', 'Revenue', amSum),
TAtlasMeasure.Create('', 'Orders', amCount)]);SetFilters method
procedure SetFilters(const Filters: array of TAtlasFilter);Replaces the filters. A row is included only when every filter matches; in a summary report the filters apply before grouping. An empty array removes all filters. Filters are checked when the report runs: an unknown field or afContains on a non-text field raises EAtlasAnalyticsError, and a value that does not fit the column kind (for example lots for a number) raises EAtlasDataError.
Report.SetFilters([TAtlasFilter.Create('Region', 'West'),
TAtlasFilter.Create('Revenue', '10000', afGreaterEqual)]);Execute method
function Execute(Source: TAtlasTable): TAtlasTable;Runs the definition over the current view of Source (the table's own filter and sort apply) and returns a new, unowned TAtlasTable that the caller must free. A detail report returns the selected columns of the matching rows in view order, with the source column names, kinds and widths. A summary report returns the dimension columns (named as you passed them) and one ackNumber column per measure. All result columns are read-only. Numbers are stored in invariant format, for example 12216.6666666667. Raises EAtlasAnalyticsError for a missing source, an unknown field, afContains on a non-text field or a numeric measure on a non-numeric field, and EAtlasDataError for filter values that do not fit their column and for duplicate result column names.
Summary := Report.Execute(Sales);
try
Writeln(Summary.Cell(0, 0), ' ', Summary.Cell(1, 0));
finally
Summary.Free;
end;RenderHTML method
function RenderHTML(Source: TAtlasTable): string;Runs Execute and returns a complete HTML5 page with no external resources: Title in <title> and <h1>, Subtitle in a paragraph, a table with one <th scope="col"> per result column, numeric cells right-aligned with class="number", and a final N result rows paragraph. Every title, caption and value is escaped. The inline style sheet repeats the table header on every printed page and avoids splitting rows across pages. Values are written as stored, without thousands separators or rounding. The page declares UTF-8, so write it with TEncoding.UTF8. Raises the same exceptions as Execute.
TFile.WriteAllText('report.html', Report.RenderHTML(Sales), TEncoding.UTF8);PreparePrint method
function PreparePrint(Source: TAtlasTable): TAtlasPrintDocument;Runs Execute and returns a new paginated TAtlasPrintDocument that the caller must free. Its Title is the report title and it is prepared with the default geometry of TAtlasPrintDocument.Prepare (794 by 1123 pixels, a 48-pixel margin, 28-pixel rows). Use the document's PageCount, Preview, RenderPage and Print. The subtitle is not printed. Raises the same exceptions as Execute. Output on a physical printer has not been certified.
Document := Report.PreparePrint(Sales);
try
Writeln(Document.PageCount, ' pages');
finally
Document.Free;
end;ToJSON method
function ToJSON: string;Returns the definition as a version 1 JSON object with title, subtitle, fields, dimensions, measures (each field, caption, and kind as the ordinal of TAtlasMeasureKind) and filters (each field, value, and operator as the ordinal of TAtlasFilterOperator). No data is included.
{"version":1,"title":"Sales by region","subtitle":"","fields":[],"dimensions":["Region"],"measures":[{"field":"Revenue","caption":"Revenue","kind":1}],"filters":[]}LoadJSON method
procedure LoadJSON(const Text: string);Replaces the whole definition with one written by ToJSON, after validating all of it: the text must be a JSON object with version 1, string title and subtitle, arrays fields and dimensions of non-blank strings, measures objects with a string field, a non-blank caption and a kind from 0 to 4, and filters objects with string field and value and an operator from 0 to 6. A definition cannot have both fields and measures, and dimensions need at least one measure. On any failure (including text that is not JSON) it raises EAtlasAnalyticsError and the current definition is unchanged. Field names are not checked against a table until the report runs.
Report.LoadJSON(TFile.ReadAllText('report.json', TEncoding.UTF8));Examples
Grouped report to an HTML file
Totals and average sale per region, written as a page that opens in any browser.
uses System.SysUtils, System.IOUtils, Atlas.Data, Atlas.Analytics, Atlas.Reports;
procedure WriteRegionReport(Sales: TAtlasTable; const FileName: string);
var
Report: TAtlasReportDefinition;
begin
Report := TAtlasReportDefinition.Create(nil);
try
Report.Title := 'Sales by region';
Report.Subtitle := 'Revenue and average sale per region';
Report.GroupBy(['Region'], [
TAtlasMeasure.Create('Revenue', 'Revenue', amSum),
TAtlasMeasure.Create('Revenue', 'Average sale', amAverage)]);
TFile.WriteAllText(FileName, Report.RenderHTML(Sales), TEncoding.UTF8);
finally
Report.Free;
end;
end;
Filtered detail report
Three columns of the large West sales; the caller frees the returned table.
uses Atlas.Data, Atlas.Analytics, Atlas.Reports;
function LargeWestSales(Sales: TAtlasTable): TAtlasTable;
var
Report: TAtlasReportDefinition;
begin
Report := TAtlasReportDefinition.Create(nil);
try
Report.SelectFields(['Booked', 'Product', 'Revenue']);
Report.SetFilters([
TAtlasFilter.Create('Region', 'West'),
TAtlasFilter.Create('Revenue', '10000', afGreaterEqual)]);
Result := Report.Execute(Sales);
finally
Report.Free;
end;
end;
Preview the first printed page
PreparePrint and Preview both return objects the caller frees.
uses Vcl.Graphics, Vcl.Imaging.pngimage, Atlas.Data, Atlas.Printing, Atlas.Reports;
procedure SavePreview(Report: TAtlasReportDefinition; Sales: TAtlasTable; const FileName: string);
var
Document: TAtlasPrintDocument;
Page: TBitmap;
Png: TPngImage;
begin
Document := Report.PreparePrint(Sales);
try
Page := Document.Preview(0);
try
Png := TPngImage.Create;
try
Png.Assign(Page);
Png.SaveToFile(FileName);
finally
Png.Free;
end;
finally
Page.Free;
end;
finally
Document.Free;
end;
end;
Keep a definition in a file
LoadJSON validates first, so a damaged file leaves the current definition in place.
uses System.SysUtils, System.IOUtils, Atlas.Analytics, Atlas.Reports;
procedure SaveDefinition(Report: TAtlasReportDefinition; const FileName: string);
begin
TFile.WriteAllText(FileName, Report.ToJSON, TEncoding.UTF8);
end;
function LoadDefinition(Report: TAtlasReportDefinition; const FileName: string): Boolean;
begin
Result := True;
try
Report.LoadJSON(TFile.ReadAllText(FileName, TEncoding.UTF8));
except
on EAtlasAnalyticsError do
Result := False;
end;
end;
Related
TAtlasAnalytics · TAtlasMeasure · TAtlasFilter · TAtlasDashboard · TAtlasTable · TAtlasPrintDocument