strolch/li.strolch.website/www.strolch.li/documentation-reports.html

367 lines
19 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="google-site-verification" content="CPhbjooaiTdROm7Vs4E7kuHZvBfkeLUtonGgcVUbTL8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" href="ico/favicon.ico">
<title>Strolch: Reports</title>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/custom.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --><!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script><![endif]-->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="index.html">Strolch</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="index.html">Overview</a></li>
<li><a href="api.html">API</a></li>
<li class="active"><a href="documentation.html">Documentation</a></li>
<li><a href="tutorial.html">Tutorial</a></li>
<li><a href="downloads.html">Downloads</a></li>
<li><a href="development.html">Development</a></li>
<li><a href="blog.html">Blog</a></li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
<div class="container">
<div class="page-header">
<h1 class="page-title">Documentation: Reports</h1>
<p class="lead page-description">This page discusses how to create reports in Strolch</p>
</div>
<div class="content">
<p>Since Strolch has a generic model, it was rather straight forward to create a simple API for writing reports.
In Strolch a report is defined by using its own model, i.e. a Report is a <code>Resource</code> of type
<code>Report</code>.</p>
<p>A report consists of the following parts:</p>
<ul>
<li>policy definition, thus allowing extensions</li>
<li>basic configuration like base object type, order direction, etc.</li>
<li>column definitions</li>
<li>joins</li>
<li>ordering definition</li>
<li>filters</li>
</ul>
<p>An example of a report is as follows:</p>
<pre>
&lt;Resource Id="stockReport" Name="Stock Report" Type="Report"&gt;
&lt;ParameterBag Id="parameters" Name="parameters" Type="Parameters"&gt;
&lt;Parameter Id="objectType" Index="20" Hidden="false" Name="Object Type" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Player"/&gt;
&lt;Parameter Id="descending" Name="Descending order" Type="Boolean" Value="true"/&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="ordering" Name="Ordering" Type="Ordering"&gt;
&lt;Parameter Id="name" Name="Name" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="noTeamFilter" Name="Filter" Type="Filter"&gt;
&lt;Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/&gt;
&lt;Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Slot" Value="Bags/relations/team"/&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="columns" Name="Display Columns" Type="Display"&gt;
&lt;Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/&gt;
&lt;Parameter Id="birthDate" Name="Birth date" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Bags/parameters/birthDate"/&gt;
&lt;Parameter Id="team" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="$name"/&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="joins" Name="Joins" Type="Joins"&gt;
&lt;Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/&gt;
&lt;/ParameterBag&gt;
&lt;Policies&gt;
&lt;Policy Type="ReportPolicy" Value="java:li.strolch.report.policy.GenericReport"/&gt;
&lt;/Policies&gt;
&lt;/Resource&gt;</pre>
<p>This report</p>
<ul>
<li>shows all Resources of type player (parameter <code>objectType</code>) &rarr; marks the object type to
be show in the filter criteria (default), and that its sorting index is at 20.
</li>
<li>orders the report by player's name (parameter bag <code>ordering</code>)</li>
<li>filters out all players with no team assigned (parameter bag <code>noTeamFilter</code>)</li>
<li>defines three columns: Player, Birth date, Team (paramger bag <code>columns</code>)</li>
<li>joins in the resource of type <code>Team</code></li>
<li>Uses the <code>GenericReport</code> class to generate the report</li>
</ul>
<h2>GenericReport</h2>
The default generic report implemented in Strolch has the following features and options:
<h3>Parameters</h3>
<p>The parameters bag can contain the following parameters:</p>
<ul>
<li><code>objectType</code> &rarr; the base type of object to get the input for the report. This means that
the <code>Interpretation</code> is set to one of:
<ul>
<li>Resource-Ref</li>
<li>Order-Ref</li>
<li>Activity-Ref</li>
</ul>
and that the <code>UOM</code> and <code>value</code> of the parameter is set to
the type of element with which to retrieve the elements from the strolch model.
<br /> <b>Note:</b> that the attributes <code>Hidden</code> and <code>Index</code> define the visibility
and sorting index as filter criteria respectively.
</li>
<li><code>descending</code> &rarr; boolean flag to define if sorting is in descending order</li>
<li><code>allowMissingColumns</code> &rarr; flag to define if no exception should be thrown if a column is
missing
</li>
<li><code>dateRangeSel</code> &rarr; defines a lookup parameter to use as a date range selector. This
requires input when executing the report
</li>
</ul>
<h3>Lookups</h3>
<p>Many of the features of the generic report rely on looking up a value on the referenced element. The
following shows the ways that a lookup can be performed:</p>
<ul>
<li><code>$id</code> &rarr; lookup the ID of the element</li>
<li><code>$name</code> &rarr; lookup the name of the element</li>
<li><code>$type</code> &rarr; lookup the type of the element</li>
<li><code>$date</code> &rarr; lookup the date of the element (only possible on <code>Order</code> and <code>Activity</code>
elements)
</li>
<li><code>$state</code> &rarr; lookup the state of the element (only possible on <code>Order</code> and
<code>Activity</code> elements)
</li>
<li><code>Bags/&lt;bag_id&gt;/&lt;param_id&gt;</code> &rarr;</li>
<li><code>$search:&lt;parent_ref_id&gt;:Bags/&lt;bag_id&gt;/&lt;param_id&gt;</code> &rarr; searches for a
parameter with the given
bag and parameter, and
if it does not exist,
looks for the parent
with the given <code>parent_ref_id</code>
on the element. This
allows a recursive
search up a tree of
elements which all have
the same parameter
referencing a parent.
<code>relations</code> bag
</li>
</ul>
<p><b>Note:</b> these definitions are set as the value of a Parameter, and the <code>Interpretation</code> and
<code>UOM</code> of the parameter is used to find the element on which to perform the lookup. I.e. the
following definition:</p>
<pre>&lt;Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/&gt;</pre>
<p>defines that we want to lookup the name of the resource of type Player.</p>
<h3>Ordering</h3>
<p>Ordering, i.e sorting is done by adding the parameter bag with the id <code>ordering</code> and each
parameter defines a column to order by. The sequence of the ordering is defined by the index value assigned
to each parameter.</p>
<h3>Filtering</h3>
<p>Filtering use additional Strolch Policies which implement the operator function. I.e. performing an equals,
etc. The following <code>ReportFilterPolicy</code> are available and should be added in your <code>StrolchPolicies.xml</code>
file:</p>
<pre>
&lt;StrolchPolicies&gt;
...
&lt;PolicyType Type="ReportFilterPolicy" Api="li.strolch.report.policy.ReportFilterPolicy"&gt;
&lt;Policy Key="GreaterThan" Class="li.strolch.report.policy.GreaterThanReportFilter" /&gt;
&lt;Policy Key="LessThan" Class="li.strolch.report.policy.LessThanReportFilter" /&gt;
&lt;Policy Key="Equals" Class="li.strolch.report.policy.EqualsReportFilter" /&gt;
&lt;/PolicyType&gt;
...
&lt;StrolchPolicies&gt;</pre>
<p>From this we can see that we can perform a <code>GreaterThan</code>, <code>LessThan</code> and
<code>Equals</code> filtering. These filters can also be negated by prefixing the filter value with an
exclamation mark (!).</p>
<p>A special case for the filter values are filters on dates. If you are filtering on a date, then you can use
the special operator <code>now()</code>. This filter will use the current date and time and will add/subtract
the ISO8601 period passed as an argument to the operator.</p>
<p>The following shows examples of these filters:</p>
<pre>
&lt;ParameterBag Id="minQtyFilter" Name="Filter" Type="Filter"&gt;
&lt;Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:GreaterThan" Value="10"/&gt;
&lt;Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/quantity"/&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="notEmptyFilter" Name="Filter" Type="Filter"&gt;
&lt;Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/&gt;
&lt;Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Bags/relations/team"/&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="threeMonthsAgoFilter" Name="Filter" Type="Filter"&gt;
&lt;Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:LessThan" Value="now(-P3M)"/&gt;
&lt;Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="FromStock" Value="$date"/&gt;
&lt;/ParameterBag&gt;</pre>
<p><b>Note: </b> One parameter defines which policy gets used and the <code>key:&lt;name&gt;</code> value
references a policy defined in the <code>StrolchPolicies.xml</code> file. Further the lookup is
defined in the <code>fieldRef</code> parameter.</p>
<h3>Joins</h3>
<p>To add columns from data which is not on the element denoted by the base object type, we can join further
elements. This is done by adding the parameter bag <code>joins</code> and then each parameter references an
element to join. The joining is done as follows:</p>
<ul>
<li>The <code>Intepretation</code> and <code>UOM</code> define which object we want to join, i.e. resource
of type foo
</li>
<li>The value of the parameter defines the type of element on which to find the reference</li>
<li>The join ordering is not relevant, as the tree is traversed accordingly</li>
<li>At least one join must reference the base object type</li>
<li>The lookup of the join is done by finding a parameter with any ID, which has the same <code>Interpretation</code>
and <code>UOM</code> as the join definition
</li>
<li>The attributes <code>Hidden</code> and <code>Index</code> define the visibility and sorting index as
filter criteria respectively.
</li>
</ul>
<p>Thus the following:</p>
<pre>
&lt;ParameterBag Id="joins" Name="Joins" Type="Joins"&gt;
&lt;Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/&gt;
&lt;Parameter Id="Country" Index="5" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Country" Value="Team"/&gt;
&lt;/ParameterBag&gt;</pre>
<p>Performs two joins: First we join a resource of type <code>Team</code> by finding the relevant parameter on
the <code>Player</code> resource, and then we lookup a resource of type <code>Country</code> on the
previously joined <code>Team</code> resource.</p>
<h3>Execution of Reports</h3>
<p>To execute a reports, we must instantiate the Report and can then directly generate a JsonObject stream,
which we can then pipe to a browser, file, etc.:</p>
<pre>
Stream&lt;JsonObject&gt; jsonObjectStream = new Report(tx, reportId).doReportAsJson();</pre>
<p>If you prefer a CSV report:</p>
<pre>
try (CSVPrinter csvP = new CSVPrinter(new OutputStreamWriter(out),
CSVFormat.DEFAULT.withHeader(headers).withDelimiter(';'))) {
// do report without AsJson, and then iterating each row and sending to a CSV writer
report.doReport().forEach(row -> {
try {
csvP.printRecord(row.valueStream().collect(Collectors.toList())); // add to CSV
} catch (Exception e) {
logger.error("Could not write CSV row", e);
}
});
}
</pre>
<h3>Filter Criteria</h3>
<p>Predefining filters is a good start, but in some case you only want a portion of the actual filtered data.
For instance if you make a stock report, you might only want one location. This information is dynamic and
thus not stored on the report definition.</p>
<p>To perform these dynamic filterings, one would call the <code>filter()</code> method on the report, passing
the type of element to be filtered, and to which element IDs to reduce the report data to. The following
reduces the report to only return the rows with the <code>product01</code> Product and
<code>location02</code> Location elements:</p>
<pre>
new Report(tx, "stockReport")
.filter("Product", "product01")
.filter("Location", "location02")
.doReportAsJson()</pre>
<p>It is possible to find the possible filter criteria dynamically using the
<code>generateFilterCriteria()</code> method.</p>
<h3>Date Range Filtering</h3>
<p>The last option to filter dynamically is using a date range selector. Define the <code>dateRangeSel</code>
lookup parameter, and then set the date range on the instantiated report:</p>
<pre>
&lt;ParameterBag Id="parameters" Name="parameters" Type="Parameters"&gt;
...
&lt;Parameter Id="dateRangeSel" Name="Date Range Selector" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/expirationDate"/&gt;
...
&lt;/ParameterBag&gt;
Date from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000);
Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000);
DateRange dateRange = new DateRange().from(from, true).to(to, false);
List&lt;JsonObject&gt; result = new Report(tx, "stockReport") //
.filter("Product", "product01") //
.dateRange(dateRange) //
.doReportAsJson()
</pre>
<p><b>Note:</b> See the
<a href="https://github.com/4treesCH/strolch/blob/develop/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java"
target="_blank">GenericReportTest</a> for examples.</p>
<!-- content here -->
</div>
<!-- /.content -->
<div id="footer">
<div class="container">
<p class="text-muted">&copy; Strolch / <a href="mailto:eitch@eitchnet.ch">Robert von Burg</a> / Hosting by
<a href="http://www.eitchnet.ch">eitchnet.ch</a></p>
</div>
</div>
</div>
<!-- /.container -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual xsd as needed -->
<script src="js/bootstrap.min.js"></script>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = (("https:" == document.location.protocol) ? "https" : "http") + "://piwik.eitchnet.ch/";
_paq.push(['setTrackerUrl', u + 'piwik.php']);
_paq.push(['setSiteId', 2]);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.type = 'text/javascript';
g.defer = true;
g.async = true;
g.src = u + 'piwik.js';
s.parentNode.insertBefore(g, s);
})();
</script>
<noscript><p><img src="http://piwik.eitchnet.ch/piwik.php?idsite=2" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body>
</html>