From 5f1f4af7b0785ab80c8d6fee35040946dfe8d9ca Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Wed, 3 May 2017 15:32:20 +0200 Subject: [PATCH] [Major] GenericReport now supports filter policies The first two are: - GreaterThan for FloatParameters - Equals for strings --- .../java/li/strolch/report/GenericReport.java | 87 ++++++++++++++----- .../report/policy/EqualsReportFilter.java | 40 +++++++++ .../policy/GreaterThanReportFilter.java | 41 +++++++++ .../report/policy/ReportFilterPolicy.java | 24 +++++ .../li/strolch/report/GenericReportTest.java | 22 ++--- .../reporttest/config/StrolchPolicies.xml | 5 ++ .../reporttest/data/StrolchModel.xml | 31 +++++++ 7 files changed, 217 insertions(+), 33 deletions(-) create mode 100644 li.strolch.service/src/main/java/li/strolch/report/policy/EqualsReportFilter.java create mode 100644 li.strolch.service/src/main/java/li/strolch/report/policy/GreaterThanReportFilter.java create mode 100644 li.strolch.service/src/main/java/li/strolch/report/policy/ReportFilterPolicy.java diff --git a/li.strolch.service/src/main/java/li/strolch/report/GenericReport.java b/li.strolch.service/src/main/java/li/strolch/report/GenericReport.java index 6cbbf4b13..95fb9485b 100644 --- a/li.strolch.service/src/main/java/li/strolch/report/GenericReport.java +++ b/li.strolch.service/src/main/java/li/strolch/report/GenericReport.java @@ -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 filtersByType; + private Map filtersByPolicy; + private MapOfSets 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 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 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 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 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; diff --git a/li.strolch.service/src/main/java/li/strolch/report/policy/EqualsReportFilter.java b/li.strolch.service/src/main/java/li/strolch/report/policy/EqualsReportFilter.java new file mode 100644 index 000000000..b9c2a6853 --- /dev/null +++ b/li.strolch.service/src/main/java/li/strolch/report/policy/EqualsReportFilter.java @@ -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); + + } +} diff --git a/li.strolch.service/src/main/java/li/strolch/report/policy/GreaterThanReportFilter.java b/li.strolch.service/src/main/java/li/strolch/report/policy/GreaterThanReportFilter.java new file mode 100644 index 000000000..94e1a1d6a --- /dev/null +++ b/li.strolch.service/src/main/java/li/strolch/report/policy/GreaterThanReportFilter.java @@ -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!"); + } +} diff --git a/li.strolch.service/src/main/java/li/strolch/report/policy/ReportFilterPolicy.java b/li.strolch.service/src/main/java/li/strolch/report/policy/ReportFilterPolicy.java new file mode 100644 index 000000000..7c94ec8e3 --- /dev/null +++ b/li.strolch.service/src/main/java/li/strolch/report/policy/ReportFilterPolicy.java @@ -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 + } +} diff --git a/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java b/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java index 629f9e922..95f56bd72 100644 --- a/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java +++ b/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java @@ -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 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 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 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 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 filterCriteria = report // .dateRange(dateRange) // .filter("Product", "product01") // diff --git a/li.strolch.service/src/test/resources/reporttest/config/StrolchPolicies.xml b/li.strolch.service/src/test/resources/reporttest/config/StrolchPolicies.xml index cba3b97d9..c4305d6c6 100644 --- a/li.strolch.service/src/test/resources/reporttest/config/StrolchPolicies.xml +++ b/li.strolch.service/src/test/resources/reporttest/config/StrolchPolicies.xml @@ -1,3 +1,8 @@ + + + + + \ No newline at end of file diff --git a/li.strolch.service/src/test/resources/reporttest/data/StrolchModel.xml b/li.strolch.service/src/test/resources/reporttest/data/StrolchModel.xml index 960e917cd..5dec9e6a6 100644 --- a/li.strolch.service/src/test/resources/reporttest/data/StrolchModel.xml +++ b/li.strolch.service/src/test/resources/reporttest/data/StrolchModel.xml @@ -8,6 +8,15 @@ + + + + + + + + + @@ -189,4 +198,26 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file