package org.molgenis.data.mapper.service.impl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Sets;
import org.mockito.Matchers;
import org.molgenis.auth.User;
import org.molgenis.auth.UserFactory;
import org.molgenis.data.DataService;
import org.molgenis.data.Entity;
import org.molgenis.data.EntityManager;
import org.molgenis.data.mapper.algorithmgenerator.service.AlgorithmGeneratorService;
import org.molgenis.data.mapper.algorithmgenerator.service.impl.AlgorithmGeneratorServiceImpl;
import org.molgenis.data.mapper.mapping.model.AttributeMapping;
import org.molgenis.data.mapper.mapping.model.EntityMapping;
import org.molgenis.data.mapper.mapping.model.MappingProject;
import org.molgenis.data.mapper.service.AlgorithmService;
import org.molgenis.data.mapper.service.UnitResolver;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.AttributeFactory;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.data.meta.model.EntityTypeFactory;
import org.molgenis.data.populate.IdGenerator;
import org.molgenis.data.semantic.Relation;
import org.molgenis.data.semanticsearch.explain.bean.ExplainedAttribute;
import org.molgenis.data.semanticsearch.explain.bean.ExplainedQueryString;
import org.molgenis.data.semanticsearch.repository.TagRepository;
import org.molgenis.data.semanticsearch.service.OntologyTagService;
import org.molgenis.data.semanticsearch.service.SemanticSearchService;
import org.molgenis.data.support.DynamicEntity;
import org.molgenis.js.magma.JsMagmaScriptEvaluator;
import org.molgenis.js.nashorn.NashornScriptEngine;
import org.molgenis.ontology.core.model.OntologyTerm;
import org.molgenis.ontology.core.service.OntologyService;
import org.molgenis.test.data.AbstractMolgenisSpringTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Stream;
import static java.util.Collections.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.molgenis.data.meta.AttributeType.*;
import static org.molgenis.data.meta.model.EntityType.AttributeRole.ROLE_ID;
import static org.molgenis.data.meta.model.EntityType.AttributeRole.ROLE_LABEL;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
@ContextConfiguration(classes = AlgorithmServiceImplTest.Config.class)
public class AlgorithmServiceImplTest extends AbstractMolgenisSpringTest
{
@Autowired
private EntityTypeFactory entityTypeFactory;
@Autowired
private AttributeFactory attrMetaFactory;
@Autowired
private AlgorithmService algorithmService;
@Autowired
private DataService dataService;
@Autowired
private EntityManager entityManager;
@Autowired
private OntologyTagService ontologyTagService;
@Autowired
private SemanticSearchService semanticSearchService;
@Autowired
private AlgorithmTemplateService algorithmTemplateService;
@Autowired
private UserFactory userFactory;
@BeforeMethod
public void setUpBeforeMethod()
{
when(algorithmTemplateService.find(Matchers.any())).thenReturn(Stream.empty());
}
@Test
public void testGetSourceAttributeNames()
{
assertEquals(algorithmService.getSourceAttributeNames("$('id')"), singletonList("id"));
}
@Test
public void testGetSourceAttributeNamesNoQuotes()
{
assertEquals(algorithmService.getSourceAttributeNames("$(id)"), singletonList("id"));
}
@Test
public void testInt() throws ParseException
{
String identifier = "id";
String sourceIntAttribute = "age";
EntityType entityType = entityTypeFactory.create("testInt");
entityType.addAttribute(attrMetaFactory.create().setName(identifier).setDataType(INT), ROLE_ID);
entityType.addAttribute(attrMetaFactory.create().setName(sourceIntAttribute).setDataType(INT));
Entity source = new DynamicEntity(entityType);
source.set(identifier, 1);
source.set(sourceIntAttribute, 25);
String targetIntAttribute = "years_lived";
Attribute targetAttribute = attrMetaFactory.create().setName(targetIntAttribute).setDataType(INT);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm("$('age').value()");
Object result = algorithmService.apply(attributeMapping, source, entityType);
assertEquals(result, 25);
}
@Test
public void testBool() throws ParseException
{
String identifier = "id";
String sourceBoolAttribute = "has_had_coffee";
EntityType entityType = entityTypeFactory.create("testInt");
entityType.addAttribute(attrMetaFactory.create().setName(identifier).setDataType(INT), ROLE_ID);
entityType.addAttribute(attrMetaFactory.create().setName(sourceBoolAttribute).setDataType(BOOL));
Entity source = new DynamicEntity(entityType);
source.set(identifier, 1);
source.set(sourceBoolAttribute, false);
String targetBoolAttribute = "awake";
Attribute targetAttribute = attrMetaFactory.create().setName(targetBoolAttribute).setDataType(BOOL);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm("$('has_had_coffee').value()");
Object result = algorithmService.apply(attributeMapping, source, entityType);
assertEquals(result, false);
}
@Test
public void testLong() throws ParseException
{
String identifier = "id";
String sourceLongAttribute = "serial_number";
EntityType entityType = entityTypeFactory.create("testInt");
entityType.addAttribute(attrMetaFactory.create().setName(identifier).setDataType(INT), ROLE_ID);
entityType.addAttribute(attrMetaFactory.create().setName(sourceLongAttribute).setDataType(LONG));
Entity source = new DynamicEntity(entityType);
source.set(identifier, 1);
source.set(sourceLongAttribute, 529387981723498l);
String targetLongAttribute = "super_id_code";
Attribute targetAttribute = attrMetaFactory.create().setName(targetLongAttribute).setDataType(LONG);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm("$('serial_number').value()");
Object result = algorithmService.apply(attributeMapping, source, entityType);
assertEquals(result, 529387981723498l);
}
@Test
public void testDate() throws ParseException
{
String idAttrName = "id";
EntityType entityType = entityTypeFactory.create("LL");
entityType.addAttribute(attrMetaFactory.create().setName(idAttrName).setDataType(INT), ROLE_ID);
entityType.addAttribute(attrMetaFactory.create().setName("dob").setDataType(DATE));
Entity source = new DynamicEntity(entityType);
source.set(idAttrName, 1);
source.set("dob", new SimpleDateFormat("dd-MM-yyyy").parse("13-05-2015"));
Attribute targetAttribute = attrMetaFactory.create().setName("bob");
targetAttribute.setDataType(DATE);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm("$('dob').value()");
Object result = algorithmService.apply(attributeMapping, source, entityType);
assertEquals(result.toString(), "Wed May 13 00:00:00 CEST 2015");
}
@Test
public void testGetAgeScript() throws ParseException
{
String idAttrName = "id";
EntityType entityType = entityTypeFactory.create("LL");
entityType.addAttribute(attrMetaFactory.create().setName(idAttrName).setDataType(INT), ROLE_ID);
entityType.addAttribute(attrMetaFactory.create().setName("dob").setDataType(DATE));
Entity source = new DynamicEntity(entityType);
source.set(idAttrName, 1);
source.set("dob", new SimpleDateFormat("dd-MM-yyyy").parse("28-08-1973"));
Attribute targetAttribute = attrMetaFactory.create().setName("age");
targetAttribute.setDataType(INT);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm(
"Math.floor((new Date('02/12/2015') - $('dob').value())/(365.2425 * 24 * 60 * 60 * 1000))");
Object result = algorithmService.apply(attributeMapping, source, entityType);
assertEquals(result, 41);
}
@Test
public void testGetXrefScript() throws ParseException
{
// xref entities
EntityType entityTypeXref = entityTypeFactory.create("xrefEntity1");
entityTypeXref.addAttribute(attrMetaFactory.create().setName("id").setDataType(INT), ROLE_ID);
entityTypeXref.addAttribute(attrMetaFactory.create().setName("field1"));
Entity xref1a = new DynamicEntity(entityTypeXref);
xref1a.set("id", 1);
xref1a.set("field1", "Test");
EntityType entityTypeXref2 = entityTypeFactory.create("xrefEntity2");
entityTypeXref2.addAttribute(attrMetaFactory.create().setName("id").setDataType(INT), ROLE_ID);
entityTypeXref2.addAttribute(attrMetaFactory.create().setName("field2"));
Entity xref2a = new DynamicEntity(entityTypeXref2);
xref2a.set("id", 2);
xref2a.set("field2", "Test");
// source Entity
EntityType entityTypeSource = entityTypeFactory.create("Source");
entityTypeSource.addAttribute(attrMetaFactory.create().setName("id").setDataType(INT), ROLE_ID);
entityTypeSource.addAttribute(attrMetaFactory.create().setName("xref").setDataType(XREF));
Entity source = new DynamicEntity(entityTypeSource);
source.set("id", 1);
source.set("xref", xref2a);
Attribute targetAttribute = attrMetaFactory.create().setName("field1");
targetAttribute.setDataType(XREF);
targetAttribute.setRefEntity(entityTypeXref);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm("$('xref').map({'1':'2', '2':'1'}).value();");
when(entityManager.getReference(entityTypeXref, 1)).thenReturn(xref1a);
Entity result = (Entity) algorithmService.apply(attributeMapping, source, entityTypeSource);
assertEquals(result.get("field1"), xref2a.get("field2"));
}
@Test
public void testApplyMref() throws ParseException
{
String refEntityName = "refEntity";
String refEntityIdAttrName = "id";
String refEntityLabelAttrName = "label";
String refEntityId0 = "id0";
String refEntityId1 = "id1";
String sourceEntityName = "source";
String sourceEntityAttrName = "mref-source";
String targetEntityAttrName = "mref-target";
// ref entities
EntityType refEntityType = entityTypeFactory.create(refEntityName);
refEntityType.addAttribute(attrMetaFactory.create().setName(refEntityIdAttrName), ROLE_ID);
refEntityType
.addAttribute(attrMetaFactory.create().setName(refEntityLabelAttrName).setDataType(STRING), ROLE_LABEL);
Entity refEntity0 = new DynamicEntity(refEntityType);
refEntity0.set(refEntityIdAttrName, refEntityId0);
refEntity0.set(refEntityLabelAttrName, "label0");
Entity refEntity1 = new DynamicEntity(refEntityType);
refEntity1.set(refEntityIdAttrName, refEntityId1);
refEntity1.set(refEntityLabelAttrName, "label1");
// mapping
Attribute targetAttribute = attrMetaFactory.create().setName(targetEntityAttrName);
targetAttribute.setDataType(MREF).setNillable(false).setRefEntity(refEntityType);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm("$('" + sourceEntityAttrName + "').value()");
when(entityManager.getReference(refEntityType, refEntityId0)).thenReturn(refEntity0);
when(entityManager.getReference(refEntityType, refEntityId1)).thenReturn(refEntity1);
// source Entity
EntityType entityTypeSource = entityTypeFactory.create(sourceEntityName);
entityTypeSource
.addAttribute(attrMetaFactory.create().setName(refEntityIdAttrName).setDataType(INT).setAuto(true),
ROLE_ID);
entityTypeSource.addAttribute(
attrMetaFactory.create().setName(sourceEntityAttrName).setDataType(MREF).setNillable(false)
.setRefEntity(refEntityType));
Entity source = new DynamicEntity(entityTypeSource);
source.set(sourceEntityAttrName, Arrays.asList(refEntity0, refEntity1));
Object result = algorithmService.apply(attributeMapping, source, entityTypeSource);
assertEquals(result, Arrays.asList(refEntity0, refEntity1));
}
@Test
public void testApplyMrefNillable() throws ParseException
{
String refEntityName = "refEntity";
String refEntityIdAttrName = "id";
String refEntityLabelAttrName = "label";
String sourceEntityName = "source";
String sourceEntityAttrName = "mref-source";
String targetEntityAttrName = "mref-target";
// ref entities
EntityType refEntityType = entityTypeFactory.create(refEntityName);
refEntityType.addAttribute(attrMetaFactory.create().setName(refEntityIdAttrName), ROLE_ID);
refEntityType.addAttribute(attrMetaFactory.create().setName(refEntityLabelAttrName), ROLE_LABEL);
// mapping
Attribute targetAttribute = attrMetaFactory.create().setName(targetEntityAttrName);
targetAttribute.setDataType(MREF).setNillable(true).setRefEntity(refEntityType);
AttributeMapping attributeMapping = new AttributeMapping(targetAttribute);
attributeMapping.setAlgorithm("$('" + sourceEntityAttrName + "').value()");
// source Entity
EntityType entityTypeSource = entityTypeFactory.create(sourceEntityName);
entityTypeSource
.addAttribute(attrMetaFactory.create().setName(refEntityIdAttrName).setDataType(INT).setAuto(true),
ROLE_ID);
entityTypeSource.addAttribute(
attrMetaFactory.create().setName(sourceEntityAttrName).setDataType(MREF).setNillable(true)
.setRefEntity(refEntityType));
Entity source = new DynamicEntity(entityTypeSource);
source.set(sourceEntityAttrName, emptyList());
Object result = algorithmService.apply(attributeMapping, source, entityTypeSource);
assertEquals(result, emptyList());
}
@Test
public void testCreateAttributeMappingIfOnlyOneMatch()
{
EntityType targetEntityType = entityTypeFactory.create("target");
Attribute targetAttribute = attrMetaFactory.create().setName("targetHeight");
targetAttribute.setDescription("height");
targetEntityType.addAttribute(targetAttribute);
EntityType sourceEntityType = entityTypeFactory.create("source");
Attribute sourceAttribute = attrMetaFactory.create().setName("sourceHeight");
sourceAttribute.setDescription("height");
sourceEntityType.addAttribute(sourceAttribute);
User owner = userFactory.create();
owner.setUsername("flup");
owner.setPassword("geheim");
owner.setId("12345");
owner.setActive(true);
owner.setEmail("flup@blah.com");
owner.setFirstName("Flup");
owner.setLastName("de Flap");
MappingProject project = new MappingProject("project", owner);
project.addTarget(targetEntityType);
EntityMapping mapping = project.getMappingTarget("target").addSource(sourceEntityType);
Map<Attribute, ExplainedAttribute> matches = ImmutableMap.of(sourceAttribute, ExplainedAttribute
.create(sourceAttribute, singletonList(ExplainedQueryString.create("height", "height", "height", 100)),
true));
LinkedHashMultimap<Relation, OntologyTerm> ontologyTermTags = LinkedHashMultimap.create();
when(semanticSearchService
.decisionTreeToFindRelevantAttributes(sourceEntityType, targetAttribute, ontologyTermTags.values(),
null)).thenReturn(matches);
when(ontologyTagService.getTagsForAttribute(targetEntityType, targetAttribute)).thenReturn(ontologyTermTags);
algorithmService.autoGenerateAlgorithm(sourceEntityType, targetEntityType, mapping, targetAttribute);
assertEquals(mapping.getAttributeMapping("targetHeight").getAlgorithm(), "$('sourceHeight').value();");
}
@Test
public void testWhenSourceDoesNotMatchThenNoMappingGetsCreated()
{
EntityType targetEntityType = entityTypeFactory.create("target");
Attribute targetAttribute = attrMetaFactory.create().setName("targetHeight");
targetAttribute.setDescription("height");
targetEntityType.addAttribute(targetAttribute);
EntityType sourceEntityType = entityTypeFactory.create("source");
Attribute sourceAttribute = attrMetaFactory.create().setName("sourceHeight");
sourceAttribute.setDescription("weight");
sourceEntityType.addAttribute(sourceAttribute);
User owner = userFactory.create();
owner.setUsername("flup");
owner.setPassword("geheim");
owner.setId("12345");
owner.setActive(true);
owner.setEmail("flup@blah.com");
owner.setFirstName("Flup");
owner.setLastName("de Flap");
MappingProject project = new MappingProject("project", owner);
project.addTarget(targetEntityType);
EntityMapping mapping = project.getMappingTarget("target").addSource(sourceEntityType);
when(semanticSearchService
.findAttributes(sourceEntityType, Sets.newHashSet("targetHeight", "height"), Collections.emptyList()))
.thenReturn(emptyMap());
when(ontologyTagService.getTagsForAttribute(targetEntityType, targetAttribute))
.thenReturn(LinkedHashMultimap.create());
algorithmService.autoGenerateAlgorithm(sourceEntityType, targetEntityType, mapping, targetAttribute);
assertNull(mapping.getAttributeMapping("targetHeight"));
}
@Test
public void testWhenSourceHasMultipleMatchesThenFirstMappingGetsCreated()
{
EntityType targetEntityType = entityTypeFactory.create("target");
Attribute targetAttribute = attrMetaFactory.create().setName("targetHeight");
targetAttribute.setDescription("height");
targetEntityType.addAttribute(targetAttribute);
EntityType sourceEntityType = entityTypeFactory.create("source");
Attribute sourceAttribute1 = attrMetaFactory.create().setName("sourceHeight1");
sourceAttribute1.setDescription("height");
Attribute sourceAttribute2 = attrMetaFactory.create().setName("sourceHeight2");
sourceAttribute2.setDescription("height");
sourceEntityType.addAttributes(Arrays.asList(sourceAttribute1, sourceAttribute2));
User owner = userFactory.create();
owner.setUsername("flup");
owner.setPassword("geheim");
owner.setId("12345");
owner.setActive(true);
owner.setEmail("flup@blah.com");
owner.setFirstName("Flup");
owner.setLastName("de Flap");
MappingProject project = new MappingProject("project", owner);
project.addTarget(targetEntityType);
EntityMapping mapping = project.getMappingTarget("target").addSource(sourceEntityType);
Map<Attribute, ExplainedAttribute> mappings = ImmutableMap
.of(sourceAttribute1, ExplainedAttribute.create(sourceAttribute1), sourceAttribute2,
ExplainedAttribute.create(sourceAttribute2));
LinkedHashMultimap<Relation, OntologyTerm> ontologyTermTags = LinkedHashMultimap.create();
when(semanticSearchService
.decisionTreeToFindRelevantAttributes(sourceEntityType, targetAttribute, ontologyTermTags.values(),
null)).thenReturn(mappings);
when(ontologyTagService.getTagsForAttribute(targetEntityType, targetAttribute)).thenReturn(ontologyTermTags);
algorithmService.autoGenerateAlgorithm(sourceEntityType, targetEntityType, mapping, targetAttribute);
assertEquals(mapping.getAttributeMapping("targetHeight").getSourceAttributes().get(0), sourceAttribute1);
}
@Configuration
@ComponentScan({ "org.molgenis.data.mapper.meta", "org.molgenis.auth" })
public static class Config
{
@Bean
public DataService dataService()
{
return mock(DataService.class);
}
@Bean
public SemanticSearchService semanticSearchService()
{
return mock(SemanticSearchService.class);
}
@Bean
public UnitResolver unitResolver()
{
return new UnitResolverImpl(ontologyService());
}
@Bean
public EntityManager entityManager()
{
return mock(EntityManager.class);
}
@Bean
public JsMagmaScriptEvaluator jsScriptEvaluator()
{
return new JsMagmaScriptEvaluator(new NashornScriptEngine());
}
@Bean
public AlgorithmService algorithmService()
{
return new AlgorithmServiceImpl(ontologyTagService(), semanticSearchService(), algorithmGeneratorService(),
entityManager(), jsScriptEvaluator());
}
@Bean
public AlgorithmTemplateService algorithmTemplateService()
{
return mock(AlgorithmTemplateServiceImpl.class);
}
@Bean
public OntologyService ontologyService()
{
return mock(OntologyService.class);
}
@Bean
public TagRepository tagRepository()
{
return mock(TagRepository.class);
}
@Bean
IdGenerator idGenerator()
{
return mock(IdGenerator.class);
}
@Bean
public OntologyTagService ontologyTagService()
{
return mock(OntologyTagService.class);
}
@Bean
public AlgorithmGeneratorService algorithmGeneratorService()
{
return new AlgorithmGeneratorServiceImpl(dataService(), unitResolver(), algorithmTemplateService());
}
}
}