/*
* Copyright 2015-2016 OpenCB
*
* 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.opencb.opencga.catalog.managers;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
import org.opencb.commons.datastore.core.ObjectMap;
import org.opencb.commons.datastore.core.Query;
import org.opencb.commons.datastore.core.QueryOptions;
import org.opencb.commons.datastore.core.QueryResult;
import org.opencb.opencga.catalog.audit.AuditManager;
import org.opencb.opencga.catalog.audit.AuditRecord;
import org.opencb.opencga.catalog.auth.authorization.AuthorizationManager;
import org.opencb.opencga.catalog.config.Configuration;
import org.opencb.opencga.catalog.db.DBAdaptorFactory;
import org.opencb.opencga.catalog.db.api.IndividualDBAdaptor;
import org.opencb.opencga.catalog.db.api.SampleDBAdaptor;
import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException;
import org.opencb.opencga.catalog.exceptions.CatalogDBException;
import org.opencb.opencga.catalog.exceptions.CatalogException;
import org.opencb.opencga.catalog.io.CatalogIOManagerFactory;
import org.opencb.opencga.catalog.managers.api.IIndividualManager;
import org.opencb.opencga.catalog.managers.api.IUserManager;
import org.opencb.opencga.catalog.models.*;
import org.opencb.opencga.catalog.models.acls.permissions.IndividualAclEntry;
import org.opencb.opencga.catalog.models.acls.permissions.StudyAclEntry;
import org.opencb.opencga.catalog.utils.AnnotationManager;
import org.opencb.opencga.catalog.utils.CatalogAnnotationsValidator;
import org.opencb.opencga.catalog.utils.ParamUtils;
import org.opencb.opencga.core.common.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created by hpccoll1 on 19/06/15.
*/
public class IndividualManager extends AbstractManager implements IIndividualManager {
protected static Logger logger = LoggerFactory.getLogger(IndividualManager.class);
private IUserManager userManager;
@Deprecated
public IndividualManager(AuthorizationManager authorizationManager, AuditManager auditManager,
DBAdaptorFactory catalogDBAdaptorFactory, CatalogIOManagerFactory ioManagerFactory,
Properties catalogProperties) {
super(authorizationManager, auditManager, catalogDBAdaptorFactory, ioManagerFactory, catalogProperties);
}
public IndividualManager(AuthorizationManager authorizationManager, AuditManager auditManager, CatalogManager catalogManager,
DBAdaptorFactory catalogDBAdaptorFactory, CatalogIOManagerFactory ioManagerFactory,
Configuration configuration) {
super(authorizationManager, auditManager, catalogManager, catalogDBAdaptorFactory, ioManagerFactory,
configuration);
this.userManager = catalogManager.getUserManager();
}
@Deprecated
@Override
public QueryResult<Individual> create(long studyId, String name, String family, long fatherId, long motherId, Individual.Sex sex,
String ethnicity, String speciesCommonName, String speciesScientificName,
String speciesTaxonomyCode, String populationName, String populationSubpopulation,
String populationDescription, Individual.KaryotypicSex karyotypicSex,
Individual.LifeStatus lifeStatus, Individual.AffectationStatus affectationStatus,
QueryOptions options, String sessionId) throws CatalogException {
return create(studyId, name, family, fatherId, motherId, sex, ethnicity, speciesCommonName, speciesScientificName,
speciesTaxonomyCode, populationName, populationSubpopulation, populationDescription, karyotypicSex, lifeStatus,
affectationStatus, null, options, sessionId);
}
@Override
public QueryResult<Individual> get(Long individualId, QueryOptions options, String sessionId)
throws CatalogException {
ParamUtils.checkObj(individualId, "individualId");
ParamUtils.checkObj(sessionId, "sessionId");
options = ParamUtils.defaultObject(options, QueryOptions::new);
String userId = userManager.getId(sessionId);
authorizationManager.checkIndividualPermission(individualId, userId, IndividualAclEntry.IndividualPermissions.VIEW);
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(individualId, options);
long studyId = individualDBAdaptor.getStudyId(individualId);
authorizationManager.filterIndividuals(userId, studyId, individualQueryResult.getResult());
individualQueryResult.setNumResults(individualQueryResult.getResult().size());
return individualQueryResult;
}
@Override
public QueryResult<Individual> get(Query query, QueryOptions options, String sessionId) throws CatalogException {
query = ParamUtils.defaultObject(query, Query::new);
return get(query.getInt("studyId"), query, options, sessionId);
}
@Override
public QueryResult<Individual> get(long studyId, Query query, QueryOptions options, String sessionId)
throws CatalogException {
ParamUtils.checkObj(sessionId, "sessionId");
query = ParamUtils.defaultObject(query, Query::new);
options = ParamUtils.defaultObject(options, QueryOptions::new);
String userId = userManager.getId(sessionId);
if (!authorizationManager.memberHasPermissionsInStudy(studyId, userId)) {
throw CatalogAuthorizationException.deny(userId, "view", "individual", studyId, null);
}
query.append(IndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId);
QueryResult<Individual> queryResult = individualDBAdaptor.get(query, options);
authorizationManager.filterIndividuals(userId, studyId, queryResult.getResult());
queryResult.setNumResults(queryResult.getResult().size());
return queryResult;
}
@Override
public List<QueryResult<Individual>> restore(String individualIdStr, QueryOptions options, String sessionId) throws CatalogException {
ParamUtils.checkParameter(individualIdStr, "id");
options = ParamUtils.defaultObject(options, QueryOptions::new);
String userId = userManager.getId(sessionId);
List<Long> individualIds = getIds(userId, individualIdStr);
List<QueryResult<Individual>> queryResultList = new ArrayList<>(individualIds.size());
for (Long individualId : individualIds) {
QueryResult<Individual> queryResult = null;
try {
authorizationManager.checkIndividualPermission(individualId, userId, IndividualAclEntry.IndividualPermissions.DELETE);
queryResult = individualDBAdaptor.restore(individualId, options);
auditManager.recordAction(AuditRecord.Resource.individual, AuditRecord.Action.restore, AuditRecord.Magnitude.medium,
individualId, userId, Status.DELETED, Status.READY, "Individual restore", new ObjectMap());
} catch (CatalogAuthorizationException e) {
auditManager.recordAction(AuditRecord.Resource.individual, AuditRecord.Action.restore, AuditRecord.Magnitude.high,
individualId, userId, null, null, e.getMessage(), null);
queryResult = new QueryResult<>("Restore individual " + individualId);
queryResult.setErrorMsg(e.getMessage());
} catch (CatalogException e) {
e.printStackTrace();
queryResult = new QueryResult<>("Restore individual " + individualId);
queryResult.setErrorMsg(e.getMessage());
} finally {
queryResultList.add(queryResult);
}
}
return queryResultList;
}
@Override
public List<QueryResult<Individual>> restore(Query query, QueryOptions options, String sessionId) throws CatalogException {
QueryOptions queryOptions = new QueryOptions(QueryOptions.INCLUDE, IndividualDBAdaptor.QueryParams.ID.key());
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(query, queryOptions);
List<Long> individualIds = individualQueryResult.getResult().stream().map(Individual::getId).collect(Collectors.toList());
String individualStr = StringUtils.join(individualIds, ",");
return restore(individualStr, options, sessionId);
}
@Override
public void setStatus(String id, String status, String message, String sessionId) throws CatalogException {
throw new NotImplementedException("Project: Operation not yet supported");
}
@Override
public QueryResult<IndividualAclEntry> getAcls(String individualStr, List<String> members, String sessionId)
throws CatalogException {
long startTime = System.currentTimeMillis();
String userId = userManager.getId(sessionId);
Long individualId = getId(userId, individualStr);
authorizationManager.checkIndividualPermission(individualId, userId, IndividualAclEntry.IndividualPermissions.SHARE);
Long studyId = getStudyId(individualId);
// Split and obtain the set of members (users + groups), users and groups
Set<String> memberSet = new HashSet<>();
Set<String> userIds = new HashSet<>();
Set<String> groupIds = new HashSet<>();
for (String member: members) {
memberSet.add(member);
if (!member.startsWith("@")) {
userIds.add(member);
} else {
groupIds.add(member);
}
}
// Obtain the groups the user might belong to in order to be able to get the permissions properly
// (the permissions might be given to the group instead of the user)
// Map of group -> users
Map<String, List<String>> groupUsers = new HashMap<>();
if (userIds.size() > 0) {
List<String> tmpUserIds = userIds.stream().collect(Collectors.toList());
QueryResult<Group> groups = studyDBAdaptor.getGroup(studyId, null, tmpUserIds);
// We add the groups where the users might belong to to the memberSet
if (groups.getNumResults() > 0) {
for (Group group : groups.getResult()) {
for (String tmpUserId : group.getUserIds()) {
if (userIds.contains(tmpUserId)) {
memberSet.add(group.getName());
if (!groupUsers.containsKey(group.getName())) {
groupUsers.put(group.getName(), new ArrayList<>());
}
groupUsers.get(group.getName()).add(tmpUserId);
}
}
}
}
}
List<String> memberList = memberSet.stream().collect(Collectors.toList());
QueryResult<IndividualAclEntry> individualAclQueryResult = individualDBAdaptor.getAcl(individualId, memberList);
if (members.size() == 0) {
return individualAclQueryResult;
}
// For the cases where the permissions were given at group level, we obtain the user and return it as if they were given to the user
// instead of the group.
// We loop over the results and recreate one individualAcl per member
Map<String, IndividualAclEntry> individualAclHashMap = new HashMap<>();
for (IndividualAclEntry individualAcl : individualAclQueryResult.getResult()) {
if (memberList.contains(individualAcl.getMember())) {
if (individualAcl.getMember().startsWith("@")) {
// Check if the user was demanding the group directly or a user belonging to the group
if (groupIds.contains(individualAcl.getMember())) {
individualAclHashMap.put(individualAcl.getMember(),
new IndividualAclEntry(individualAcl.getMember(), individualAcl.getPermissions()));
} else {
// Obtain the user(s) belonging to that group whose permissions wanted the userId
if (groupUsers.containsKey(individualAcl.getMember())) {
for (String tmpUserId : groupUsers.get(individualAcl.getMember())) {
if (userIds.contains(tmpUserId)) {
individualAclHashMap.put(tmpUserId, new IndividualAclEntry(tmpUserId, individualAcl.getPermissions()));
}
}
}
}
} else {
// Add the user
individualAclHashMap.put(individualAcl.getMember(), new IndividualAclEntry(individualAcl.getMember(),
individualAcl.getPermissions()));
}
}
}
// We recreate the output that is in fileAclHashMap but in the same order the members were queried.
List<IndividualAclEntry> individualAclList = new ArrayList<>(individualAclHashMap.size());
for (String member : members) {
if (individualAclHashMap.containsKey(member)) {
individualAclList.add(individualAclHashMap.get(member));
}
}
// Update queryResult info
individualAclQueryResult.setId(individualStr);
individualAclQueryResult.setNumResults(individualAclList.size());
individualAclQueryResult.setDbTime((int) (System.currentTimeMillis() - startTime));
individualAclQueryResult.setResult(individualAclList);
return individualAclQueryResult;
}
@Override
public Long getStudyId(long individualId) throws CatalogException {
return individualDBAdaptor.getStudyId(individualId);
}
@Override
public Long getId(String userId, String individualStr) throws CatalogException {
if (StringUtils.isNumeric(individualStr)) {
return Long.parseLong(individualStr);
}
// Resolve the studyIds and filter the individualName
ObjectMap parsedSampleStr = parseFeatureId(userId, individualStr);
List<Long> studyIds = getStudyIds(parsedSampleStr);
String individualName = parsedSampleStr.getString("featureName");
Query query = new Query(IndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyIds)
.append(IndividualDBAdaptor.QueryParams.NAME.key(), individualName);
QueryOptions qOptions = new QueryOptions(QueryOptions.INCLUDE, "projects.studies.individuals.id");
QueryResult<Individual> queryResult = individualDBAdaptor.get(query, qOptions);
if (queryResult.getNumResults() > 1) {
throw new CatalogException("Error: More than one individual id found based on " + individualName);
} else if (queryResult.getNumResults() == 0) {
return -1L;
} else {
return queryResult.first().getId();
}
}
@Deprecated
@Override
public Long getId(String id) throws CatalogDBException {
if (StringUtils.isNumeric(id)) {
return Long.parseLong(id);
}
Query query = new Query(IndividualDBAdaptor.QueryParams.NAME.key(), id);
QueryOptions options = new QueryOptions(QueryOptions.INCLUDE, IndividualDBAdaptor.QueryParams.ID.key());
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(query, options);
if (individualQueryResult.getNumResults() == 1) {
return individualQueryResult.first().getId();
} else {
return -1L;
}
}
@Override
public MyResourceId getId(String individualStr, @Nullable String studyStr, String sessionId) throws CatalogException {
if (StringUtils.isEmpty(individualStr)) {
throw new CatalogException("Missing individual parameter");
}
String userId;
long studyId;
long individualId;
if (StringUtils.isNumeric(individualStr) && Long.parseLong(individualStr) > configuration.getCatalog().getOffset()) {
individualId = Long.parseLong(individualStr);
individualDBAdaptor.exists(individualId);
studyId = individualDBAdaptor.getStudyId(individualId);
userId = userManager.getId(sessionId);
} else {
if (individualStr.contains(",")) {
throw new CatalogException("More than one individual found");
}
userId = userManager.getId(sessionId);
studyId = catalogManager.getStudyManager().getId(userId, studyStr);
Query query = new Query()
.append(IndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId)
.append(IndividualDBAdaptor.QueryParams.NAME.key(), individualStr);
QueryOptions queryOptions = new QueryOptions(QueryOptions.INCLUDE, IndividualDBAdaptor.QueryParams.ID.key());
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(query, queryOptions);
if (individualQueryResult.getNumResults() == 1) {
individualId = individualQueryResult.first().getId();
} else {
if (individualQueryResult.getNumResults() == 0) {
throw new CatalogException("Individual " + individualStr + " not found in study " + studyStr);
} else {
throw new CatalogException("More than one individual found under " + individualStr + " in study " + studyStr);
}
}
}
return new MyResourceId(userId, studyId, individualId);
}
@Override
public MyResourceIds getIds(String individualStr, @Nullable String studyStr, String sessionId) throws CatalogException {
if (StringUtils.isEmpty(individualStr)) {
throw new CatalogException("Missing individual parameter");
}
String userId;
long studyId;
List<Long> individualIds;
if (StringUtils.isNumeric(individualStr) && Long.parseLong(individualStr) > configuration.getCatalog().getOffset()) {
individualIds = Arrays.asList(Long.parseLong(individualStr));
individualDBAdaptor.exists(individualIds.get(0));
studyId = individualDBAdaptor.getStudyId(individualIds.get(0));
userId = userManager.getId(sessionId);
} else {
userId = userManager.getId(sessionId);
studyId = catalogManager.getStudyManager().getId(userId, studyStr);
List<String> individualSplit = Arrays.asList(individualStr.split(","));
Query query = new Query()
.append(IndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId)
.append(IndividualDBAdaptor.QueryParams.NAME.key(), individualSplit);
QueryOptions queryOptions = new QueryOptions(QueryOptions.INCLUDE, IndividualDBAdaptor.QueryParams.ID.key());
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(query, queryOptions);
if (individualQueryResult.getNumResults() == individualSplit.size()) {
individualIds = individualQueryResult.getResult().stream().map(Individual::getId).collect(Collectors.toList());
} else {
throw new CatalogException("Found only " + individualQueryResult.getNumResults() + " out of the " + individualSplit.size()
+ " individuals looked for in study " + studyStr);
}
}
return new MyResourceIds(userId, studyId, individualIds);
}
@Override
public QueryResult<Individual> search(String studyStr, Query query, QueryOptions options, String sessionId) throws CatalogException {
String userId = userManager.getId(sessionId);
List<Long> studyIds = catalogManager.getStudyManager().getIds(userId, studyStr);
// Check any permission in studies
for (Long studyId : studyIds) {
authorizationManager.memberHasPermissionsInStudy(studyId, userId);
}
QueryResult<Individual> queryResult = null;
for (Long studyId : studyIds) {
query.append(IndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId);
QueryResult<Individual> queryResultAux = individualDBAdaptor.get(query, options);
authorizationManager.filterIndividuals(userId, studyId, queryResultAux.getResult());
if (queryResult == null) {
queryResult = queryResultAux;
} else {
queryResult.getResult().addAll(queryResultAux.getResult());
queryResult.setNumTotalResults(queryResult.getNumTotalResults() + queryResultAux.getNumTotalResults());
queryResult.setDbTime(queryResult.getDbTime() + queryResultAux.getDbTime());
}
}
queryResult.setNumResults(queryResult.getResult().size());
return queryResult;
}
@Override
public QueryResult<Individual> create(long studyId, String name, String family, long fatherId, long motherId, Individual.Sex sex,
String ethnicity, String speciesCommonName, String speciesScientificName,
String speciesTaxonomyCode, String populationName, String populationSubpopulation,
String populationDescription, Individual.KaryotypicSex karyotypicSex,
Individual.LifeStatus lifeStatus, Individual.AffectationStatus affectationStatus,
String dateOfBirth, QueryOptions options, String sessionId) throws CatalogException {
options = ParamUtils.defaultObject(options, QueryOptions::new);
sex = ParamUtils.defaultObject(sex, Individual.Sex.UNKNOWN);
logger.info(Long.toString(configuration.getCatalog().getOffset()));
ParamUtils.checkAlias(name, "name", configuration.getCatalog().getOffset());
family = ParamUtils.defaultObject(family, "");
ethnicity = ParamUtils.defaultObject(ethnicity, "");
speciesCommonName = ParamUtils.defaultObject(speciesCommonName, "");
speciesScientificName = ParamUtils.defaultObject(speciesScientificName, "");
speciesTaxonomyCode = ParamUtils.defaultObject(speciesTaxonomyCode, "");
populationName = ParamUtils.defaultObject(populationName, "");
populationSubpopulation = ParamUtils.defaultObject(populationSubpopulation, "");
populationDescription = ParamUtils.defaultObject(populationDescription, "");
karyotypicSex = ParamUtils.defaultObject(karyotypicSex, Individual.KaryotypicSex.UNKNOWN);
lifeStatus = ParamUtils.defaultObject(lifeStatus, Individual.LifeStatus.UNKNOWN);
affectationStatus = ParamUtils.defaultObject(affectationStatus, Individual.AffectationStatus.UNKNOWN);
if (StringUtils.isEmpty(dateOfBirth)) {
dateOfBirth = "";
} else {
if (!TimeUtils.isValidFormat("yyyyMMdd", dateOfBirth)) {
throw new CatalogException("Invalid date of birth format. Valid format yyyyMMdd");
}
}
String userId = userManager.getId(sessionId);
authorizationManager.checkStudyPermission(studyId, userId, StudyAclEntry.StudyPermissions.WRITE_INDIVIDUALS);
Individual individual = new Individual(0, name, fatherId, motherId,
family, sex, karyotypicSex, ethnicity, new Individual.Species(speciesCommonName, speciesScientificName,
speciesTaxonomyCode), new Individual.Population(populationName, populationSubpopulation, populationDescription),
lifeStatus, affectationStatus, dateOfBirth);
QueryResult<Individual> queryResult = individualDBAdaptor.insert(individual, studyId, options);
// auditManager.recordCreation(AuditRecord.Resource.individual, queryResult.first().getId(), userId, queryResult.first(), null, null);
auditManager.recordAction(AuditRecord.Resource.individual, AuditRecord.Action.create, AuditRecord.Magnitude.low,
queryResult.first().getId(), userId, null, queryResult.first(), null, null);
return queryResult;
}
@Override
@Deprecated
public QueryResult<AnnotationSet> annotate(long individualId, String annotationSetName, long variableSetId, Map<String, Object>
annotations, Map<String, Object> attributes, String sessionId) throws CatalogException {
ParamUtils.checkParameter(annotationSetName, "annotationSetName");
ParamUtils.checkObj(annotations, "annotations");
attributes = ParamUtils.defaultObject(attributes, HashMap<String, Object>::new);
String userId = userManager.getId(sessionId);
authorizationManager.checkIndividualPermission(individualId, userId, IndividualAclEntry.IndividualPermissions.WRITE_ANNOTATIONS);
VariableSet variableSet = studyDBAdaptor.getVariableSet(variableSetId, null).first();
AnnotationSet annotationSet =
new AnnotationSet(annotationSetName, variableSetId, new HashSet<>(), TimeUtils.getTime(), attributes);
for (Map.Entry<String, Object> entry : annotations.entrySet()) {
annotationSet.getAnnotations().add(new Annotation(entry.getKey(), entry.getValue()));
}
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(individualId,
new QueryOptions("include", Collections.singletonList("projects.studies.individuals.annotationSets")));
List<AnnotationSet> annotationSets = individualQueryResult.getResult().get(0).getAnnotationSets();
// if (checkAnnotationSet) {
CatalogAnnotationsValidator.checkAnnotationSet(variableSet, annotationSet, annotationSets);
// }
QueryResult<AnnotationSet> queryResult = individualDBAdaptor.annotate(individualId, annotationSet, false);
auditManager.recordUpdate(AuditRecord.Resource.individual, individualId, userId, new ObjectMap("annotationSets", queryResult
.first()), "annotate", null);
return queryResult;
}
@Deprecated
public QueryResult<AnnotationSet> updateAnnotation(long individualId, String annotationSetName, Map<String, Object> newAnnotations,
String sessionId) throws CatalogException {
ParamUtils.checkParameter(annotationSetName, "annotationSetName");
ParamUtils.checkObj(newAnnotations, "newAnnotations");
String userId = userManager.getId(sessionId);
authorizationManager.checkIndividualPermission(individualId, userId, IndividualAclEntry.IndividualPermissions.WRITE_ANNOTATIONS);
QueryOptions queryOptions = new QueryOptions("include", "projects.studies.individuals.annotationSets");
// Get individual
Individual individual = individualDBAdaptor.get(individualId, queryOptions).first();
// Get annotation set
AnnotationSet annotationSet = null;
for (AnnotationSet annotationSetAux : individual.getAnnotationSets()) {
if (annotationSetAux.getName().equals(annotationSetName)) {
annotationSet = annotationSetAux;
individual.getAnnotationSets().remove(annotationSet);
break;
}
}
if (annotationSet == null) {
throw CatalogDBException.idNotFound("AnnotationSet", annotationSetName);
}
// Get variable set
VariableSet variableSet = studyDBAdaptor.getVariableSet(annotationSet.getVariableSetId(), null).first();
// Update and validate annotations
CatalogAnnotationsValidator.mergeNewAnnotations(annotationSet, newAnnotations);
CatalogAnnotationsValidator.checkAnnotationSet(variableSet, annotationSet, individual.getAnnotationSets());
// Commit changes
QueryResult<AnnotationSet> queryResult = individualDBAdaptor.annotate(individualId, annotationSet, true);
AnnotationSet annotationSetUpdate = new AnnotationSet(annotationSet.getName(), annotationSet.getVariableSetId(),
newAnnotations.entrySet().stream().map(entry -> new Annotation(entry.getKey(), entry.getValue())).collect(Collectors
.toSet()),
annotationSet.getCreationDate(), null);
auditManager.recordUpdate(AuditRecord.Resource.individual, individualId, userId, new ObjectMap("annotationSets",
Collections.singletonList(annotationSetUpdate)), "update annotation", null);
return queryResult;
}
@Override
public QueryResult<Individual> update(Long individualId, ObjectMap parameters, QueryOptions options, String sessionId)
throws CatalogException {
ParamUtils.defaultObject(parameters, QueryOptions::new);
ParamUtils.defaultObject(options, QueryOptions::new);
String userId = userManager.getId(sessionId);
authorizationManager.checkIndividualPermission(individualId, userId, IndividualAclEntry.IndividualPermissions.UPDATE);
for (Map.Entry<String, Object> param : parameters.entrySet()) {
IndividualDBAdaptor.QueryParams queryParam = IndividualDBAdaptor.QueryParams.getParam(param.getKey());
switch (queryParam) {
case NAME:
ParamUtils.checkAlias(parameters.getString(queryParam.key()), "name", configuration.getCatalog().getOffset());
String myName = parameters.getString(IndividualDBAdaptor.QueryParams.NAME.key());
long studyId = individualDBAdaptor.getStudyId(individualId);
Query query = new Query()
.append(IndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId)
.append(IndividualDBAdaptor.QueryParams.NAME.key(), myName);
if (individualDBAdaptor.count(query).first() > 0) {
throw new CatalogException("Individual name " + myName + " already in use");
}
break;
case DATE_OF_BIRTH:
if (StringUtils.isEmpty((String) param.getValue())) {
parameters.put(param.getKey(), "");
} else {
if (!TimeUtils.isValidFormat("yyyyMMdd", (String) param.getValue())) {
throw new CatalogException("Invalid date of birth format. Valid format yyyyMMdd");
}
}
break;
case FATHER_ID:
case MOTHER_ID:
case FAMILY:
case SEX:
case ETHNICITY:
case SPECIES_COMMON_NAME:
case SPECIES_SCIENTIFIC_NAME:
case SPECIES_TAXONOMY_CODE:
case POPULATION_DESCRIPTION:
case POPULATION_NAME:
case POPULATION_SUBPOPULATION:
case KARYOTYPIC_SEX:
case LIFE_STATUS:
case AFFECTATION_STATUS:
break;
default:
throw new CatalogException("Cannot update " + queryParam);
}
}
options.putAll(parameters); //FIXME: Use separated params and options, or merge
QueryResult<Individual> queryResult = individualDBAdaptor.update(individualId, new ObjectMap(options));
auditManager.recordUpdate(AuditRecord.Resource.individual, individualId, userId, parameters, null, null);
return queryResult;
}
@Override
public List<QueryResult<Individual>> delete(String individualIdStr, @Nullable String studyStr, QueryOptions options, String sessionId)
throws CatalogException, IOException {
ParamUtils.checkParameter(individualIdStr, "id");
options = ParamUtils.defaultObject(options, QueryOptions::new);
MyResourceIds resourceId = getIds(individualIdStr, studyStr, sessionId);
List<Long> individualIds = resourceId.getResourceIds();
String userId = resourceId.getUser();
List<QueryResult<Individual>> queryResultList = new ArrayList<>(individualIds.size());
for (Long individualId : individualIds) {
QueryResult<Individual> queryResult = null;
try {
authorizationManager.checkIndividualPermission(individualId, userId, IndividualAclEntry.IndividualPermissions.DELETE);
// We can delete an individual if their samples can be deleted
// We obtain the samples associated to the individual and check if those can be deleted
Query query = new Query()
.append(SampleDBAdaptor.QueryParams.STUDY_ID.key(), resourceId.getStudyId())
.append(SampleDBAdaptor.QueryParams.INDIVIDUAL_ID.key(), individualId);
QueryOptions queryOptions = new QueryOptions(QueryOptions.INCLUDE, SampleDBAdaptor.QueryParams.ID.key());
QueryResult<Sample> sampleQueryResult = sampleDBAdaptor.get(query, queryOptions);
if (sampleQueryResult.getNumResults() > 0) {
List<Long> sampleIds = sampleQueryResult.getResult().stream().map(Sample::getId).collect(Collectors.toList());
MyResourceIds sampleResource = new MyResourceIds(resourceId.getUser(), resourceId.getStudyId(), sampleIds);
// FIXME:
// We are first checking and deleting later because that delete method does not check if all the samples can be deleted
// directly. Instead, it makes a loop and checks one by one. Changes should be done there.
catalogManager.getSampleManager().checkCanDeleteSamples(sampleResource);
catalogManager.getSampleManager().delete(StringUtils.join(sampleIds, ","),
Long.toString(resourceId.getStudyId()), QueryOptions.empty(), sessionId);
}
// Get the individual info before the update
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(individualId, QueryOptions.empty());
String newIndividualName = individualQueryResult.first().getName() + ".DELETED_" + TimeUtils.getTime();
ObjectMap updateParams = new ObjectMap()
.append(IndividualDBAdaptor.QueryParams.NAME.key(), newIndividualName)
.append(IndividualDBAdaptor.QueryParams.STATUS_NAME.key(), Status.DELETED);
queryResult = individualDBAdaptor.update(individualId, updateParams);
auditManager.recordAction(AuditRecord.Resource.individual, AuditRecord.Action.delete, AuditRecord.Magnitude.high,
individualId, resourceId.getUser(), individualQueryResult.first(), queryResult.first(), "", null);
} catch (CatalogAuthorizationException e) {
auditManager.recordAction(AuditRecord.Resource.individual, AuditRecord.Action.delete, AuditRecord.Magnitude.high,
individualId, userId, null, null, e.getMessage(), null);
queryResult = new QueryResult<>("Delete individual " + individualId);
queryResult.setErrorMsg(e.getMessage());
} catch (CatalogException e) {
e.printStackTrace();
queryResult = new QueryResult<>("Delete individual " + individualId);
queryResult.setErrorMsg(e.getMessage());
} finally {
queryResultList.add(queryResult);
}
}
return queryResultList;
}
@Override
public List<QueryResult<Individual>> delete(Query query, QueryOptions options, String sessionId) throws CatalogException, IOException {
QueryOptions queryOptions = new QueryOptions(QueryOptions.INCLUDE, IndividualDBAdaptor.QueryParams.ID.key());
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(query, queryOptions);
List<Long> individualIds = individualQueryResult.getResult().stream().map(Individual::getId).collect(Collectors.toList());
String individualStr = StringUtils.join(individualIds, ",");
return delete(individualStr, null, options, sessionId);
}
@Override
public QueryResult rank(long studyId, Query query, String field, int numResults, boolean asc, String sessionId)
throws CatalogException {
query = ParamUtils.defaultObject(query, Query::new);
ParamUtils.checkObj(field, "field");
ParamUtils.checkObj(studyId, "studyId");
ParamUtils.checkObj(sessionId, "sessionId");
String userId = userManager.getId(sessionId);
authorizationManager.checkStudyPermission(studyId, userId, StudyAclEntry.StudyPermissions.VIEW_INDIVIDUALS);
// TODO: In next release, we will have to check the count parameter from the queryOptions object.
boolean count = true;
// query.append(CatalogIndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId);
QueryResult queryResult = null;
if (count) {
// We do not need to check for permissions when we show the count of files
queryResult = individualDBAdaptor.rank(query, field, numResults, asc);
}
return ParamUtils.defaultObject(queryResult, QueryResult::new);
}
@Override
public QueryResult groupBy(@Nullable String studyStr, Query query, List<String> fields, QueryOptions options, String sessionId)
throws CatalogException {
query = ParamUtils.defaultObject(query, Query::new);
options = ParamUtils.defaultObject(options, QueryOptions::new);
ParamUtils.checkObj(fields, "fields");
String userId = userManager.getId(sessionId);
long studyId = catalogManager.getStudyManager().getId(userId, studyStr);
authorizationManager.checkStudyPermission(studyId, userId, StudyAclEntry.StudyPermissions.VIEW_INDIVIDUALS);
// Add study id to the query
query.put(IndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId);
// TODO: In next release, we will have to check the count parameter from the queryOptions object.
boolean count = true;
// query.append(CatalogIndividualDBAdaptor.QueryParams.STUDY_ID.key(), studyId);
QueryResult queryResult = null;
if (count) {
// We do not need to check for permissions when we show the count of files
queryResult = individualDBAdaptor.groupBy(query, fields, options);
}
return ParamUtils.defaultObject(queryResult, QueryResult::new);
}
private long commonGetAllInvidualSets(String id, @Nullable String studyStr, String sessionId) throws CatalogException {
MyResourceId resource = getId(id, studyStr, sessionId);
authorizationManager.checkIndividualPermission(resource.getResourceId(), resource.getUser(),
IndividualAclEntry.IndividualPermissions.VIEW_ANNOTATIONS);
return resource.getResourceId();
}
private long commonGetAnnotationSet(String id, @Nullable String studyStr, String annotationSetName, String sessionId)
throws CatalogException {
ParamUtils.checkAlias(annotationSetName, "annotationSetName", configuration.getCatalog().getOffset());
MyResourceId resource = getId(id, studyStr, sessionId);
authorizationManager.checkIndividualPermission(resource.getResourceId(), resource.getUser(),
IndividualAclEntry.IndividualPermissions.VIEW_ANNOTATIONS);
return resource.getResourceId();
}
@Override
public QueryResult<AnnotationSet> createAnnotationSet(String id, @Nullable String studyStr, long variableSetId,
String annotationSetName, Map<String, Object> annotations,
Map<String, Object> attributes, String sessionId) throws CatalogException {
ParamUtils.checkParameter(annotationSetName, "annotationSetName");
ParamUtils.checkObj(annotations, "annotations");
attributes = ParamUtils.defaultObject(attributes, HashMap<String, Object>::new);
MyResourceId resource = catalogManager.getIndividualManager().getId(id, studyStr, sessionId);
authorizationManager.checkIndividualPermission(resource.getResourceId(), resource.getUser(),
IndividualAclEntry.IndividualPermissions.WRITE_ANNOTATIONS);
VariableSet variableSet = studyDBAdaptor.getVariableSet(variableSetId, null).first();
QueryResult<AnnotationSet> annotationSet = AnnotationManager.createAnnotationSet(resource.getResourceId(), variableSet,
annotationSetName, annotations, attributes, individualDBAdaptor);
auditManager.recordUpdate(AuditRecord.Resource.individual, resource.getResourceId(), resource.getUser(),
new ObjectMap("annotationSets", annotationSet.first()), "annotate", null);
return annotationSet;
}
@Override
public QueryResult<AnnotationSet> getAllAnnotationSets(String id, @Nullable String studyStr, String sessionId) throws CatalogException {
long individualId = commonGetAllInvidualSets(id, studyStr, sessionId);
return individualDBAdaptor.getAnnotationSet(individualId, null);
}
@Override
public QueryResult<ObjectMap> getAllAnnotationSetsAsMap(String id, @Nullable String studyStr, String sessionId)
throws CatalogException {
long individualId = commonGetAllInvidualSets(id, studyStr, sessionId);
return individualDBAdaptor.getAnnotationSetAsMap(individualId, null);
}
@Override
public QueryResult<AnnotationSet> getAnnotationSet(String id, @Nullable String studyStr, String annotationSetName, String sessionId)
throws CatalogException {
long individualId = commonGetAnnotationSet(id, studyStr, annotationSetName, sessionId);
return individualDBAdaptor.getAnnotationSet(individualId, annotationSetName);
}
@Override
public QueryResult<ObjectMap> getAnnotationSetAsMap(String id, @Nullable String studyStr, String annotationSetName, String sessionId)
throws CatalogException {
long individualId = commonGetAnnotationSet(id, studyStr, annotationSetName, sessionId);
return individualDBAdaptor.getAnnotationSetAsMap(individualId, annotationSetName);
}
@Override
public QueryResult<AnnotationSet> updateAnnotationSet(String id, @Nullable String studyStr, String annotationSetName, Map<String,
Object> newAnnotations, String sessionId) throws CatalogException {
ParamUtils.checkParameter(id, "id");
ParamUtils.checkParameter(annotationSetName, "annotationSetName");
ParamUtils.checkObj(newAnnotations, "newAnnotations");
MyResourceId resource = getId(id, studyStr, sessionId);
authorizationManager.checkIndividualPermission(resource.getResourceId(), resource.getUser(),
IndividualAclEntry.IndividualPermissions.WRITE_ANNOTATIONS);
// Update the annotation
QueryResult<AnnotationSet> queryResult =
AnnotationManager.updateAnnotationSet(resource.getResourceId(), annotationSetName, newAnnotations, individualDBAdaptor,
studyDBAdaptor);
if (queryResult == null || queryResult.getNumResults() == 0) {
throw new CatalogException("There was an error with the update");
}
AnnotationSet annotationSet = queryResult.first();
// Audit the changes
AnnotationSet annotationSetUpdate = new AnnotationSet(annotationSet.getName(), annotationSet.getVariableSetId(),
newAnnotations.entrySet().stream()
.map(entry -> new Annotation(entry.getKey(), entry.getValue()))
.collect(Collectors.toSet()), annotationSet.getCreationDate(), null);
auditManager.recordUpdate(AuditRecord.Resource.individual, resource.getResourceId(), resource.getUser(),
new ObjectMap("annotationSets", Collections.singletonList(annotationSetUpdate)), "update annotation", null);
return queryResult;
}
@Override
public QueryResult<AnnotationSet> deleteAnnotationSet(String id, @Nullable String studyStr, String annotationSetName, String sessionId)
throws CatalogException {
ParamUtils.checkParameter(id, "id");
ParamUtils.checkParameter(annotationSetName, "annotationSetName");
MyResourceId resource = getId(id, studyStr, sessionId);
authorizationManager.checkIndividualPermission(resource.getResourceId(), resource.getUser(),
IndividualAclEntry.IndividualPermissions.DELETE_ANNOTATIONS);
QueryResult<AnnotationSet> annotationSet = individualDBAdaptor.getAnnotationSet(resource.getResourceId(), annotationSetName);
if (annotationSet == null || annotationSet.getNumResults() == 0) {
throw new CatalogException("Could not delete annotation set. The annotation set with name " + annotationSetName + " could not "
+ "be found in the database.");
}
individualDBAdaptor.deleteAnnotationSet(resource.getResourceId(), annotationSetName);
auditManager.recordDeletion(AuditRecord.Resource.individual, resource.getResourceId(), resource.getUser(),
new ObjectMap("annotationSets", Collections.singletonList(annotationSet.first())), "delete annotation", null);
return annotationSet;
}
@Override
public QueryResult<ObjectMap> searchAnnotationSetAsMap(String id, @Nullable String studyStr, long variableSetId,
@Nullable String annotation, String sessionId) throws CatalogException {
QueryResult<Individual> individualQueryResult = commonSearchAnnotationSet(id, studyStr, variableSetId, annotation, sessionId);
List<ObjectMap> annotationSets;
if (individualQueryResult == null || individualQueryResult.getNumResults() == 0) {
annotationSets = Collections.emptyList();
} else {
annotationSets = individualQueryResult.first().getAnnotationSetAsMap();
}
return new QueryResult<>("Search annotation sets", individualQueryResult.getDbTime(), annotationSets.size(), annotationSets.size(),
individualQueryResult.getWarningMsg(), individualQueryResult.getErrorMsg(), annotationSets);
}
@Override
public QueryResult<AnnotationSet> searchAnnotationSet(String id, @Nullable String studyStr, long variableSetId,
@Nullable String annotation, String sessionId) throws CatalogException {
QueryResult<Individual> individualQueryResult = commonSearchAnnotationSet(id, studyStr, variableSetId, annotation, sessionId);
List<AnnotationSet> annotationSets;
if (individualQueryResult == null || individualQueryResult.getNumResults() == 0) {
annotationSets = Collections.emptyList();
} else {
annotationSets = individualQueryResult.first().getAnnotationSets();
}
return new QueryResult<>("Search annotation sets", individualQueryResult.getDbTime(), annotationSets.size(), annotationSets.size(),
individualQueryResult.getWarningMsg(), individualQueryResult.getErrorMsg(), annotationSets);
}
private QueryResult<Individual> commonSearchAnnotationSet(String id, @Nullable String studyStr, long variableSetId,
@Nullable String annotation, String sessionId) throws CatalogException {
ParamUtils.checkParameter(id, "id");
MyResourceId resource = getId(id, studyStr, sessionId);
authorizationManager.checkIndividualPermission(resource.getResourceId(), resource.getUser(),
IndividualAclEntry.IndividualPermissions.VIEW_ANNOTATIONS);
Query query = new Query(IndividualDBAdaptor.QueryParams.ID.key(), id);
if (variableSetId > 0) {
query.append(IndividualDBAdaptor.QueryParams.VARIABLE_SET_ID.key(), variableSetId);
}
if (annotation != null) {
query.append(IndividualDBAdaptor.QueryParams.ANNOTATION.key(), annotation);
}
QueryOptions queryOptions = new QueryOptions(QueryOptions.INCLUDE, IndividualDBAdaptor.QueryParams.ANNOTATION_SETS.key());
logger.debug("Query: {}, \t QueryOptions: {}", query.safeToString(), queryOptions.safeToString());
QueryResult<Individual> individualQueryResult = individualDBAdaptor.get(query, queryOptions);
// Filter out annotation sets only from the variable set id specified
for (Individual individual : individualQueryResult.getResult()) {
List<AnnotationSet> finalAnnotationSets = new ArrayList<>();
for (AnnotationSet annotationSet : individual.getAnnotationSets()) {
if (annotationSet.getVariableSetId() == variableSetId) {
finalAnnotationSets.add(annotationSet);
}
}
if (finalAnnotationSets.size() > 0) {
individual.setAnnotationSets(finalAnnotationSets);
}
}
return individualQueryResult;
}
}