[Major] GenericReport now supports filter policies

The first two are:
- GreaterThan for FloatParameters
- Equals for strings
This commit is contained in:
Robert von Burg 2017-05-03 15:32:20 +02:00
parent 96ed97132b
commit 5f1f4af7b0
7 changed files with 217 additions and 33 deletions

View File

@ -13,6 +13,7 @@ import java.util.stream.Stream;
import com.google.gson.JsonObject;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.Locator;
import li.strolch.model.ParameterBag;
import li.strolch.model.Resource;
@ -21,9 +22,12 @@ import li.strolch.model.StrolchValueType;
import li.strolch.model.parameter.DateParameter;
import li.strolch.model.parameter.Parameter;
import li.strolch.model.parameter.StringParameter;
import li.strolch.model.policy.PolicyDef;
import li.strolch.model.visitor.ElementDateVisitor;
import li.strolch.model.visitor.ElementStateVisitor;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.policy.PolicyHandler;
import li.strolch.report.policy.ReportFilterPolicy;
import li.strolch.runtime.StrolchConstants;
import li.strolch.utils.collections.DateRange;
import li.strolch.utils.collections.MapOfSets;
@ -35,6 +39,7 @@ import li.strolch.utils.iso8601.ISO8601FormatFactory;
public class GenericReport {
private static final String TYPE_REPORT = "Report";
private static final String TYPE_FILTER = "Filter";
private static final String BAG_RELATIONS = "relations";
private static final String BAG_JOINS = "joins";
@ -43,6 +48,8 @@ public class GenericReport {
private static final String PARAM_OBJECT_TYPE = "objectType";
private static final String PARAM_DATE_RANGE_SEL = "dateRangeSel";
private static final String PARAM_FIELD_REF = "fieldRef";
private static final String PARAM_POLICY = "policy";
private static final String COL_ID = "$id";
private static final String COL_NAME = "$name";
@ -65,17 +72,36 @@ public class GenericReport {
private StringParameter dateRangeSelP;
private DateRange dateRange;
private MapOfSets<String, String> filtersByType;
private Map<ReportFilterPolicy, StringParameter> filtersByPolicy;
private MapOfSets<String, String> filtersById;
public GenericReport(StrolchTransaction tx, String reportId) {
public GenericReport(ComponentContainer container, StrolchTransaction tx, String reportId) {
this.tx = tx;
this.reportId = reportId;
// get the report
this.report = this.tx.getResourceBy(TYPE_REPORT, this.reportId, true);
// prepare
this.columnsBag = this.report.getParameterBag(BAG_COLUMNS, true);
this.columnIds = this.columnsBag.getParameterKeySet();
this.dateRangeSelP = this.report.getParameter(BAG_PARAMETERS, PARAM_DATE_RANGE_SEL);
// evaluate filters
this.filtersByPolicy = new HashMap<>();
List<ParameterBag> filterBags = this.report.getParameterBagsByType(TYPE_FILTER);
for (ParameterBag filterBag : filterBags) {
// prepare filter function policy
StringParameter functionP = filterBag.getParameter(PARAM_POLICY);
PolicyHandler policyHandler = container.getComponent(PolicyHandler.class);
PolicyDef policyDef = PolicyDef.valueOf(functionP.getInterpretation(), functionP.getUom());
ReportFilterPolicy filterFunction = policyHandler.getPolicy(policyDef, tx);
filterFunction.init(functionP.getValue());
StringParameter fieldRefP = filterBag.getParameter(PARAM_FIELD_REF);
this.filtersByPolicy.put(filterFunction, fieldRefP);
}
}
public boolean hasDateRangeSelector() {
@ -95,28 +121,28 @@ public class GenericReport {
}
public GenericReport filter(String type, String... ids) {
if (this.filtersByType == null)
this.filtersByType = new MapOfSets<>();
if (this.filtersById == null)
this.filtersById = new MapOfSets<>();
for (String id : ids) {
this.filtersByType.addElement(type, id);
this.filtersById.addElement(type, id);
}
return this;
}
public GenericReport filter(String type, List<String> ids) {
if (this.filtersByType == null)
this.filtersByType = new MapOfSets<>();
if (this.filtersById == null)
this.filtersById = new MapOfSets<>();
for (String id : ids) {
this.filtersByType.addElement(type, id);
this.filtersById.addElement(type, id);
}
return this;
}
public GenericReport filter(String type, Set<String> ids) {
if (this.filtersByType == null)
this.filtersByType = new MapOfSets<>();
if (this.filtersById == null)
this.filtersById = new MapOfSets<>();
for (String id : ids) {
this.filtersByType.addElement(type, id);
this.filtersById.addElement(type, id);
}
return this;
}
@ -148,7 +174,26 @@ public class GenericReport {
private boolean filter(Map<String, StrolchRootElement> row) {
// first we do a date range selection, if required
// do filtering by policies
for (ReportFilterPolicy filterPolicy : this.filtersByPolicy.keySet()) {
StringParameter fieldRefP = this.filtersByPolicy.get(filterPolicy);
String type = fieldRefP.getUom();
StrolchRootElement column = row.get(type);
if (fieldRefP.getValue().startsWith("$")) {
String columnValue = evaluateColumnValue(fieldRefP, row);
if (!filterPolicy.filter(columnValue))
return false;
} else {
Parameter<?> param = lookupParameter(fieldRefP, column);
if (!filterPolicy.filter(param))
return false;
}
}
// do a date range selection, if required
if (this.dateRange != null) {
if (this.dateRangeSelP == null)
throw new IllegalStateException(
@ -179,13 +224,13 @@ public class GenericReport {
}
// then we do a filter by criteria
if (this.filtersByType != null && !this.filtersByType.isEmpty()) {
for (String type : this.filtersByType.keySet()) {
if (this.filtersById != null && !this.filtersById.isEmpty()) {
for (String type : this.filtersById.keySet()) {
StrolchRootElement element = row.get(type);
if (element == null)
return false;
if (!this.filtersByType.getSet(type).contains(element.getId()))
if (!this.filtersById.getSet(type).contains(element.getId()))
return false;
}
}
@ -354,6 +399,8 @@ public class GenericReport {
// recursively find the dependency
StringParameter dependencyP = joinBag.getParameter(dependencyType);
dependency = addColumnJoin(refs, joinBag, dependencyP, false);
if (dependency == null)
return null;
}
ParameterBag relationsBag = dependency.getParameterBag(BAG_RELATIONS);
@ -378,13 +425,9 @@ public class GenericReport {
}
Locator locator = Locator.valueOf(elementType, joinType, relationP.getValue());
StrolchRootElement joinElem;
try {
joinElem = this.tx.findElement(locator);
} catch (Exception e) {
throw new IllegalStateException("Failed to find join element " + joinType + " for dependency "
+ dependency.getLocator() + " with locator " + locator);
}
StrolchRootElement joinElem = this.tx.findElement(locator, true);
if (joinElem == null)
return null;
refs.put(joinType, joinElem);
return joinElem;

View File

@ -0,0 +1,40 @@
package li.strolch.report.policy;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.parameter.Parameter;
import li.strolch.persistence.api.StrolchTransaction;
public class EqualsReportFilter extends ReportFilterPolicy {
private boolean negate;
private String value;
public EqualsReportFilter(ComponentContainer container, StrolchTransaction tx) {
super(container, tx);
}
@Override
public void init(String value) {
if (value.startsWith("!")) {
this.negate = true;
this.value = value.substring(1);
} else {
this.value = value;
}
}
@Override
public boolean filter(Parameter<?> parameter) {
String value = parameter.getValueAsString();
return filter(value);
}
@Override
public boolean filter(String value) {
if (this.negate)
return !this.value.equals(value);
else
return this.value.equals(value);
}
}

View File

@ -0,0 +1,41 @@
package li.strolch.report.policy;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.parameter.FloatParameter;
import li.strolch.model.parameter.Parameter;
import li.strolch.persistence.api.StrolchTransaction;
public class GreaterThanReportFilter extends ReportFilterPolicy {
private boolean negate;
private double value;
public GreaterThanReportFilter(ComponentContainer container, StrolchTransaction tx) {
super(container, tx);
}
@Override
public void init(String value) {
if (value.startsWith("!")) {
this.negate = true;
this.value = Double.parseDouble(value.substring(1));
} else {
this.value = Double.parseDouble(value);
}
}
@Override
public boolean filter(Parameter<?> parameter) {
FloatParameter floatP = (FloatParameter) parameter;
if (this.negate)
return floatP.getValue().doubleValue() <= this.value;
else
return floatP.getValue().doubleValue() > this.value;
}
@Override
public boolean filter(String value) {
throw new UnsupportedOperationException("Greater Than not supported on string!");
}
}

View File

@ -0,0 +1,24 @@
package li.strolch.report.policy;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.model.parameter.Parameter;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.policy.StrolchPolicy;
public abstract class ReportFilterPolicy extends StrolchPolicy {
public ReportFilterPolicy(ComponentContainer container, StrolchTransaction tx) {
super(container, tx);
}
public abstract void init(String value);
public abstract boolean filter(String value);
public abstract boolean filter(Parameter<?> param);
@Override
public void undo() {
// do nothing
}
}

View File

@ -50,7 +50,7 @@ public class GenericReportTest {
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) {
GenericReport report = new GenericReport(tx, "stockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
report.doReportAsJson().forEach(e -> {
if (e.get("slot").getAsString().equals("Slot 1")) {
@ -105,7 +105,7 @@ public class GenericReportTest {
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) {
GenericReport report = new GenericReport(tx, "stockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
report.filter("Product", "product01").doReportAsJson().forEach(e -> {
String slotName = e.get("slot").getAsString();
@ -126,7 +126,7 @@ public class GenericReportTest {
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) {
GenericReport report = new GenericReport(tx, "stockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
report.filter("Product", "product01").filter("Location", "location02").doReportAsJson().forEach(e -> {
String slotName = e.get("slot").getAsString();
@ -151,7 +151,7 @@ public class GenericReportTest {
DateRange dateRange = new DateRange().from(from, true).to(to, false);
// expect no slots as all not in date range
GenericReport report = new GenericReport(tx, "stockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
List<JsonObject> result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
.collect(Collectors.toList());
assertTrue(result.isEmpty());
@ -159,7 +159,7 @@ public class GenericReportTest {
// expect 2 slots, as in date range
to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
dateRange = new DateRange().from(from, true).to(to, false);
report = new GenericReport(tx, "stockReport");
report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
.collect(Collectors.toList());
assertEquals(2, result.size());
@ -176,7 +176,7 @@ public class GenericReportTest {
DateRange dateRange = new DateRange().from(from, true).to(to, false);
// expect no orders as all not in date range
GenericReport report = new GenericReport(tx, "fromStockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "fromStockReport");
List<JsonObject> result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
.collect(Collectors.toList());
assertTrue(result.isEmpty());
@ -184,7 +184,7 @@ public class GenericReportTest {
// expect 2 orders, as in date range
to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
dateRange = new DateRange().from(from, true).to(to, false);
report = new GenericReport(tx, "fromStockReport");
report = new GenericReport(runtimeMock.getContainer(), tx, "fromStockReport");
result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
.collect(Collectors.toList());
assertEquals(2, result.size());
@ -192,7 +192,7 @@ public class GenericReportTest {
// expect 4 orders, as all in date range
to = new Date(LocalDate.of(2017, 3, 2).toEpochDay() * 86400000);
dateRange = new DateRange().from(from, true).to(to, false);
report = new GenericReport(tx, "fromStockReport");
report = new GenericReport(runtimeMock.getContainer(), tx, "fromStockReport");
result = report.filter("Product", "product01", "product02").dateRange(dateRange).doReportAsJson()
.collect(Collectors.toList());
assertEquals(4, result.size());
@ -204,7 +204,7 @@ public class GenericReportTest {
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) {
GenericReport report = new GenericReport(tx, "stockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
MapOfSets<String, StrolchRootElement> filterCriteria = report.generateFilterCriteria();
assertThat(filterCriteria.getSet("Product").stream().map(e -> e.getId()).collect(Collectors.toSet()),
@ -225,7 +225,7 @@ public class GenericReportTest {
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) {
GenericReport report = new GenericReport(tx, "stockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
MapOfSets<String, StrolchRootElement> filterCriteria = report //
.filter("Product", "product01") //
.generateFilterCriteria();
@ -252,7 +252,7 @@ public class GenericReportTest {
Date to = new Date(LocalDate.of(2017, 3, 5).toEpochDay() * 86400000);
DateRange dateRange = new DateRange().from(from, true).to(to, false);
GenericReport report = new GenericReport(tx, "stockReport");
GenericReport report = new GenericReport(runtimeMock.getContainer(), tx, "stockReport");
MapOfSets<String, StrolchRootElement> filterCriteria = report //
.dateRange(dateRange) //
.filter("Product", "product01") //

View File

@ -1,3 +1,8 @@
<StrolchPolicies>
<PolicyType Type="ReportFilterPolicy" Api="li.strolch.report.policy.ReportFilterPolicy">
<Policy Key="GreaterThan" Class="li.strolch.report.policy.GreaterThanReportFilter" />
<Policy Key="Equals" Class="li.strolch.report.policy.EqualsReportFilter" />
</PolicyType>
</StrolchPolicies>

View File

@ -8,6 +8,15 @@
<Parameter Id="dateRangeSel" Name="Date Range Selector" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/expirationDate" />
</ParameterBag>
<ParameterBag Id="quantityFilter" Name="Filter" Type="Filter">
<Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:GreaterThan" Value="0.0" />
<Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Slot" Value="Bags/parameters/quantity" />
</ParameterBag>
<ParameterBag Id="noProductFilter" Name="Filter" Type="Filter">
<Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!-" />
<Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="$id" />
</ParameterBag>
<ParameterBag Id="columns" Name="Display Columns" Type="Display">
<Parameter Id="location" Name="Location" Type="String" Interpretation="Resource-Ref" Uom="Location" Value="$name" />
<Parameter Id="storage" Name="Storage" Type="String" Interpretation="Resource-Ref" Uom="Storage" Value="$name" />
@ -189,4 +198,26 @@
</ParameterBag>
</Resource>
<Resource Id="slot005" Name="Slot 4" Type="Slot">
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="0.0" />
<Parameter Id="maxQuantity" Name="max. quantity of items" Type="Float" Value="20.0" />
</ParameterBag>
<ParameterBag Id="relations" Name="Relations" Type="Parameters">
<Parameter Id="parentSection" Name="Section" Type="String" Interpretation="Resource-Ref" Uom="Section" Value="section002" />
<Parameter Id="product" Name="Product" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="product02" />
</ParameterBag>
</Resource>
<Resource Id="slot006" Name="Slot 4" Type="Slot">
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="22.0" />
<Parameter Id="maxQuantity" Name="max. quantity of items" Type="Float" Value="20.0" />
</ParameterBag>
<ParameterBag Id="relations" Name="Relations" Type="Parameters">
<Parameter Id="parentSection" Name="Section" Type="String" Interpretation="Resource-Ref" Uom="Section" Value="section002" />
<Parameter Id="product" Name="Product" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="-" />
</ParameterBag>
</Resource>
</StrolchModel>