Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DROOLS-5964] #1458

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.drools.workbench.screens.scenariosimulation.model.typedescriptor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -37,6 +38,7 @@ public class FactModelTree {
public enum Type {
INPUT,
DECISION,
PREDICTION,
UNDEFINED
}

Expand Down Expand Up @@ -117,6 +119,18 @@ public static FactModelTree ofDMN(String factName, String importPrefix, Map<Stri
return new FactModelTree(factName, "", simpleProperties, genericTypesMap, type, typeName, importPrefix);
}

/**
* Static factory method to be used in PMML context.
* @param factName
* @param simpleProperties
* @param typeName
* @param type
* @return
*/
public static FactModelTree ofPMML(String factName, String importPrefix, Map<String, PropertyTypeName> simpleProperties, String typeName, Type type) {
return new FactModelTree(factName, "", simpleProperties, Collections.emptyMap(), type, typeName, importPrefix);
}

/**
* Static factory method to be used in DMN context, to create a Simple Type FactModelTree.
* @param factName
Expand All @@ -134,6 +148,22 @@ public static FactModelTree ofSimpleDMN(String factName, String importPrefix, St
return toReturn;
}

/**
* Static factory method to be used in DMN context, to create a Simple Type FactModelTree.
* @param factName
* @param simplePropertyType
* @param typeName
* @param type
* @return
*/
public static FactModelTree ofSimplePMML(String factName, String simplePropertyType, String typeName, Type type) {
Map<String, FactModelTree.PropertyTypeName> simpleProperties = new HashMap<>();
simpleProperties.put(VALUE, new FactModelTree.PropertyTypeName(simplePropertyType));
FactModelTree toReturn = new FactModelTree(factName, "", simpleProperties, Collections.emptyMap(), type, typeName, "");
toReturn.setSimple(true);
return toReturn;
}

public FactModelTree() {
// CDI
}
Expand Down Expand Up @@ -273,7 +303,7 @@ public FactModelTree cloneFactModelTree() {
public String toString() {
return "FactModelTree{" +
"factName='" + factName + '\'' +
"typeName='" + typeName + '\'' +
", typeName='" + typeName + '\'' +
", simpleProperties=" + simpleProperties +
", expandableProperties=" + expandableProperties +
'}';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.workbench.screens.scenariosimulation.service;

import org.drools.workbench.screens.scenariosimulation.model.typedescriptor.FactModelTuple;
import org.jboss.errai.bus.server.annotations.Remote;
import org.uberfire.backend.vfs.Path;

@Remote
public interface PMMLTypeService {

/**
* Retrieves a <code>FactModelTuple</code> representing the given <b>pmml</b> file
* @param path
* @param pmmlPath
* @param modelName
* @return
* @throws Exception
*/
FactModelTuple retrieveFactModelTuple(Path path, String pmmlPath, String modelName);

}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ public void ofDMN() {
assertEquals(IMPORT_PREFIX, factModelTree.getImportPrefix());
}

@Test
public void ofPMML() {
FactModelTree factModelTree = FactModelTree.ofPMML(FACT_NAME, IMPORT_PREFIX, Collections.emptyMap(), FACT_TYPE, FactModelTree.Type.INPUT);
assertEquals(FACT_NAME, factModelTree.getFactName());
assertEquals(FACT_TYPE, factModelTree.getTypeName());
assertEquals("", factModelTree.getFullPackage());
assertEquals(FACT_TYPE, factModelTree.getFullTypeName());
assertEquals(FACT_NAME, factModelTree.getFactName());
assertEquals(0, factModelTree.getSimpleProperties().size());
assertEquals(0, factModelTree.getGenericTypesMap().size());
assertEquals(FactModelTree.Type.INPUT, factModelTree.getType());
assertFalse(factModelTree.isSimple());
assertEquals(IMPORT_PREFIX, factModelTree.getImportPrefix());
}

@Test
public void ofSimpleDMN() {
FactModelTree factModelTree = FactModelTree.ofSimpleDMN(FACT_NAME, IMPORT_PREFIX, SIMPLE_PROPERTY_TYPE, Collections.emptyMap(), FACT_TYPE, FactModelTree.Type.INPUT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,25 @@
<artifactId>kie-dmn-core</artifactId>
</dependency>

<!-- PMML -->
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-bom</artifactId>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-api</artifactId>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-evaluator-core</artifactId>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-evaluator-assembler</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
Expand Down Expand Up @@ -214,6 +233,27 @@
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-bom</artifactId>
<type>pom</type>
<version>${version.org.kie}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-api</artifactId>
<version>${version.org.kie}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-evaluator-core</artifactId>
<version>${version.org.kie}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-pmml-evaluator-assembler</artifactId>
<version>${version.org.kie}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-scenario-simulation-backend</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.workbench.screens.scenariosimulation.backend.server;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

import org.drools.scenariosimulation.api.model.FactMapping;
import org.drools.scenariosimulation.api.model.Settings;
import org.drools.scenariosimulation.api.model.Simulation;
import org.drools.workbench.screens.scenariosimulation.model.FactMappingValidationError;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieRuntimeFactory;
import org.kie.pmml.api.enums.DATA_TYPE;
import org.kie.pmml.api.exceptions.KiePMMLInternalException;
import org.kie.pmml.api.models.MiningField;
import org.kie.pmml.api.models.OutputField;
import org.kie.pmml.api.models.PMMLModel;
import org.kie.pmml.api.runtime.PMMLRuntime;

import static org.drools.workbench.screens.scenariosimulation.model.FactMappingValidationError.createFieldChangedError;
import static org.drools.workbench.screens.scenariosimulation.model.FactMappingValidationError.createNodeChangedError;

public class PMMLScenarioValidation extends AbstractScenarioValidation {

public static final PMMLScenarioValidation INSTANCE = new PMMLScenarioValidation();

/**
* Validate structure of a PMML test scenario.
* Supported checks for each column:
* - empty column skip
* - PMML node removed
* - simple type becomes complex type
* - navigation of data type still valid
* - field type changed
* @param simulation
* @param settings
* @param kieContainer
* @return
*/
@Override
public List<FactMappingValidationError> validate(Simulation simulation, Settings settings,
KieContainer kieContainer) {
List<FactMappingValidationError> errors = new ArrayList<>();
String pmmlModelName = settings.getPmmlModelName();
PMMLModel pmmlModel = getPMMLModel(kieContainer, pmmlModelName);

for (FactMapping factMapping : simulation.getScesimModelDescriptor().getFactMappings()) {
if (isToSkip(factMapping)) {
continue;
}

String nodeName = factMapping.getFactIdentifier().getName();

DATA_TYPE fieldType;
try {
fieldType = Stream.of(getMiningFieldDataTypeByName(pmmlModel.getMiningFields(), nodeName),
getOutputFieldDataTypeByName(pmmlModel.getOutputFields(), nodeName))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.orElseThrow(() -> new KiePMMLInternalException(String.format("Failed to find DataType for " +
"field %s",
nodeName)));
} catch (NullPointerException e) {
errors.add(createNodeChangedError(factMapping, "node not found"));
continue;
}
if (!isPMMLFactMappingValid(factMapping, fieldType)) {
errors.add(defineFieldChangedError(factMapping, fieldType, fieldType));
}
}
return errors;
}

private static Optional<DATA_TYPE> getMiningFieldDataTypeByName(List<MiningField> miningFields, String fieldName) {
return miningFields.stream().filter(miningField -> fieldName.equals(miningField.getName()))
.findFirst()
.map(MiningField::getDataType);
}

private static Optional<DATA_TYPE> getOutputFieldDataTypeByName(List<OutputField> outputFields, String fieldName) {
return outputFields.stream().filter(outputField -> fieldName.equals(outputField.getName()))
.findFirst()
.map(OutputField::getDataType);
}

private FactMappingValidationError defineFieldChangedError(FactMapping factMapping, DATA_TYPE factType,
DATA_TYPE fieldType) {
// String typeName = factMapping.getClassName();
// if (isConstraintAdded(typeName, fieldType)) {
// return FactMappingValidationError.createFieldAddedConstraintError(factMapping);
// }
// if (isConstraintRemoved(typeName, factType, fieldType)) {
// return FactMappingValidationError.createFieldRemovedConstraintError(factMapping);
// }
return createFieldChangedError(factMapping, fieldType.getName());
}

private boolean isPMMLFactMappingValid(FactMapping factMapping, DATA_TYPE pmmlType) {
String factMappingType = factMapping.getClassName();
return Objects.equals(factMappingType, pmmlType.getName());
}
//
// /**
// * To define if a constraint (allowed values) were added in a DATA_TYPE fieldType given a typeName, the following
// * conditions
// * are requires:
// * - typeName MUST BE a BuiltInType (eg. STRING, NUMERIC ..)
// * - DATA_TYPE fieldType MUST have at least one defined Allowed Values
// * - DATA_TYPE fieldType MUST have a Base Type. It's name MUST be equals to given typeName.
// * @param typeName TypeName present in the scesim file
// * @param fieldType DATA_TYPE of field under analysis
// * @return
// */
// private boolean isConstraintAdded(String typeName, DATA_TYPE fieldType) {
// boolean isTypeNameBuiltInType = !Objects.equals(UNKNOWN, BuiltInType.determineTypeFromName(typeName));
// boolean hasFieldTypeAllowedValues =
// fieldType.getAllowedValues() != null && !fieldType.getAllowedValues().isEmpty();
// boolean hasFieldTypeBaseType = Objects.nonNull(fieldType.getBaseType());
// if (isTypeNameBuiltInType && hasFieldTypeBaseType && hasFieldTypeAllowedValues) {
// Type baseType = getRootType((BaseDATA_TYPEImpl) fieldType.getBaseType());
// return Objects.equals(typeName, baseType.getName());
// }
// return false;
// }
//
// /**
// * To define if a constraint (allowed values) were removed in a DATA_TYPE fieldType given a typeName, the
// * following conditions
// * are requires:
// * - DATA_TYPE fieldType MUST DON'T have Allowed Values defined
// * - DATA_TYPE fieldType MUST DON'T have a Base Type.
// * - typeName MUST BE a DATA_TYPE factType's field. The field's DATA_TYPE MUST BE the same of given fieldType
// * DATA_TYPE
// * @param typeName
// * @param factType Fact DATA_TYPE
// * @param fieldType Field DATA_TYPE
// * @return
// */
// private boolean isConstraintRemoved(String typeName, DATA_TYPE factType, DATA_TYPE fieldType) {
// boolean hasFieldTypeAllowedValues =
// fieldType.getAllowedValues() != null && !fieldType.getAllowedValues().isEmpty();
// boolean hasFieldTypeBaseType = Objects.nonNull(fieldType.getBaseType());
// boolean isTypeNameFactTypeField = factType.getFields().containsKey(typeName);
// if (!hasFieldTypeBaseType && !hasFieldTypeAllowedValues && isTypeNameFactTypeField) {
// DATA_TYPE typeNameDATA_TYPE = factType.getFields().get(typeName);
// return Objects.equals(fieldType.getNamespace(), typeNameDATA_TYPE.getNamespace());
// }
// return false;
// }

protected PMMLModel getPMMLModel(KieContainer kieContainer, String pmmlModelName) {
PMMLRuntime pmmlRuntime = KieRuntimeFactory.of(kieContainer.getKieBase()).get(PMMLRuntime.class);
return pmmlRuntime.getPMMLModel(pmmlModelName).orElseThrow(() -> new RuntimeException("Unable to find model " + pmmlModelName));
}
}
Loading