[Major] GenericReport now supports filter policies
The first two are: - GreaterThan for FloatParameters - Equals for strings
This commit is contained in:
parent
96ed97132b
commit
5f1f4af7b0
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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!");
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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") //
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in New Issue