package org.molgenis.dataexplorer.controller;
import com.google.common.collect.Lists;
import org.molgenis.data.DataService;
import org.molgenis.data.Entity;
import org.molgenis.data.Repository;
import org.molgenis.data.annotation.core.RepositoryAnnotator;
import org.molgenis.data.annotation.web.AnnotationService;
import org.molgenis.data.annotation.web.meta.AnnotationJobExecution;
import org.molgenis.data.annotation.web.meta.AnnotationJobExecutionFactory;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.security.core.MolgenisPermissionService;
import org.molgenis.security.core.Permission;
import org.molgenis.security.permission.PermissionSystemService;
import org.molgenis.security.user.UserAccountService;
import org.molgenis.util.ErrorMessageResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import static org.molgenis.data.meta.AttributeType.COMPOUND;
import static org.molgenis.data.meta.model.Package.PACKAGE_SEPARATOR;
import static org.molgenis.data.settings.SettingsPackage.PACKAGE_SETTINGS;
import static org.molgenis.dataexplorer.controller.AnnotatorController.URI;
@Controller
@RequestMapping(URI)
public class AnnotatorController
{
private static final Logger LOG = LoggerFactory.getLogger(AnnotatorController.class);
public static final String URI = "/annotators";
private final DataService dataService;
private final AnnotationService annotationService;
private final MolgenisPermissionService molgenisPermissionService;
private final UserAccountService userAccountService;
private final AnnotationJobFactory annotationJobFactory;
private final ExecutorService taskExecutor;
private final AnnotationJobExecutionFactory annotationJobExecutionFactory;
@Autowired
public AnnotatorController(DataService dataService, AnnotationService annotationService,
MolgenisPermissionService molgenisPermissionService, PermissionSystemService permissionSystemService,
UserAccountService userAccountService, AnnotationJobFactory annotationJobFactory,
ExecutorService taskExecutor, AnnotationJobExecutionFactory annotationJobExecutionFactory)
{
this.dataService = dataService;
this.annotationService = annotationService;
this.molgenisPermissionService = molgenisPermissionService;
this.userAccountService = userAccountService;
this.annotationJobFactory = annotationJobFactory;
this.taskExecutor = taskExecutor;
this.annotationJobExecutionFactory = annotationJobExecutionFactory;
}
/**
* Gets a map of all available annotators.
*
* @param dataSetName
* @return annotatorMap
*/
@RequestMapping(value = "/get-available-annotators", method = RequestMethod.POST)
@ResponseBody
public Map<String, Map<String, Object>> getMapOfAvailableAnnotators(@RequestBody String dataSetName)
{
Map<String, Map<String, Object>> annotatorMap = setMapOfAnnotators(dataSetName);
return annotatorMap;
}
/**
* Annotates an entity based on selected entity and selected annotators. Creates a copy of the entity dataset if
* option is ticked by the user.
*
* @param annotatorNames
* @param entityName
* @return repositoryName
*/
@RequestMapping(value = "/annotate-data", method = RequestMethod.POST)
@ResponseBody
public String annotateData(HttpServletRequest request,
@RequestParam(value = "annotatorNames", required = false) String[] annotatorNames,
@RequestParam("dataset-identifier") String entityName)
{
Repository<Entity> repository = dataService.getRepository(entityName);
if (annotatorNames != null && repository != null)
{
scheduleAnnotatorRun(repository.getEntityType().getName(), annotatorNames);
}
return entityName;
}
public String scheduleAnnotatorRun(String entityName, String[] annotatorNames)
{
AnnotationJobExecution annotationJobExecution = annotationJobExecutionFactory.create();
annotationJobExecution.setUser(userAccountService.getCurrentUser());
annotationJobExecution.setTargetName(entityName);
annotationJobExecution.setAnnotators(String.join(",", (CharSequence[]) annotatorNames));
annotationJobExecution.setResultUrl("/menu/main/dataexplorer?entity=" + entityName);
AnnotationJob job = annotationJobFactory.createJob(annotationJobExecution);
taskExecutor.submit(job);
return annotationJobExecution.getIdentifier();
}
/**
* Sets a map of annotators, whether they can be used by the selected data set.
*
* @param dataSetName
* @return mapOfAnnotators
*/
private Map<String, Map<String, Object>> setMapOfAnnotators(String dataSetName)
{
Map<String, Map<String, Object>> mapOfAnnotators = new HashMap<String, Map<String, Object>>();
if (dataSetName != null)
{
EntityType entityType = dataService.getEntityType(dataSetName);
for (RepositoryAnnotator annotator : annotationService.getAllAnnotators())
{
List<Attribute> outputAttrs = annotator.getOutputAttributes();
outputAttrs = getAtomicAttributesFromList(outputAttrs);
Map<String, Object> map = new HashMap<String, Object>();
map.put("description", annotator.getDescription());
map.put("canAnnotate", annotator.canAnnotate(entityType));
map.put("inputAttributes", createAttrsResponse(annotator.getRequiredAttributes()));
map.put("inputAttributeTypes", toMap(annotator.getRequiredAttributes()));
map.put("outputAttributes", createAttrsResponse(outputAttrs));
map.put("outputAttributeTypes", toMap(annotator.getOutputAttributes()));
String settingsEntityName = PACKAGE_SETTINGS + PACKAGE_SEPARATOR + annotator.getInfo().getCode();
map.put("showSettingsButton",
molgenisPermissionService.hasPermissionOnEntity(settingsEntityName, Permission.WRITE));
mapOfAnnotators.put(annotator.getSimpleName(), map);
}
}
return mapOfAnnotators;
}
private List<Map<String, Object>> createAttrsResponse(List<Attribute> inputMetaData)
{
return inputMetaData.stream().map(attr ->
{
Map<String, Object> attrMap = new HashMap<String, Object>();
attrMap.put("name", attr.getName());
attrMap.put("description", attr.getDescription());
return attrMap;
}).collect(Collectors.toList());
}
private List<Attribute> getAtomicAttributesFromList(List<Attribute> outputAttrs)
{
if (outputAttrs.size() == 1 && outputAttrs.get(0).getDataType() == COMPOUND)
{
return getAtomicAttributesFromList(Lists.newArrayList(outputAttrs.get(0).getChildren()));
}
else
{
return outputAttrs;
}
}
private Map<String, String> toMap(Iterable<Attribute> attrs)
{
Map<String, String> result = new HashMap<>();
for (Attribute attr : attrs)
{
result.put(attr.getName(), attr.getDataType().toString());
}
return result;
}
@ExceptionHandler(RuntimeException.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorMessageResponse handleRuntimeException(RuntimeException e)
{
LOG.error(e.getMessage(), e);
return new ErrorMessageResponse(new ErrorMessageResponse.ErrorMessage(
"An error occurred. Please contact the administrator.<br />Message:" + e.getMessage()));
}
}