diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java index 50e6d32da75..3ca18fe0eb9 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalInterpretationManager.java @@ -824,7 +824,7 @@ public ClinicalAnalyst getAnalyst(String token) throws ToolException { OpenCGAResult userQueryResult = catalogManager.getUserManager().get(userId, new QueryOptions(QueryOptions.INCLUDE, Arrays.asList(UserDBAdaptor.QueryParams.EMAIL.key(), UserDBAdaptor.QueryParams.ORGANIZATION.key())), token); User user = userQueryResult.first(); - return new ClinicalAnalyst(userId, user.getName(), user.getEmail(), "", ""); + return new ClinicalAnalyst(userId, user.getName(), user.getEmail(), "", Collections.emptyMap()); } catch (CatalogException e) { throw new ToolException(e); } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalTsvAnnotationLoader.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalTsvAnnotationLoader.java new file mode 100644 index 00000000000..1d2be95e82e --- /dev/null +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalTsvAnnotationLoader.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015-2020 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.analysis.clinical; + +import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.commons.datastore.core.Query; +import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.opencga.analysis.annotations.TsvAnnotationLoader; +import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.managers.AnnotationSetManager; +import org.opencb.opencga.catalog.utils.Constants; +import org.opencb.opencga.catalog.utils.ParamUtils; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysisUpdateParams; +import org.opencb.opencga.core.models.common.AnnotationSet; +import org.opencb.opencga.core.models.common.Enums; +import org.opencb.opencga.core.tools.annotations.Tool; + +import java.util.Collections; + +@Tool(id = ClinicalTsvAnnotationLoader.ID, resource = Enums.Resource.CLINICAL_ANALYSIS, type = Tool.Type.OPERATION, + description = "Load annotations from TSV file.") +public class ClinicalTsvAnnotationLoader extends TsvAnnotationLoader { + public final static String ID = "clinical-tsv-load"; + + @Override + public int count(Query query) throws CatalogException { + return catalogManager.getClinicalAnalysisManager().count(study, query, token).getNumResults(); + } + + @Override + public void addAnnotationSet(String entryId, AnnotationSet annotationSet, QueryOptions options) throws CatalogException { + ClinicalAnalysisUpdateParams updateParams = new ClinicalAnalysisUpdateParams() + .setAnnotationSets(Collections.singletonList(annotationSet)); + QueryOptions queryOptions = options != null ? new QueryOptions(options) : new QueryOptions(); + queryOptions.put(Constants.ACTIONS, new ObjectMap(AnnotationSetManager.ANNOTATION_SETS, ParamUtils.BasicUpdateAction.ADD)); + + catalogManager.getClinicalAnalysisManager().update(study, entryId, updateParams, queryOptions, token); + } +} diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/InternalCliOptionsParser.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/InternalCliOptionsParser.java index 132ad854c82..0079461d25a 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/InternalCliOptionsParser.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/InternalCliOptionsParser.java @@ -226,6 +226,7 @@ public InternalCliOptionsParser() { clinicalSubCommands.addCommand(RGA_INDEX_RUN_COMMAND, clinicalCommandOptions.rgaSecondaryIndexCommandOptions); clinicalSubCommands.addCommand(RGA_AUX_INDEX_RUN_COMMAND, clinicalCommandOptions.rgaAuxiliarSecondaryIndexCommandOptions); clinicalSubCommands.addCommand(EXOMISER_INTERPRETATION_RUN_COMMAND, clinicalCommandOptions.exomiserInterpretationCommandOptions); + clinicalSubCommands.addCommand("tsv-load", clinicalCommandOptions.tsvLoad); fileCommandOptions = new FileCommandOptions(commonCommandOptions, jCommander); jCommander.addCommand("files", fileCommandOptions); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ClinicalCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ClinicalCommandExecutor.java index e0785c79fee..14256ac1652 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ClinicalCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ClinicalCommandExecutor.java @@ -24,6 +24,7 @@ import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.opencga.analysis.clinical.ClinicalTsvAnnotationLoader; import org.opencb.opencga.analysis.clinical.exomiser.ExomiserInterpretationAnalysis; import org.opencb.opencga.analysis.clinical.rga.AuxiliarRgaAnalysis; import org.opencb.opencga.analysis.clinical.rga.RgaAnalysis; @@ -96,6 +97,9 @@ public void execute() throws Exception { case EXOMISER_INTERPRETATION_RUN_COMMAND: exomiserInterpretation(); break; + case "tsv-load": + tsvLoad(); + break; default: logger.error("Subcommand not valid"); break; @@ -314,4 +318,19 @@ private void exomiserInterpretation() throws Exception { // exomiserInterpretationAnalysis.setPrimary(cliOptions.primary); exomiserInterpretationAnalysis.start(); } + + private void tsvLoad() throws ToolException { + ClinicalCommandOptions.TsvLoad options = clinicalCommandOptions.tsvLoad; + + Path outDir = Paths.get(options.outDir); + + ClinicalTsvAnnotationLoader annotationLoader = new ClinicalTsvAnnotationLoader(); + annotationLoader.setAnnotationSetId(options.annotationSetId); + annotationLoader.setVariableSetId(options.variableSetId); + annotationLoader.setPath(options.filePath); + annotationLoader.setStudy(options.study); + + annotationLoader.setUp(opencgaHome.toString(), new ObjectMap(), outDir, options.commonOptions.token); + annotationLoader.start(); + } } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/ClinicalCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/ClinicalCommandOptions.java index 2663f3c7c5e..9c7f8806fc7 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/ClinicalCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/options/ClinicalCommandOptions.java @@ -5,6 +5,7 @@ import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; import org.opencb.biodata.models.clinical.ClinicalProperty; +import org.opencb.opencga.analysis.clinical.ClinicalTsvAnnotationLoader; import org.opencb.opencga.analysis.clinical.exomiser.ExomiserInterpretationAnalysis; import org.opencb.opencga.analysis.clinical.rga.AuxiliarRgaAnalysis; import org.opencb.opencga.analysis.clinical.rga.RgaAnalysis; @@ -14,6 +15,7 @@ import org.opencb.opencga.analysis.clinical.zetta.ZettaInterpretationAnalysis; import org.opencb.opencga.app.cli.GeneralCliOptions; import org.opencb.opencga.app.cli.internal.InternalCliOptionsParser; +import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.models.clinical.RgaAnalysisParams; import org.opencb.opencga.storage.app.cli.client.options.StorageVariantCommandOptions.BasicVariantQueryOptions; @@ -33,6 +35,7 @@ public class ClinicalCommandOptions { public final RgaSecondaryIndexCommandOptions rgaSecondaryIndexCommandOptions; public final RgaAuxiliarSecondaryIndexCommandOptions rgaAuxiliarSecondaryIndexCommandOptions; public final ExomiserInterpretationCommandOptions exomiserInterpretationCommandOptions; + public final TsvLoad tsvLoad; public JCommander jCommander; public GeneralCliOptions.CommonCommandOptions commonCommandOptions; @@ -53,6 +56,7 @@ public ClinicalCommandOptions(GeneralCliOptions.CommonCommandOptions commonComma this.rgaSecondaryIndexCommandOptions = new RgaSecondaryIndexCommandOptions(); this.rgaAuxiliarSecondaryIndexCommandOptions = new RgaAuxiliarSecondaryIndexCommandOptions(); this.exomiserInterpretationCommandOptions = new ExomiserInterpretationCommandOptions(); + this.tsvLoad = new TsvLoad(); } @Parameters(commandNames = {TieringCommandOptions.TIERING_INTERPRETATION_RUN_COMMAND}, commandDescription = @@ -335,4 +339,29 @@ public class ExomiserInterpretationCommandOptions extends GeneralCliOptions.Stud @Parameter(names = {"-o", "--outdir"}, description = "Directory where output files will be saved", arity = 1) public String outdir; } + + @Parameters(commandNames = {"tsv-load"}, commandDescription = "Load annotations from a TSV file") + public class TsvLoad extends GeneralCliOptions.StudyOption { + + public static final String TSV_LOAD_COMMAND = ClinicalTsvAnnotationLoader.ID; + + @ParametersDelegate + public GeneralCliOptions.CommonCommandOptions commonOptions = commonCommandOptions; + + @ParametersDelegate + public InternalCliOptionsParser.JobOptions jobOptions = internalJobOptions; + + @Parameter(names = {"--file"}, description = "Path to the TSV file.", required = true, arity = 1) + public String filePath; + + @Parameter(names = {"--variable-set-id"}, description = ParamConstants.VARIABLE_SET_DESCRIPTION, required = true, arity = 1) + public String variableSetId; + + @Parameter(names = {"--annotation-set-id"}, description = "AnnotationSet id that will be given to the new annotations.", + required = true, arity = 1) + public String annotationSetId; + + @Parameter(names = {"-o", "--outdir"}, description = "Directory where output files will be saved", required = true, arity = 1) + public String outDir; + } } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_0/catalog/AddAnnotationSetsInClinicalAnalysisMigration.java b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_0/catalog/AddAnnotationSetsInClinicalAnalysisMigration.java new file mode 100644 index 00000000000..0ab4fceb81a --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_0/catalog/AddAnnotationSetsInClinicalAnalysisMigration.java @@ -0,0 +1,43 @@ +package org.opencb.opencga.app.migrations.v2_12_0.catalog; + + +import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.Filters; +import com.mongodb.client.result.UpdateResult; +import org.bson.Document; +import org.bson.conversions.Bson; +import org.opencb.opencga.catalog.db.mongodb.AnnotationMongoDBAdaptor; +import org.opencb.opencga.catalog.db.mongodb.MongoDBAdaptorFactory; +import org.opencb.opencga.catalog.migration.Migration; +import org.opencb.opencga.catalog.migration.MigrationTool; + +import java.util.Arrays; +import java.util.Collections; + +@Migration(id = "add_annotation_sets_to_clinical_analysis" , + description = "Add private annotation fields to ClinicalAnalysis documents #TASK-5198", + version = "2.12.0", + domain = Migration.MigrationDomain.CATALOG, + language = Migration.MigrationLanguage.JAVA, + date = 20231116 +) +public class AddAnnotationSetsInClinicalAnalysisMigration extends MigrationTool { + + @Override + protected void run() throws Exception { + Bson query = Filters.exists(AnnotationMongoDBAdaptor.AnnotationSetParams.ANNOTATION_SETS.key(), false); + Document update = new Document("$set", new Document() + .append(AnnotationMongoDBAdaptor.AnnotationSetParams.ANNOTATION_SETS.key(), Collections.emptyList()) + .append(AnnotationMongoDBAdaptor.AnnotationSetParams.INTERNAL_ANNOTATION_SETS.key(), Collections.emptyList()) + .append(AnnotationMongoDBAdaptor.AnnotationSetParams.PRIVATE_VARIABLE_SET_MAP.key(), Collections.emptyMap()) + .append(AnnotationMongoDBAdaptor.AnnotationSetParams.PRIVATE_INTERNAL_VARIABLE_SET_MAP.key(), Collections.emptyMap()) + ); + // Initialise private fields in all Clinical Analysis documents + for (String collection : Arrays.asList(MongoDBAdaptorFactory.CLINICAL_ANALYSIS_COLLECTION, + MongoDBAdaptorFactory.DELETED_CLINICAL_ANALYSIS_COLLECTION)) { + MongoCollection mongoCollection = getMongoCollection(collection); + UpdateResult updateResult = mongoCollection.updateMany(query, update); + logger.info("{} clinical analysis documents updated from the {} collection", updateResult.getModifiedCount(), collection); + } + } +} diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_0/catalog/CompleteClinicalReportDataModelMigration.java b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_0/catalog/CompleteClinicalReportDataModelMigration.java new file mode 100644 index 00000000000..f7e6123b223 --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v2_12_0/catalog/CompleteClinicalReportDataModelMigration.java @@ -0,0 +1,62 @@ +package org.opencb.opencga.app.migrations.v2_12_0.catalog; + +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Projections; +import com.mongodb.client.model.UpdateOneModel; +import org.bson.Document; +import org.opencb.opencga.catalog.db.mongodb.MongoDBAdaptor; +import org.opencb.opencga.catalog.db.mongodb.MongoDBAdaptorFactory; +import org.opencb.opencga.catalog.migration.Migration; +import org.opencb.opencga.catalog.migration.MigrationTool; + +import java.util.Arrays; +import java.util.Collections; + +import static com.mongodb.client.model.Filters.eq; + +@Migration(id = "complete_clinical_report_data_model" , + description = "Complete Clinical Report data model #TASK-5198", + version = "2.12.0", + domain = Migration.MigrationDomain.CATALOG, + language = Migration.MigrationLanguage.JAVA, + date = 20231128 +) +public class CompleteClinicalReportDataModelMigration extends MigrationTool { + + @Override + protected void run() throws Exception { + migrateCollection( + Arrays.asList(MongoDBAdaptorFactory.CLINICAL_ANALYSIS_COLLECTION, MongoDBAdaptorFactory.DELETED_CLINICAL_ANALYSIS_COLLECTION), + Filters.exists("analyst"), + Projections.include(Arrays.asList("analyst", "report")), + (document, bulk) -> { + Document analyst = document.get("analyst", Document.class); + analyst.remove("assignedBy"); + analyst.remove("date"); + + Document report = document.get("report", Document.class); + if (report != null) { + report.put("comments", Collections.emptyList()); + report.put("supportingEvidences", Collections.emptyList()); + report.put("files", Collections.emptyList()); + } + + MongoDBAdaptor.UpdateDocument updateDocument = new MongoDBAdaptor.UpdateDocument(); + updateDocument.getSet().put("report", report); + updateDocument.getSet().put("request", new Document()); + updateDocument.getSet().put("responsible", new Document() + .append("id", analyst.get("id")) + .append("name", analyst.get("name")) + .append("email", analyst.get("email")) + ); + updateDocument.getSet().put("analysts", Collections.singletonList(analyst)); + updateDocument.getUnset().add("analyst"); + + bulk.add(new UpdateOneModel<>( + eq("_id", document.get("_id")), + updateDocument.toFinalUpdateDocument() + ) + ); + }); + } +} diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/ClinicalAnalysisDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/ClinicalAnalysisDBAdaptor.java index f40e073d760..a4b3d8f19b4 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/ClinicalAnalysisDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/ClinicalAnalysisDBAdaptor.java @@ -26,8 +26,10 @@ import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.exceptions.CatalogParameterException; +import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; +import org.opencb.opencga.core.models.study.VariableSet; import org.opencb.opencga.core.response.OpenCGAResult; import java.util.List; @@ -38,7 +40,7 @@ /** * Created by pfurio on 05/06/17. */ -public interface ClinicalAnalysisDBAdaptor extends CoreDBAdaptor { +public interface ClinicalAnalysisDBAdaptor extends AnnotationSetDBAdaptor { enum QueryParams implements QueryParam { ID("id", TEXT, ""), @@ -67,10 +69,14 @@ enum QueryParams implements QueryParam { CONSENT("consent", OBJECT, ""), PRIORITY("priority", OBJECT, ""), PRIORITY_ID("priority.id", TEXT, ""), - ANALYST("analyst", TEXT_ARRAY, ""), - ANALYST_ID("analyst.id", TEXT, ""), - ANALYST_ASSIGNED_BY("analyst.assignedBy", TEXT, ""), + ANALYSTS("analysts", TEXT_ARRAY, ""), + ANALYSTS_ID("analysts.id", TEXT, ""), + ANALYSTS_ASSIGNED_BY("analysts.assignedBy", TEXT, ""), REPORT("report", OBJECT, ""), + REPORT_SUPPORTING_EVIDENCES("report.supportingEvidences", TEXT_ARRAY, ""), + REPORT_FILES("report.files", TEXT_ARRAY, ""), + REQUEST("request", OBJECT, ""), + RESPONSIBLE("responsible", OBJECT, ""), FLAGS("flags", OBJECT, ""), FLAGS_ID("flags.id", TEXT, ""), RELEASE("release", INTEGER, ""), @@ -109,7 +115,11 @@ enum QueryParams implements QueryParam { DELETED(ParamConstants.DELETED_PARAM, BOOLEAN, ""), STUDY_UID("studyUid", INTEGER_ARRAY, ""), - STUDY("study", INTEGER_ARRAY, ""); // Alias to studyId in the database. Only for the webservices. + STUDY("study", INTEGER_ARRAY, ""), // Alias to studyId in the database. Only for the webservices. + + ANNOTATION_SETS("annotationSets", TEXT_ARRAY, ""), + ANNOTATION_SET_NAME("annotationSetName", TEXT_ARRAY, ""), + ANNOTATION(Constants.ANNOTATION, TEXT_ARRAY, ""); private static Map map; @@ -154,6 +164,54 @@ public static QueryParams getParam(String key) { } } + enum ReportQueryParams implements QueryParam { + COMMENTS("comments", OBJECT, ""), + SUPPORTING_EVIDENCES("supportingEvidences", TEXT_ARRAY, ""), + FILES("files", TEXT_ARRAY, ""); + + private static Map map; + + static { + map = new LinkedMap(); + for (ReportQueryParams params : ReportQueryParams.values()) { + map.put(params.key(), params); + } + } + + private final String key; + private Type type; + private String description; + + ReportQueryParams(String key, Type type, String description) { + this.key = key; + this.type = type; + this.description = description; + } + + @Override + public String key() { + return key; + } + + @Override + public Type type() { + return type; + } + + @Override + public String description() { + return description; + } + + public static Map getMap() { + return map; + } + + public static ReportQueryParams getParam(String key) { + return map.get(key); + } + } + default boolean exists(long clinicalAnalysisId) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { return count(new Query(QueryParams.UID.key(), clinicalAnalysisId)).getNumMatches() > 0; } @@ -170,14 +228,16 @@ default void checkId(long clinicalAnalysisId) throws CatalogDBException, Catalog OpenCGAResult nativeInsert(Map clinicalAnalysis, String userId) throws CatalogDBException; - OpenCGAResult insert(long studyId, ClinicalAnalysis clinicalAnalysis, List clinicalAuditList, QueryOptions options) + OpenCGAResult insert(long studyId, ClinicalAnalysis clinicalAnalysis, List variableSetList, + List clinicalAuditList, QueryOptions options) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException; - OpenCGAResult update(long id, ObjectMap parameters, List clinicalAuditList, QueryOptions queryOptions) + OpenCGAResult update(long id, ObjectMap parameters, List variableSetList, + List clinicalAuditList, QueryOptions queryOptions) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException; - OpenCGAResult update(Query query, ObjectMap parameters, List clinicalAuditList, - QueryOptions queryOptions) + OpenCGAResult update(Query query, ObjectMap parameters, List variableSetList, + List clinicalAuditList, QueryOptions queryOptions) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException; OpenCGAResult get(long clinicalAnalysisUid, QueryOptions options) diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AnnotationMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AnnotationMongoDBAdaptor.java index 15c7555e75e..e8cfa1d0ad1 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AnnotationMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AnnotationMongoDBAdaptor.java @@ -262,7 +262,7 @@ public boolean containsAnnotationQuery(Query query) { return query.containsKey(Constants.ANNOTATION); } - OpenCGAResult updateAnnotationSets(ClientSession clientSession, long entryId, ObjectMap parameters, + OpenCGAResult updateAnnotationSets(ClientSession clientSession, long entryUid, ObjectMap parameters, List variableSetList, QueryOptions options, boolean isVersioned) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { Map actionMap = options.getMap(Constants.ACTIONS, new HashMap<>()); @@ -281,7 +281,7 @@ OpenCGAResult updateAnnotationSets(ClientSession clientSess // Create or remove a new annotation set if (action == ParamUtils.BasicUpdateAction.ADD || action == ParamUtils.BasicUpdateAction.SET) { // 1. Check the annotation set ids are not in use - validateNewAnnotations(clientSession, entryId, annotationSetList, variableSetList, isVersioned); + validateNewAnnotations(clientSession, entryUid, annotationSetList, variableSetList, isVersioned); // 2. Obtain the list of documents that need to be inserted List annotationDocumentList = getNewAnnotationList(annotationSetList, variableSetList); @@ -290,26 +290,26 @@ OpenCGAResult updateAnnotationSets(ClientSession clientSess if (action == ParamUtils.BasicUpdateAction.SET) { if (CollectionUtils.isEmpty(internalAnnotationDocumentList)) { // 2.1 Remove all user existing annotations - removeAllAnnotationSets(clientSession, entryId, isVersioned); + removeAllAnnotationSets(clientSession, entryUid, isVersioned); } else { // 2.1 Remove all internal existing annotations - removeAllAnnotationSets(clientSession, entryId, isVersioned, true); + removeAllAnnotationSets(clientSession, entryUid, isVersioned, true); } } if (CollectionUtils.isEmpty(internalAnnotationDocumentList)) { // 3. Insert the list of documents - addNewAnnotations(clientSession, entryId, annotationDocumentList, isVersioned); + addNewAnnotations(clientSession, entryUid, annotationDocumentList, isVersioned); // 4. Set variable set map uid - id - addPrivateVariableMap(clientSession, entryId, getPrivateVariableMapToSet(annotationSetList, variableSetList), + addPrivateVariableMap(clientSession, entryUid, getPrivateVariableMapToSet(annotationSetList, variableSetList), isVersioned); } else { // 3. Insert the list of documents - addNewAnnotations(clientSession, entryId, internalAnnotationDocumentList, isVersioned, true); + addNewAnnotations(clientSession, entryUid, internalAnnotationDocumentList, isVersioned, true); // 4. Set variable set map uid - id - addPrivateVariableMap(clientSession, entryId, getPrivateVariableMapToSet(annotationSetList, variableSetList), + addPrivateVariableMap(clientSession, entryUid, getPrivateVariableMapToSet(annotationSetList, variableSetList), isVersioned, true); } @@ -317,7 +317,7 @@ OpenCGAResult updateAnnotationSets(ClientSession clientSess // Action = REMOVE // 0. Obtain the annotationSet to be removed to know the variableSet being annotated - OpenCGAResult queryResult = nativeGet(new Query(PRIVATE_UID, entryId), new QueryOptions(QueryOptions.INCLUDE, + OpenCGAResult queryResult = nativeGet(new Query(PRIVATE_UID, entryUid), new QueryOptions(QueryOptions.INCLUDE, Arrays.asList(ANNOTATION_SETS))); if (queryResult.getNumResults() != 1) { @@ -373,7 +373,7 @@ OpenCGAResult updateAnnotationSets(ClientSession clientSess } // 1. Remove annotationSet - removeAnnotationSetByAnnotationSetId(clientSession, entryId, annotationSet.getId(), isVersioned); + removeAnnotationSetByAnnotationSetId(clientSession, entryUid, annotationSet.getId(), isVersioned); String variableSetId = annotationSetIdVariableSetUidMap.get(annotationSet.getId()); // Remove the annotation set from the variableSetAnnotationsets @@ -384,7 +384,7 @@ OpenCGAResult updateAnnotationSets(ClientSession clientSess // 2. Unset variable set map uid - id Map variableSetMapToRemove = new HashMap<>(); variableSetMapToRemove.put(variableSetId, null); - removePrivateVariableMap(clientSession, entryId, variableSetMapToRemove, isVersioned); + removePrivateVariableMap(clientSession, entryUid, variableSetMapToRemove, isVersioned); } } else if (StringUtils.isNotEmpty(annotationSet.getVariableSetId())) { VariableSet variableSet = variableSetMap.get(annotationSet.getVariableSetId()); @@ -402,19 +402,18 @@ OpenCGAResult updateAnnotationSets(ClientSession clientSess if (!variableSet.isInternal()) { // Remove all annotationSets - removeAnnotationSetByVariableSetId(clientSession, entryId, variableSet.getUid(), isVersioned); - removePrivateVariableMap(clientSession, entryId, variableSetMapToRemove, isVersioned); + removeAnnotationSetByVariableSetId(clientSession, entryUid, variableSet.getUid(), isVersioned); + removePrivateVariableMap(clientSession, entryUid, variableSetMapToRemove, isVersioned); } else { // Remove all annotationSets - removeAnnotationSetByVariableSetId(clientSession, entryId, variableSet.getUid(), isVersioned, true); - removePrivateVariableMap(clientSession, entryId, variableSetMapToRemove, isVersioned, true); + removeAnnotationSetByVariableSetId(clientSession, entryUid, variableSet.getUid(), isVersioned, true); + removePrivateVariableMap(clientSession, entryUid, variableSetMapToRemove, isVersioned, true); } } } else { throw new CatalogDBException("Could not delete AnnotationSet. AnnotationSet 'id' or 'variableSetId' not defined."); } } - } } else if (actionMap.containsKey(ANNOTATIONS)) { // Update annotation @@ -424,10 +423,10 @@ OpenCGAResult updateAnnotationSets(ClientSession clientSess List annotationDocumentList = getNewAnnotationList(Collections.singletonList(annotationSet), variableSetList); // 2. Remove all the existing annotations of the annotation set - removeAnnotationSetByAnnotationSetId(clientSession, entryId, annotationSet.getId(), isVersioned); + removeAnnotationSetByAnnotationSetId(clientSession, entryUid, annotationSet.getId(), isVersioned); // 3. Add new list of annotations - addNewAnnotations(clientSession, entryId, annotationDocumentList, isVersioned); + addNewAnnotations(clientSession, entryUid, annotationDocumentList, isVersioned); } return endWrite(startTime, 1, 1, new ArrayList<>()); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java index fff9a1ed3b3..26d65edff3f 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java @@ -128,7 +128,7 @@ private void initCollectionConnections() { this.dbCollectionMap.put(Enums.Resource.JOB, Collections.singletonList(dbAdaptorFactory.getCatalogJobDBAdaptor().getJobCollection())); this.dbCollectionMap.put(Enums.Resource.CLINICAL_ANALYSIS, - Collections.singletonList(dbAdaptorFactory.getClinicalAnalysisDBAdaptor().getClinicalCollection())); + Collections.singletonList(dbAdaptorFactory.getClinicalAnalysisDBAdaptor().getCollection())); // Versioned models will always have first the main collection and second the archive collection this.dbCollectionMap.put(Enums.Resource.INDIVIDUAL, Arrays.asList(dbAdaptorFactory.getCatalogIndividualDBAdaptor().getCollection(), diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java index 9ef226dabde..996a7eaac48 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java @@ -25,6 +25,7 @@ import org.apache.commons.lang3.time.StopWatch; import org.bson.Document; import org.bson.conversions.Bson; +import org.opencb.biodata.models.clinical.ClinicalAnalyst; import org.opencb.biodata.models.clinical.ClinicalAudit; import org.opencb.biodata.models.clinical.ClinicalComment; import org.opencb.commons.datastore.core.*; @@ -48,6 +49,7 @@ import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.Configuration; import org.opencb.opencga.core.models.clinical.*; +import org.opencb.opencga.core.models.common.AnnotationSet; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.common.FlagAnnotation; import org.opencb.opencga.core.models.common.InternalStatus; @@ -56,22 +58,27 @@ import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.panel.Panel; import org.opencb.opencga.core.models.sample.Sample; +import org.opencb.opencga.core.models.study.StudyPermissions; +import org.opencb.opencga.core.models.study.VariableSet; import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.UnaryOperator; import static org.opencb.opencga.catalog.db.api.ClinicalAnalysisDBAdaptor.QueryParams.*; +import static org.opencb.opencga.catalog.db.mongodb.AuthorizationMongoDBUtils.filterAnnotationSets; import static org.opencb.opencga.catalog.db.mongodb.AuthorizationMongoDBUtils.getQueryForAuthorisedEntries; import static org.opencb.opencga.catalog.db.mongodb.MongoDBUtils.*; /** * Created by pfurio on 05/06/17. */ -public class ClinicalAnalysisMongoDBAdaptor extends MongoDBAdaptor implements ClinicalAnalysisDBAdaptor { +public class ClinicalAnalysisMongoDBAdaptor extends AnnotationMongoDBAdaptor implements ClinicalAnalysisDBAdaptor { private static final String PRIVATE_DUE_DATE = "_dueDate"; private final MongoDBCollection clinicalCollection; @@ -87,6 +94,11 @@ public ClinicalAnalysisMongoDBAdaptor(MongoDBCollection clinicalCollection, Mong this.clinicalConverter = new ClinicalAnalysisConverter(); } + @Override + protected MongoDBCollection getCollection() { + return clinicalCollection; + } + static void fixCommentsForRemoval(ObjectMap parameters) { if (parameters.get(COMMENTS.key()) == null) { return; @@ -144,8 +156,41 @@ static void fixFilesForRemoval(ObjectMap parameters) { parameters.put(FILES.key(), fileParamList); } - public MongoDBCollection getClinicalCollection() { - return clinicalCollection; + static void fixAnalystsForRemoval(ObjectMap parameters) { + if (parameters.get(ANALYSTS.key()) == null) { + return; + } + + List analystParamList = new LinkedList<>(); + for (Object analyst : parameters.getAsList(ANALYSTS.key())) { + if (analyst instanceof ClinicalAnalyst) { + analystParamList.add(new Document("id", ((ClinicalAnalyst) analyst).getId())); + } + } + parameters.put(ANALYSTS.key(), analystParamList); + } + + @Override + public OpenCGAResult getAnnotationSet(long id, @Nullable String annotationSetName) + throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { + QueryOptions queryOptions = new QueryOptions(); + List includeList = new ArrayList<>(); + + if (StringUtils.isNotEmpty(annotationSetName)) { + includeList.add(Constants.ANNOTATION_SET_NAME + "." + annotationSetName); + } else { + includeList.add(QueryParams.ANNOTATION_SETS.key()); + } + queryOptions.put(QueryOptions.INCLUDE, includeList); + + OpenCGAResult clinicalDataResult = get(id, queryOptions); + if (CollectionUtils.isEmpty(clinicalDataResult.first().getAnnotationSets())) { + return new OpenCGAResult<>(clinicalDataResult.getTime(), clinicalDataResult.getEvents(), 0, Collections.emptyList(), 0); + } else { + List annotationSets = clinicalDataResult.first().getAnnotationSets(); + int size = annotationSets.size(); + return new OpenCGAResult<>(clinicalDataResult.getTime(), clinicalDataResult.getEvents(), size, annotationSets, size); + } } @Override @@ -190,7 +235,8 @@ public OpenCGAResult update(Query query, ObjectMap parameters, } @Override - public OpenCGAResult update(long uid, ObjectMap parameters, List clinicalAuditList, QueryOptions queryOptions) + public OpenCGAResult update(long uid, ObjectMap parameters, List variableSetList, List clinicalAuditList, + QueryOptions queryOptions) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { Query query = new Query(QueryParams.UID.key(), uid); QueryOptions options = new QueryOptions() @@ -203,7 +249,8 @@ public OpenCGAResult update(long uid, ObjectMap parameters, List String clinicalAnalysisId = result.first().getId(); try { - return runTransaction(clientSession -> update(clientSession, result.first(), parameters, clinicalAuditList, queryOptions)); + return runTransaction(clientSession -> privateUpdate(clientSession, result.first(), parameters, variableSetList, + clinicalAuditList, queryOptions)); } catch (CatalogDBException e) { logger.error("Could not update clinical analysis {}: {}", clinicalAnalysisId, e.getMessage(), e); throw new CatalogDBException("Could not update clinical analysis " + clinicalAnalysisId + ": " + e.getMessage(), e.getCause()); @@ -211,26 +258,40 @@ public OpenCGAResult update(long uid, ObjectMap parameters, List } @Override - public OpenCGAResult update(Query query, ObjectMap parameters, List clinicalAuditList, QueryOptions queryOptions) - throws CatalogDBException { - return null; + public OpenCGAResult update(Query query, ObjectMap parameters, List variableSetList, List clinicalAuditList, + QueryOptions queryOptions) throws CatalogDBException { + throw new NotImplementedException("Not possible updating Clinical Analyses based on a query"); } - OpenCGAResult update(ClientSession clientSession, ClinicalAnalysis clinical, ObjectMap parameters, - List clinicalAuditList, QueryOptions queryOptions) + @Override + public OpenCGAResult update(long uid, ObjectMap parameters, List variableSetList, QueryOptions queryOptions) + throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { + throw new NotImplementedException("Use other update method passing the ClinicalAuditList"); + } + + @Override + public OpenCGAResult update(Query query, ObjectMap parameters, List variableSetList, QueryOptions queryOptions) + throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { + throw new NotImplementedException("Use other update method passing the ClinicalAuditList"); + } + + OpenCGAResult privateUpdate(ClientSession clientSession, ClinicalAnalysis clinical, ObjectMap parameters, + List variableSetList, List clinicalAuditList, QueryOptions queryOptions) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { long tmpStartTime = startQuery(); String clinicalAnalysisId = clinical.getId(); long clinicalAnalysisUid = clinical.getUid(); + DataResult result = updateAnnotationSets(clientSession, clinicalAnalysisUid, parameters, variableSetList, queryOptions, false); + + // Perform the update Query query = new Query(QueryParams.UID.key(), clinicalAnalysisUid); UpdateDocument updateDocument = parseAndValidateUpdateParams(parameters, clinicalAuditList, query, queryOptions); Document updateOperation = updateDocument.toFinalUpdateDocument(); - List events = new ArrayList<>(); if (!updateOperation.isEmpty() || !updateDocument.getNestedUpdateList().isEmpty()) { - DataResult update; + DataResult update; if (!updateOperation.isEmpty()) { Bson bsonQuery = Filters.eq(PRIVATE_UID, clinicalAnalysisUid); @@ -270,7 +331,7 @@ OpenCGAResult update(ClientSession clientSession, ClinicalAnalysis clinical, Obj } } - } else { + } else if (result.getNumUpdated() == 0) { throw new CatalogDBException("Nothing to update"); } @@ -336,8 +397,8 @@ UpdateDocument parseAndValidateUpdateParams(ObjectMap parameters, List actionMap = queryOptions.getMap(Constants.ACTIONS, new HashMap<>()); @@ -374,8 +436,26 @@ UpdateDocument parseAndValidateUpdateParams(ObjectMap parameters, List iterator(Query query, QueryOptions options) public DBIterator iterator(ClientSession clientSession, Query query, QueryOptions options) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { MongoDBIterator mongoCursor = getMongoCursor(clientSession, query, options, null); - return new ClinicalAnalysisCatalogMongoDBIterator<>(mongoCursor, clientSession, clinicalConverter, dbAdaptorFactory, options); + return new ClinicalAnalysisCatalogMongoDBIterator<>(mongoCursor, clientSession, clinicalConverter, null, dbAdaptorFactory, options); } @Override @@ -694,7 +774,7 @@ private DBIterator nativeIterator(ClientSession clientSession, Query q queryOptions.put(NATIVE_QUERY, true); MongoDBIterator mongoCursor = getMongoCursor(clientSession, query, queryOptions); - return new ClinicalAnalysisCatalogMongoDBIterator(mongoCursor, clientSession, null, dbAdaptorFactory, queryOptions); + return new ClinicalAnalysisCatalogMongoDBIterator(mongoCursor, clientSession, null, null, dbAdaptorFactory, queryOptions); } @Override @@ -702,7 +782,12 @@ public DBIterator iterator(long studyUid, Query query, QueryOp throws CatalogDBException, CatalogAuthorizationException, CatalogParameterException { query.put(PRIVATE_STUDY_UID, studyUid); MongoDBIterator mongoCursor = getMongoCursor(query, options, user); - return new ClinicalAnalysisCatalogMongoDBIterator(mongoCursor, null, clinicalConverter, dbAdaptorFactory, studyUid, user, options); + Document studyDocument = getStudyDocument(null, studyUid); + UnaryOperator iteratorFilter = (d) -> filterAnnotationSets(studyDocument, d, user, + StudyPermissions.Permissions.VIEW_CLINICAL_ANNOTATIONS.name(), + ClinicalAnalysisPermissions.VIEW_ANNOTATIONS.name()); + return new ClinicalAnalysisCatalogMongoDBIterator(mongoCursor, null, clinicalConverter, iteratorFilter, dbAdaptorFactory, studyUid, + user, options); } @Override @@ -710,10 +795,14 @@ public DBIterator nativeIterator(long studyUid, Query query, QueryOptions option throws CatalogDBException, CatalogAuthorizationException, CatalogParameterException { QueryOptions queryOptions = options != null ? new QueryOptions(options) : new QueryOptions(); queryOptions.put(NATIVE_QUERY, true); - query.put(PRIVATE_STUDY_UID, studyUid); MongoDBIterator mongoCursor = getMongoCursor(query, queryOptions, user); - return new ClinicalAnalysisCatalogMongoDBIterator(mongoCursor, null, null, dbAdaptorFactory, studyUid, user, options); + Document studyDocument = getStudyDocument(null, studyUid); + UnaryOperator iteratorFilter = (d) -> filterAnnotationSets(studyDocument, d, user, + StudyPermissions.Permissions.VIEW_CLINICAL_ANNOTATIONS.name(), + ClinicalAnalysisPermissions.VIEW_ANNOTATIONS.name()); + return new ClinicalAnalysisCatalogMongoDBIterator(mongoCursor, null, null, iteratorFilter, dbAdaptorFactory, studyUid, user, + options); } private MongoDBIterator getMongoCursor(ClientSession clientSession, Query query, QueryOptions options) @@ -823,14 +912,15 @@ public OpenCGAResult nativeInsert(Map clinicalAnalysis, String u } @Override - public OpenCGAResult insert(long studyId, ClinicalAnalysis clinicalAnalysis, List clinicalAuditList, - QueryOptions options) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { + public OpenCGAResult insert(long studyId, ClinicalAnalysis clinicalAnalysis, List variableSetList, + List clinicalAuditList, QueryOptions options) + throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { try { return runTransaction(clientSession -> { long tmpStartTime = startQuery(); logger.debug("Starting ClinicalAnalysis insert transaction for ClinicalAnalysis id '{}'", clinicalAnalysis.getId()); dbAdaptorFactory.getCatalogStudyDBAdaptor().checkId(studyId); - insert(clientSession, studyId, clinicalAnalysis, clinicalAuditList); + insert(clientSession, studyId, clinicalAnalysis, variableSetList, clinicalAuditList); return endWrite(tmpStartTime, 1, 1, 0, 0, null); }); } catch (Exception e) { @@ -839,7 +929,8 @@ public OpenCGAResult insert(long studyId, ClinicalAnalysis clinicalAnalysis, Lis } } - ClinicalAnalysis insert(ClientSession clientSession, long studyId, ClinicalAnalysis clinicalAnalysis, List clinicalAudit) + ClinicalAnalysis insert(ClientSession clientSession, long studyId, ClinicalAnalysis clinicalAnalysis, List variableSetList, + List clinicalAudit) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { if (clinicalAnalysis.getInterpretation() != null) { InterpretationMongoDBAdaptor interpretationDBAdaptor = dbAdaptorFactory.getInterpretationDBAdaptor(); @@ -865,7 +956,7 @@ ClinicalAnalysis insert(ClientSession clientSession, long studyId, ClinicalAnaly clinicalAnalysis.setUuid(UuidUtils.generateOpenCgaUuid(UuidUtils.Entity.CLINICAL)); } - Document clinicalDocument = clinicalConverter.convertToStorageType(clinicalAnalysis); + Document clinicalDocument = clinicalConverter.convertToStorageType(clinicalAnalysis, variableSetList); if (StringUtils.isNotEmpty(clinicalAnalysis.getCreationDate())) { clinicalDocument.put(PRIVATE_CREATION_DATE, TimeUtils.toDate(clinicalAnalysis.getCreationDate())); } else { @@ -1001,8 +1092,8 @@ void updateClinicalAnalysisFamilyReferences(ClientSession clientSession, Family } ObjectMap params = new ObjectMap(QueryParams.FAMILY.key(), familyCopy); - OpenCGAResult result = dbAdaptorFactory.getClinicalAnalysisDBAdaptor().update(clientSession, clinicalAnalysis, params, - null, QueryOptions.empty()); + OpenCGAResult result = dbAdaptorFactory.getClinicalAnalysisDBAdaptor().privateUpdate(clientSession, clinicalAnalysis, + params, Collections.emptyList(), null, QueryOptions.empty()); if (result.getNumUpdated() != 1) { throw new CatalogDBException("ClinicalAnalysis '" + clinicalAnalysis.getId() + "' could not be updated to the latest " + "family version of '" + family.getId() + "'"); @@ -1059,7 +1150,7 @@ void updateClinicalAnalysisPanelReferences(ClientSession clientSession, Panel pa actionMap.put(PANELS.key(), ParamUtils.BasicUpdateAction.SET); QueryOptions updateOptions = new QueryOptions(Constants.ACTIONS, actionMap); ObjectMap params = new ObjectMap(PANELS.key(), panelList); - update(clientSession, clinicalAnalysis, params, null, updateOptions); + privateUpdate(clientSession, clinicalAnalysis, params, Collections.emptyList(), null, updateOptions); // Update references from Interpretations dbAdaptorFactory.getInterpretationDBAdaptor().updateInterpretationPanelReferences(clientSession, clinicalAnalysis, panel); @@ -1082,6 +1173,7 @@ protected Bson parseQuery(Query query, Document extraQuery) private Bson parseQuery(Query query, Document extraQuery, String user) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { List andBsonList = new ArrayList<>(); + Document annotationDocument = null; Query queryCopy = new Query(query); queryCopy.remove(QueryParams.DELETED.key()); @@ -1094,9 +1186,13 @@ private Bson parseQuery(Query query, Document extraQuery, String user) andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, queryCopy, Enums.Resource.CLINICAL_ANALYSIS, user, configuration)); } else { - // Get the document query needed to check the permissions as well - andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, - ClinicalAnalysisPermissions.VIEW.name(), Enums.Resource.CLINICAL_ANALYSIS, configuration)); + if (containsAnnotationQuery(query)) { + andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, + ClinicalAnalysisPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.CLINICAL_ANALYSIS, configuration)); + } else { + andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, ClinicalAnalysisPermissions.VIEW.name(), + Enums.Resource.CLINICAL_ANALYSIS, configuration)); + } } queryCopy.remove(ParamConstants.ACL_PARAM); @@ -1107,6 +1203,9 @@ private Bson parseQuery(Query query, Document extraQuery, String user) QueryParams queryParam = QueryParams.getParam(entry.getKey()) != null ? QueryParams.getParam(entry.getKey()) : QueryParams.getParam(key); if (queryParam == null) { + if (Constants.PRIVATE_ANNOTATION_PARAM_TYPES.equals(entry.getKey())) { + continue; + } throw new CatalogDBException("Unexpected parameter " + entry.getKey() + ". The parameter does not exist or cannot be " + "queried for."); } @@ -1154,6 +1253,12 @@ private Bson parseQuery(Query query, Document extraQuery, String user) queryCopy.getString(queryParam.key()))); addAutoOrQuery(INTERNAL_STATUS_ID.key(), queryParam.key(), queryCopy, INTERNAL_STATUS_ID.type(), andBsonList); break; + case ANNOTATION: + if (annotationDocument == null) { + annotationDocument = createAnnotationQuery(queryCopy.getString(QueryParams.ANNOTATION.key()), + queryCopy.get(Constants.PRIVATE_ANNOTATION_PARAM_TYPES, ObjectMap.class)); + } + break; // Other parameter that can be queried. case ID: case UUID: @@ -1167,7 +1272,7 @@ private Bson parseQuery(Query query, Document extraQuery, String user) case FAMILY_MEMBERS_UID: case FAMILY_MEMBERS_SAMPLES_UID: case PANELS_UID: - case ANALYST_ID: + case ANALYSTS_ID: case PRIORITY_ID: case FLAGS_ID: case QUALITY_CONTROL_SUMMARY: @@ -1179,11 +1284,14 @@ private Bson parseQuery(Query query, Document extraQuery, String user) throw new CatalogDBException("Cannot query by parameter " + queryParam.key()); } } catch (Exception e) { - logger.error("Error with " + entry.getKey() + " " + entry.getValue()); + logger.error("Error with {}: {}", entry.getKey(), entry.getValue()); throw new CatalogDBException(e); } } + if (annotationDocument != null && !annotationDocument.isEmpty()) { + andBsonList.add(annotationDocument); + } if (extraQuery != null && !extraQuery.isEmpty()) { andBsonList.add(extraQuery); } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java index 6437a0293b2..d9c6857200a 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java @@ -625,8 +625,8 @@ private void updateClinicalAnalysisIndividualReferences(ClientSession clientSess ObjectMap params = new ObjectMap(ClinicalAnalysisDBAdaptor.QueryParams.PROBAND.key(), individualCopy); - OpenCGAResult result = dbAdaptorFactory.getClinicalAnalysisDBAdaptor().update(clientSession, clinicalAnalysis, params, null, - QueryOptions.empty()); + OpenCGAResult result = dbAdaptorFactory.getClinicalAnalysisDBAdaptor().privateUpdate(clientSession, clinicalAnalysis, + params, Collections.emptyList(), null, QueryOptions.empty()); if (result.getNumUpdated() != 1) { throw new CatalogDBException("ClinicalAnalysis '" + clinicalAnalysis.getId() + "' could not be updated to the latest " + "individual version of '" + individual.getId() + "'"); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/InterpretationMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/InterpretationMongoDBAdaptor.java index cf2b0a64eaa..97b1d50546b 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/InterpretationMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/InterpretationMongoDBAdaptor.java @@ -200,7 +200,7 @@ private void updateClinicalAnalysisReferences(ClientSession clientSession, Inter } // Update interpretation(s) in ClinicalAnalysis - clinicalDBAdaptor.update(clientSession, ca, params, clinicalAuditList, options); + clinicalDBAdaptor.privateUpdate(clientSession, ca, params, Collections.emptyList(), clinicalAuditList, options); break; case SECONDARY: // Add to secondaryInterpretations array in ClinicalAnalysis @@ -215,7 +215,7 @@ private void updateClinicalAnalysisReferences(ClientSession clientSession, Inter params.put(ClinicalAnalysisDBAdaptor.QueryParams.INTERPRETATION.key(), null); } - clinicalDBAdaptor.update(clientSession, ca, params, clinicalAuditList, options); + clinicalDBAdaptor.privateUpdate(clientSession, ca, params, Collections.emptyList(), clinicalAuditList, options); break; default: throw new IllegalStateException("Unknown action " + action); @@ -785,7 +785,8 @@ private void updateClinicalAnalysisInterpretationReference(ClientSession clientS params = new ObjectMap(ClinicalAnalysisDBAdaptor.QueryParams.SECONDARY_INTERPRETATIONS.key(), interpretationList); } - OpenCGAResult update = clinicalDBAdaptor.update(clientSession, ca, params, clinicalAuditList, options); + OpenCGAResult update = clinicalDBAdaptor.privateUpdate(clientSession, ca, params, Collections.emptyList(), clinicalAuditList, + options); if (update.getNumUpdated() != 1) { throw new CatalogDBException("Could not update interpretation reference in Clinical Analysis to new version"); } @@ -877,7 +878,8 @@ OpenCGAResult delete(ClientSession clientSession, Interpretation interpretation, actions.put(ClinicalAnalysisDBAdaptor.QueryParams.SECONDARY_INTERPRETATIONS.key(), ParamUtils.BasicUpdateAction.REMOVE); clinicalOptions.put(Constants.ACTIONS, actions); } - clinicalDBAdaptor.update(clientSession, clinicalAnalysis, clinicalParams, clinicalAuditList, clinicalOptions); + clinicalDBAdaptor.privateUpdate(clientSession, clinicalAnalysis, clinicalParams, Collections.emptyList(), clinicalAuditList, + clinicalOptions); Query query = new Query() .append(QueryParams.UID.key(), interpretation.getUid()) diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/StudyMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/StudyMongoDBAdaptor.java index d2e01a601be..d1648813ee8 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/StudyMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/StudyMongoDBAdaptor.java @@ -1263,6 +1263,10 @@ private void deleteAllAnnotationSetsByVariableSet(ClientSession session, long st dbAdaptorFactory.getCatalogFileDBAdaptor() .removeAllAnnotationSetsByVariableSetId(session, studyUid, variableSet, false); break; + case CLINICAL_ANALYSIS: + dbAdaptorFactory.getClinicalAnalysisDBAdaptor() + .removeAllAnnotationSetsByVariableSetId(session, studyUid, variableSet, false); + break; default: throw new CatalogDBException("Unexpected entity '" + entity + "'"); } @@ -1296,6 +1300,9 @@ private void checkVariableSetInUse(VariableSet variableSet) case FILE: result = dbAdaptorFactory.getCatalogFileDBAdaptor().get(query, options); break; + case CLINICAL_ANALYSIS: + result = dbAdaptorFactory.getClinicalAnalysisDBAdaptor().get(query, options); + break; default: throw new CatalogDBException("Unexpected entity '" + entity + "'"); } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/ClinicalAnalysisConverter.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/ClinicalAnalysisConverter.java index 1244c9a7b7e..45fff9dd2f3 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/ClinicalAnalysisConverter.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/converters/ClinicalAnalysisConverter.java @@ -16,31 +16,30 @@ package org.opencb.opencga.catalog.db.mongodb.converters; +import org.apache.commons.collections4.CollectionUtils; import org.bson.Document; import org.opencb.opencga.catalog.db.api.*; import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.panel.Panel; import org.opencb.opencga.core.models.sample.Sample; +import org.opencb.opencga.core.models.study.VariableSet; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** * Created by pfurio on 05/06/17. */ -public class ClinicalAnalysisConverter extends OpenCgaMongoConverter { +public class ClinicalAnalysisConverter extends AnnotableConverter { public ClinicalAnalysisConverter() { super(ClinicalAnalysis.class); } @Override - public Document convertToStorageType(ClinicalAnalysis clinicalAnalysis) { - Document document = super.convertToStorageType(clinicalAnalysis); + public Document convertToStorageType(ClinicalAnalysis clinicalAnalysis, List variableSetList) { + Document document = super.convertToStorageType(clinicalAnalysis, variableSetList); document.put(ClinicalAnalysisDBAdaptor.QueryParams.UID.key(), clinicalAnalysis.getUid()); document.put(ClinicalAnalysisDBAdaptor.QueryParams.STUDY_UID.key(), clinicalAnalysis.getStudyUid()); @@ -56,6 +55,24 @@ public void validateDocumentToUpdate(Document document) { validateProbandToUpdate(document); validatePanelsToUpdate(document); validateFilesToUpdate(document); + validateReportToUpdate(document); + } + + public void validateReportToUpdate(Document document) { + Document report = document.get(ClinicalAnalysisDBAdaptor.QueryParams.REPORT.key(), Document.class); + if (report != null) { + List files = report.getList(ClinicalAnalysisDBAdaptor.ReportQueryParams.SUPPORTING_EVIDENCES.key(), Document.class); + if (CollectionUtils.isNotEmpty(files)) { + List filteredFiles = getReducedFileDocuments(files); + report.put(ClinicalAnalysisDBAdaptor.ReportQueryParams.SUPPORTING_EVIDENCES.key(), filteredFiles); + } + + files = report.getList(ClinicalAnalysisDBAdaptor.ReportQueryParams.FILES.key(), Document.class); + if (CollectionUtils.isNotEmpty(files)) { + List filteredFiles = getReducedFileDocuments(files); + report.put(ClinicalAnalysisDBAdaptor.ReportQueryParams.FILES.key(), filteredFiles); + } + } } public void validateInterpretationToUpdate(Document document) { @@ -141,6 +158,11 @@ public void validatePanelsToUpdate(Document document) { public void validateFilesToUpdate(Document document) { List files = (List) document.get(ClinicalAnalysisDBAdaptor.QueryParams.FILES.key()); + List reducedFiles = getReducedFileDocuments(files); + document.put(ClinicalAnalysisDBAdaptor.QueryParams.FILES.key(), reducedFiles); + } + + private static List getReducedFileDocuments(List files) { if (files != null) { // We make sure we don't store duplicates Map fileMap = new HashMap<>(); @@ -154,12 +176,13 @@ public void validateFilesToUpdate(Document document) { } } - document.put(ClinicalAnalysisDBAdaptor.QueryParams.FILES.key(), - fileMap.entrySet().stream() - .map(entry -> new Document() - .append(FileDBAdaptor.QueryParams.PATH.key(), entry.getValue().getPath()) - .append(FileDBAdaptor.QueryParams.UID.key(), entry.getValue().getUid())) - .collect(Collectors.toList())); + return fileMap.values().stream() + .map(file -> new Document() + .append(FileDBAdaptor.QueryParams.PATH.key(), file.getPath()) + .append(FileDBAdaptor.QueryParams.UID.key(), file.getUid())) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/iterators/ClinicalAnalysisCatalogMongoDBIterator.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/iterators/ClinicalAnalysisCatalogMongoDBIterator.java index 05607ed5a68..fa4ead98ef1 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/iterators/ClinicalAnalysisCatalogMongoDBIterator.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/iterators/ClinicalAnalysisCatalogMongoDBIterator.java @@ -21,24 +21,26 @@ import org.bson.Document; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; -import org.opencb.commons.datastore.mongodb.GenericDocumentComplexConverter; import org.opencb.commons.datastore.mongodb.MongoDBIterator; import org.opencb.opencga.catalog.db.api.*; import org.opencb.opencga.catalog.db.mongodb.*; +import org.opencb.opencga.catalog.db.mongodb.converters.AnnotableConverter; import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.exceptions.CatalogParameterException; import org.opencb.opencga.core.common.JacksonUtils; +import org.opencb.opencga.core.models.common.Annotable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.*; +import java.util.function.UnaryOperator; import static org.opencb.opencga.catalog.db.api.ClinicalAnalysisDBAdaptor.QueryParams.*; import static org.opencb.opencga.catalog.db.mongodb.MongoDBAdaptor.NATIVE_QUERY; -public class ClinicalAnalysisCatalogMongoDBIterator extends CatalogMongoDBIterator { +public class ClinicalAnalysisCatalogMongoDBIterator extends AnnotableCatalogMongoDBIterator { private long studyUid; private String user; @@ -54,8 +56,6 @@ public class ClinicalAnalysisCatalogMongoDBIterator extends CatalogMongoDBIte private QueryOptions interpretationQueryOptions; private QueryOptions panelQueryOptions; - private QueryOptions options; - private Queue clinicalAnalysisListBuffer; private Logger logger; @@ -68,21 +68,20 @@ public class ClinicalAnalysisCatalogMongoDBIterator extends CatalogMongoDBIte private static final String UID_VERSION_SEP = "___"; public ClinicalAnalysisCatalogMongoDBIterator(MongoDBIterator mongoCursor, ClientSession clientSession, - GenericDocumentComplexConverter converter, MongoDBAdaptorFactory dbAdaptorFactory, - QueryOptions options) { - this(mongoCursor, clientSession, converter, dbAdaptorFactory, 0, null, options); + AnnotableConverter converter, UnaryOperator filter, + MongoDBAdaptorFactory dbAdaptorFactory, QueryOptions options) { + this(mongoCursor, clientSession, converter, filter, dbAdaptorFactory, 0, null, options); } public ClinicalAnalysisCatalogMongoDBIterator(MongoDBIterator mongoCursor, ClientSession clientSession, - GenericDocumentComplexConverter converter, MongoDBAdaptorFactory dbAdaptorFactory, - long studyUid, String user, QueryOptions options) { - super(mongoCursor, clientSession, converter, null); + AnnotableConverter converter, UnaryOperator filter, + MongoDBAdaptorFactory dbAdaptorFactory, long studyUid, String user, + QueryOptions options) { + super(mongoCursor, clientSession, converter, filter, options); this.user = user; this.studyUid = studyUid; - this.options = options; - this.fileDBAdaptor = dbAdaptorFactory.getCatalogFileDBAdaptor(); this.familyDBAdaptor = dbAdaptorFactory.getCatalogFamilyDBAdaptor(); this.individualDBAdaptor = dbAdaptorFactory.getCatalogIndividualDBAdaptor(); @@ -110,7 +109,7 @@ public E next() { addAclInformation(next, options); if (converter != null) { - return (E) converter.convertToDataModelType(next); + return (E) converter.convertToDataModelType(next, options); } else { return (E) next; } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManager.java index da22c3da07b..13be28039cc 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManager.java @@ -19,7 +19,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.opencb.biodata.models.clinical.*; +import org.opencb.biodata.models.clinical.ClinicalAnalyst; +import org.opencb.biodata.models.clinical.ClinicalAudit; +import org.opencb.biodata.models.clinical.ClinicalComment; +import org.opencb.biodata.models.clinical.Disorder; import org.opencb.biodata.models.common.Status; import org.opencb.commons.datastore.core.Event; import org.opencb.commons.datastore.core.ObjectMap; @@ -34,16 +37,16 @@ import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.exceptions.CatalogParameterException; import org.opencb.opencga.catalog.models.InternalGetDataResult; -import org.opencb.opencga.catalog.utils.Constants; -import org.opencb.opencga.catalog.utils.ParamUtils; -import org.opencb.opencga.catalog.utils.UuidUtils; +import org.opencb.opencga.catalog.utils.*; import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.common.JacksonUtils; import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.Configuration; import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.AclParams; import org.opencb.opencga.core.models.audit.AuditRecord; import org.opencb.opencga.core.models.clinical.*; +import org.opencb.opencga.core.models.common.AnnotationSet; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.common.FlagAnnotation; import org.opencb.opencga.core.models.common.FlagValue; @@ -56,6 +59,7 @@ import org.opencb.opencga.core.models.sample.Sample; import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.study.StudyPermissions; +import org.opencb.opencga.core.models.study.VariableSet; import org.opencb.opencga.core.models.study.configuration.ClinicalConsent; import org.opencb.opencga.core.models.study.configuration.*; import org.opencb.opencga.core.models.user.User; @@ -64,6 +68,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; +import java.io.IOException; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -75,7 +80,7 @@ /** * Created by pfurio on 05/06/17. */ -public class ClinicalAnalysisManager extends ResourceManager { +public class ClinicalAnalysisManager extends AnnotationSetManager { public static final QueryOptions INCLUDE_CLINICAL_IDS = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList( ClinicalAnalysisDBAdaptor.QueryParams.ID.key(), ClinicalAnalysisDBAdaptor.QueryParams.UID.key(), @@ -204,9 +209,10 @@ public DBIterator iterator(String studyStr, Query query, Query options = ParamUtils.defaultObject(options, QueryOptions::new); String userId = catalogManager.getUserManager().getUserId(sessionId); - Study study = catalogManager.getStudyManager().resolveId(studyStr, userId); + Study study = catalogManager.getStudyManager().resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET); fixQueryObject(study, query, userId, sessionId); + AnnotationUtils.fixQueryOptionAnnotation(options); query.append(ClinicalAnalysisDBAdaptor.QueryParams.STUDY_UID.key(), study.getUid()); return clinicalDBAdaptor.iterator(study.getUid(), query, options, userId); @@ -222,7 +228,7 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis Boolean skipCreateDefaultInterpretation, QueryOptions options, String token) throws CatalogException { String userId = catalogManager.getUserManager().getUserId(token); - Study study = catalogManager.getStudyManager().resolveId(studyStr, userId, StudyManager.INCLUDE_CONFIGURATION); + Study study = catalogManager.getStudyManager().resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET_AND_CONFIGURATION); ObjectMap auditParams = new ObjectMap() .append("study", studyStr) @@ -258,6 +264,26 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis clinicalAnalysis.setQualityControl(ParamUtils.defaultObject(clinicalAnalysis.getQualityControl(), ClinicalAnalysisQualityControl::new)); clinicalAnalysis.setPanels(ParamUtils.defaultObject(clinicalAnalysis.getPanels(), Collections.emptyList())); + clinicalAnalysis.setAnnotationSets(ParamUtils.defaultObject(clinicalAnalysis.getAnnotationSets(), Collections.emptyList())); + clinicalAnalysis.setResponsible(ParamUtils.defaultObject(clinicalAnalysis.getResponsible(), ClinicalResponsible::new)); + clinicalAnalysis.setRequest(ParamUtils.defaultObject(clinicalAnalysis.getRequest(), ClinicalRequest::new)); + + // ---------- Check and init report fields + validateAndInitReport(study, clinicalAnalysis.getReport(), userId); + + // ---------- Check and init responsible fields + ClinicalResponsible responsible = clinicalAnalysis.getResponsible(); + if (StringUtils.isEmpty(responsible.getId())) { + responsible.setId(userId); + } + fillResponsible(responsible); + + // ---------- Check and init request fields + ClinicalRequest request = clinicalAnalysis.getRequest(); + if (StringUtils.isNotEmpty(request.getId())) { + request.setDate(ParamUtils.checkDateOrGetCurrentDate(request.getDate(), "request.date")); + fillResponsible(request.getResponsible()); + } if (clinicalAnalysis.getQualityControl().getComments() != null) { for (ClinicalComment comment : clinicalAnalysis.getQualityControl().getComments()) { @@ -291,20 +317,29 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis } // Analyst + List userList; QueryOptions userInclude = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList(UserDBAdaptor.QueryParams.ID.key(), UserDBAdaptor.QueryParams.NAME.key(), UserDBAdaptor.QueryParams.EMAIL.key())); - User user; - if (clinicalAnalysis.getAnalyst() == null || StringUtils.isEmpty(clinicalAnalysis.getAnalyst().getId())) { - user = userDBAdaptor.get(userId, userInclude).first(); + if (clinicalAnalysis.getAnalysts() == null) { + userList = userDBAdaptor.get(userId, userInclude).getResults(); } else { - // Validate user - OpenCGAResult result = userDBAdaptor.get(clinicalAnalysis.getAnalyst().getId(), userInclude); - if (result.getNumResults() == 0) { - throw new CatalogException("User '" + clinicalAnalysis.getAnalyst().getId() + "' not found"); + // Validate users + Set userIds = new HashSet<>(); + for (ClinicalAnalyst analyst : clinicalAnalysis.getAnalysts()) { + userIds.add(analyst.getId()); + } + Query query = new Query(UserDBAdaptor.QueryParams.ID.key(), userIds); + OpenCGAResult result = userDBAdaptor.get(query, userInclude); + if (result.getNumResults() < userIds.size()) { + throw new CatalogException("Some clinical analysts could not be found."); } - user = result.first(); + userList = result.getResults(); } - clinicalAnalysis.setAnalyst(new ClinicalAnalyst(user.getId(), user.getName(), user.getEmail(), userId, TimeUtils.getTime())); + List clinicalAnalystList = new ArrayList<>(userList.size()); + for (User user : userList) { + clinicalAnalystList.add(new ClinicalAnalyst(user.getId(), user.getName(), user.getEmail(), userId, Collections.emptyMap())); + } + clinicalAnalysis.setAnalysts(clinicalAnalystList); if (TimeUtils.toDate(clinicalAnalysis.getDueDate()) == null) { throw new CatalogException("Unrecognised due date. Accepted format is: yyyyMMddHHmmss"); @@ -501,10 +536,17 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis } } } + List files = obtainFiles(study, clinicalAnalysis, userId); if (clinicalAnalysis.getFiles() != null && !clinicalAnalysis.getFiles().isEmpty()) { - validateFiles(study, clinicalAnalysis, userId); + Set fileIds = clinicalAnalysis.getFiles().stream().map(File::getId).collect(Collectors.toSet()); + String notFoundFiles = files.stream().map(File::getId).filter(f -> !fileIds.contains(f)).collect(Collectors.joining(", ")); + if (StringUtils.isNotEmpty(notFoundFiles)) { + throw new CatalogException("Files '" + notFoundFiles + "' not found or do not belong to any participant."); + } + List filteredFiles = files.stream().filter(f -> fileIds.contains(f.getId())).collect(Collectors.toList()); + clinicalAnalysis.setFiles(filteredFiles); } else { - obtainFiles(study, clinicalAnalysis, userId); + clinicalAnalysis.setFiles(files); } clinicalAnalysis.setCreationDate(ParamUtils.checkDateOrGetCurrentDate(clinicalAnalysis.getCreationDate(), @@ -561,7 +603,8 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis clinicalAuditList.add(new ClinicalAudit(userId, ClinicalAudit.Action.CREATE_CLINICAL_ANALYSIS, "Create ClinicalAnalysis '" + clinicalAnalysis.getId() + "'", TimeUtils.getTime())); - OpenCGAResult insert = clinicalDBAdaptor.insert(study.getUid(), clinicalAnalysis, clinicalAuditList, options); + OpenCGAResult insert = clinicalDBAdaptor.insert(study.getUid(), clinicalAnalysis, study.getVariableSets(), + clinicalAuditList, options); insert.addEvents(events); auditManager.auditCreate(userId, Enums.Resource.CLINICAL_ANALYSIS, clinicalAnalysis.getId(), clinicalAnalysis.getUuid(), @@ -582,6 +625,43 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis } } + private void validateAndInitReport(Study study, ClinicalReport report, String userId) throws CatalogException { + if (report == null) { + return; + } + if (StringUtils.isNotEmpty(report.getTitle()) || StringUtils.isNotEmpty(report.getOverview())) { + report.setDate(ParamUtils.checkDateOrGetCurrentDate(report.getDate(), "report.date")); + } + if (report.getComments() != null) { + for (ClinicalComment comment : report.getComments()) { + comment.setDate(TimeUtils.getTime()); + comment.setAuthor(userId); + } + } + if (CollectionUtils.isNotEmpty(report.getFiles())) { + List files = obtainFiles(study, userId, report.getFiles()); + report.setFiles(files); + } + if (CollectionUtils.isNotEmpty(report.getSupportingEvidences())) { + List files = obtainFiles(study, userId, report.getSupportingEvidences()); + report.setSupportingEvidences(files); + } + } + + private void fillResponsible(ClinicalResponsible responsible) throws CatalogException { + if (responsible == null) { + return; + } + QueryOptions userInclude = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList(UserDBAdaptor.QueryParams.ID.key(), + UserDBAdaptor.QueryParams.NAME.key(), UserDBAdaptor.QueryParams.EMAIL.key())); + OpenCGAResult result = userDBAdaptor.get(responsible.getId(), userInclude); + if (result.getNumResults() == 0) { + throw new CatalogException("Responsible user '" + responsible.getId() + "' not found."); + } + responsible.setName(ParamUtils.defaultString(responsible.getName(), result.first().getName())); + responsible.setEmail(ParamUtils.defaultString(responsible.getEmail(), result.first().getEmail())); + } + private void validateStatusParameter(ClinicalAnalysis clinicalAnalysis, ClinicalAnalysisStudyConfiguration clinicalConfiguration) throws CatalogException { // Status @@ -747,7 +827,7 @@ private void validateDisorder(ClinicalAnalysis clinicalAnalysis) throws CatalogE } } - private void obtainFiles(Study study, ClinicalAnalysis clinicalAnalysis, String userId) throws CatalogException { + private List obtainFiles(Study study, ClinicalAnalysis clinicalAnalysis, String userId) throws CatalogException { Set sampleSet = new HashSet<>(); if (clinicalAnalysis.getFamily() != null && clinicalAnalysis.getFamily().getMembers() != null) { for (Individual member : clinicalAnalysis.getFamily().getMembers()) { @@ -772,8 +852,18 @@ private void obtainFiles(Study study, ClinicalAnalysis clinicalAnalysis, String .append(FileDBAdaptor.QueryParams.SAMPLE_IDS.key(), new ArrayList<>(sampleSet)) .append(FileDBAdaptor.QueryParams.BIOFORMAT.key(), Arrays.asList(File.Bioformat.ALIGNMENT, File.Bioformat.VARIANT)); OpenCGAResult fileResults = fileDBAdaptor.get(study.getUid(), query, FileManager.INCLUDE_FILE_URI_PATH, userId); - clinicalAnalysis.setFiles(fileResults.getResults()); + return fileResults.getResults(); } + return Collections.emptyList(); + } + + private List obtainFiles(Study study, String userId, List files) throws CatalogException { + Query query = new Query(FileDBAdaptor.QueryParams.ID.key(), files.stream().map(File::getId).collect(Collectors.toSet())); + List results = fileDBAdaptor.get(study.getUid(), query, FileManager.INCLUDE_FILE_URI_PATH, userId).getResults(); + if (results.size() < files.size()) { + throw new CatalogException("Some of the files were not found"); + } + return results; } private void validateFiles(Study study, ClinicalAnalysis clinicalAnalysis, String userId) throws CatalogException { @@ -844,6 +934,103 @@ private void validateFiles(Study study, ClinicalAnalysis clinicalAnalysis, Strin // } } +// private void obtainFiles(Study study, ClinicalAnalysis clinicalAnalysis, String userId) throws CatalogException { +// Set sampleSet = new HashSet<>(); +// if (clinicalAnalysis.getFamily() != null && clinicalAnalysis.getFamily().getMembers() != null) { +// for (Individual member : clinicalAnalysis.getFamily().getMembers()) { +// if (member.getSamples() != null) { +// for (Sample sample : member.getSamples()) { +// sampleSet.add(sample.getId()); +// } +// } +// } +// } else if (clinicalAnalysis.getProband() != null && clinicalAnalysis.getProband().getSamples() != null) { +// for (Sample sample : clinicalAnalysis.getProband().getSamples()) { +// sampleSet.add(sample.getId()); +// } +// } +// +// if (clinicalAnalysis.getFiles() != null && !clinicalAnalysis.getFiles().isEmpty()) { +// throw new CatalogException("Cannot obtain map of files if this is already provided"); +// } +// +// if (!sampleSet.isEmpty()) { +// Query query = new Query() +// .append(FileDBAdaptor.QueryParams.SAMPLE_IDS.key(), new ArrayList<>(sampleSet)) +// .append(FileDBAdaptor.QueryParams.BIOFORMAT.key(), Arrays.asList(File.Bioformat.ALIGNMENT, File.Bioformat.VARIANT)); +// OpenCGAResult fileResults = fileDBAdaptor.get(study.getUid(), query, FileManager.INCLUDE_FILE_URI_PATH, userId); +// clinicalAnalysis.setFiles(fileResults.getResults()); +// } +// } + +// private void validateFiles(Study study, ClinicalAnalysis clinicalAnalysis, String userId) throws CatalogException { +// Map sampleMap = new HashMap<>(); +// if (clinicalAnalysis.getFamily() != null && clinicalAnalysis.getFamily().getMembers() != null) { +// for (Individual member : clinicalAnalysis.getFamily().getMembers()) { +// if (member.getSamples() != null) { +// for (Sample sample : member.getSamples()) { +// sampleMap.put(sample.getId(), sample.getUid()); +// } +// } +// } +// } else if (clinicalAnalysis.getProband() != null && clinicalAnalysis.getProband().getSamples() != null) { +// for (Sample sample : clinicalAnalysis.getProband().getSamples()) { +// sampleMap.put(sample.getId(), sample.getUid()); +// } +// } +// +// if (clinicalAnalysis.getFiles() == null || clinicalAnalysis.getFiles().isEmpty()) { +// throw new CatalogException("Found empty map of files"); +// } +// +// // Look for all the samples associated to the files +// Query query = new Query(FileDBAdaptor.QueryParams.ID.key(), +// clinicalAnalysis.getFiles().stream().map(File::getId).collect(Collectors.toList())); +// QueryOptions fileOptions = keepFieldInQueryOptions(FileManager.INCLUDE_FILE_URI_PATH, FileDBAdaptor.QueryParams.SAMPLE_IDS.key()); +// OpenCGAResult fileResults = fileDBAdaptor.get(study.getUid(), query, fileOptions, userId); +// +// if (fileResults.getNumResults() != clinicalAnalysis.getFiles().size()) { +// Set fileIds = clinicalAnalysis.getFiles().stream().map(File::getId).collect(Collectors.toSet()); +// String notFoundFiles = fileResults.getResults().stream().map(File::getId).filter(f -> !fileIds.contains(f)) +// .collect(Collectors.joining(", ")); +// throw new CatalogException("Files '" + notFoundFiles + "' not found"); +// } +// +// // Complete file information +// clinicalAnalysis.setFiles(fileResults.getResults()); +// +// // Validate the file ids passed are related to the samples +// for (File file : clinicalAnalysis.getFiles()) { +// if (CollectionUtils.isNotEmpty(file.getSampleIds())) { +// boolean found = false; +// for (String sampleId : file.getSampleIds()) { +// if (sampleMap.containsKey(sampleId)) { +// found = true; +// break; +// } +// } +// if (!found) { +// throw new CatalogException("Clinical analysis file (" + file.getId() + ") contains sample ids not related to any " +// + "member/proband"); +// } +// } +// } +// +//// for (File caFile : clinicalAnalysis.getFiles()) { +//// List fileIds = caFile.getFiles().stream().map(File::getId).collect(Collectors.toList()); +//// InternalGetDataResult fileResult = catalogManager.getFileManager().internalGet(study.getUid(), fileIds, new Query(), +//// new QueryOptions(), userId, false); +//// // Validate sample id belongs to files +//// for (File file : fileResult.getResults()) { +//// if (!file.getSamples().stream().map(Sample::getUid).collect(Collectors.toSet()) +//// .contains(sampleMap.get(caFile.getSampleId()))) { +//// throw new CatalogException("Associated file '" + file.getPath() + "' seems not to be related to sample '" +//// + caFile.getSampleId() + "'."); +//// } +//// } +//// } +// } + private Family getFullValidatedFamily(Family family, Study study, String sessionId) throws CatalogException { if (family == null) { return null; @@ -966,7 +1153,7 @@ public OpenCGAResult update(String studyStr, Query query, Clin public OpenCGAResult update(String studyStr, Query query, ClinicalAnalysisUpdateParams updateParams, boolean ignoreException, QueryOptions options, String token) throws CatalogException { String userId = userManager.getUserId(token); - Study study = studyManager.resolveId(studyStr, userId, StudyManager.INCLUDE_CONFIGURATION); + Study study = studyManager.resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET_AND_CONFIGURATION); String operationId = UuidUtils.generateOpenCgaUuid(UuidUtils.Entity.AUDIT); @@ -988,6 +1175,7 @@ public OpenCGAResult update(String studyStr, Query query, Clin DBIterator iterator; try { fixQueryObject(study, query, userId, token); + AnnotationUtils.fixQueryOptionAnnotation(options); query.append(ClinicalAnalysisDBAdaptor.QueryParams.STUDY_UID.key(), study.getUid()); iterator = clinicalDBAdaptor.iterator(study.getUid(), query, new QueryOptions(), userId); } catch (CatalogException e) { @@ -1026,7 +1214,7 @@ public OpenCGAResult update(String studyStr, Query query, Clin public OpenCGAResult update(String studyStr, String clinicalId, ClinicalAnalysisUpdateParams updateParams, QueryOptions options, String token) throws CatalogException { String userId = userManager.getUserId(token); - Study study = studyManager.resolveId(studyStr, userId, StudyManager.INCLUDE_CONFIGURATION); + Study study = studyManager.resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET_AND_CONFIGURATION); String operationId = UuidUtils.generateOpenCgaUuid(UuidUtils.Entity.AUDIT); @@ -1097,7 +1285,7 @@ public OpenCGAResult update(String studyStr, List clin public OpenCGAResult update(String studyStr, List clinicalIds, ClinicalAnalysisUpdateParams updateParams, boolean ignoreException, QueryOptions options, String token) throws CatalogException { String userId = userManager.getUserId(token); - Study study = studyManager.resolveId(studyStr, userId, StudyManager.INCLUDE_CONFIGURATION); + Study study = studyManager.resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET_AND_CONFIGURATION); String operationId = UuidUtils.generateOpenCgaUuid(UuidUtils.Entity.AUDIT); @@ -1157,6 +1345,39 @@ public OpenCGAResult update(String studyStr, List clin private OpenCGAResult update(Study study, ClinicalAnalysis clinicalAnalysis, ClinicalAnalysisUpdateParams updateParams, String userId, QueryOptions options) throws CatalogException { + ClinicalAnalysisUpdateParams updateParamsClone; + try { + updateParamsClone = JacksonUtils.copy(updateParams, ClinicalAnalysisUpdateParams.class); + } catch (IOException e) { + throw new CatalogException("Could not clone ClinicalAnalysisUpdateParams object"); + } + if (updateParamsClone == null) { + throw new CatalogException("Empty update parameters. Nothing to update."); + } + + validateAndInitReport(study, updateParamsClone.getReport(), userId); + + // ---------- Check and init responsible fields + ClinicalResponsible responsible = updateParamsClone.getResponsible(); + if (responsible != null && StringUtils.isNotEmpty(responsible.getId())) { + fillResponsible(responsible); + } + + // ---------- Check and init request fields + ClinicalRequest request = updateParamsClone.getRequest(); + if (request != null && StringUtils.isNotEmpty(request.getId())) { + request.setDate(ParamUtils.checkDateOrGetCurrentDate(request.getDate(), "request.date")); + fillResponsible(request.getResponsible()); + } + + ObjectMap parameters; + try { + parameters = updateParamsClone.getUpdateMap(); + } catch (JsonProcessingException e) { + throw new CatalogException("Could not parse ClinicalUpdateParams object: " + e.getMessage(), e); + } + ParamUtils.checkUpdateParametersMap(parameters); + options = ParamUtils.defaultObject(options, QueryOptions::new); if (study.getInternal() == null || study.getInternal().getConfiguration() == null || study.getInternal().getConfiguration().getClinical() == null) { @@ -1164,10 +1385,30 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli } ClinicalAnalysisStudyConfiguration clinicalConfiguration = study.getInternal().getConfiguration().getClinical(); - authorizationManager.checkClinicalAnalysisPermission(study.getUid(), clinicalAnalysis.getUid(), userId, - ClinicalAnalysisPermissions.WRITE); - List events = new LinkedList<>(); + // Check permissions... + // Only check write annotation permissions if the user wants to update the annotation sets + if (updateParamsClone.getAnnotationSets() != null) { + authorizationManager.checkClinicalAnalysisPermission(study.getUid(), clinicalAnalysis.getUid(), userId, + ClinicalAnalysisPermissions.WRITE_ANNOTATIONS); + } + // Only check update permissions if the user wants to update anything apart from the annotation sets + if ((parameters.size() == 1 && !parameters.containsKey(SampleDBAdaptor.QueryParams.ANNOTATION_SETS.key())) + || parameters.size() > 1) { + authorizationManager.checkClinicalAnalysisPermission(study.getUid(), clinicalAnalysis.getUid(), userId, + ClinicalAnalysisPermissions.WRITE); + } + + if (parameters.containsKey(ClinicalAnalysisDBAdaptor.QueryParams.ANNOTATION_SETS.key())) { + Map actionMap = options.getMap(Constants.ACTIONS, new HashMap<>()); + if (!actionMap.containsKey(AnnotationSetManager.ANNOTATION_SETS) + && !actionMap.containsKey(AnnotationSetManager.ANNOTATIONS)) { + logger.warn("Assuming the user wants to add the list of annotation sets provided"); + actionMap.put(AnnotationSetManager.ANNOTATION_SETS, ParamUtils.BasicUpdateAction.ADD); + options.put(Constants.ACTIONS, actionMap); + } + } + List events = new LinkedList<>(); if (StringUtils.isNotEmpty(clinicalAnalysis.getCreationDate())) { ParamUtils.checkDateFormat(clinicalAnalysis.getCreationDate(), ClinicalAnalysisDBAdaptor.QueryParams.CREATION_DATE.key()); } @@ -1176,29 +1417,17 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli ClinicalAnalysisDBAdaptor.QueryParams.MODIFICATION_DATE.key()); } - ObjectMap parameters; - if (updateParams != null) { - try { - parameters = updateParams.getUpdateMap(); - } catch (JsonProcessingException e) { - throw new CatalogException("Could not parse ClinicalUpdateParams object: " + e.getMessage(), e); - } - } else { - throw new CatalogException("Empty update parameters. Nothing to update."); - } - ParamUtils.checkUpdateParametersMap(parameters); - Map actionMap = options.getMap(Constants.ACTIONS); - if (updateParams.getId() != null) { - ParamUtils.checkIdentifier(updateParams.getId(), ClinicalAnalysisDBAdaptor.QueryParams.ID.key()); + if (updateParamsClone.getId() != null) { + ParamUtils.checkIdentifier(updateParamsClone.getId(), ClinicalAnalysisDBAdaptor.QueryParams.ID.key()); } - if (StringUtils.isNotEmpty(updateParams.getDueDate()) && TimeUtils.toDate(updateParams.getDueDate()) == null) { + if (StringUtils.isNotEmpty(updateParamsClone.getDueDate()) && TimeUtils.toDate(updateParamsClone.getDueDate()) == null) { throw new CatalogException("Unrecognised due date. Accepted format is: yyyyMMddHHmmss"); } - if (updateParams.getComments() != null && !updateParams.getComments().isEmpty()) { - List comments = new ArrayList<>(updateParams.getComments().size()); + if (updateParamsClone.getComments() != null && !updateParamsClone.getComments().isEmpty()) { + List comments = new ArrayList<>(updateParamsClone.getComments().size()); ParamUtils.AddRemoveReplaceAction action = ParamUtils.AddRemoveReplaceAction.from(actionMap, ClinicalAnalysisDBAdaptor.QueryParams.COMMENTS.key(), ParamUtils.AddRemoveReplaceAction.ADD); @@ -1207,7 +1436,7 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli case ADD: // Ensure each comment has a different milisecond Calendar calendar = Calendar.getInstance(); - for (ClinicalCommentParam comment : updateParams.getComments()) { + for (ClinicalCommentParam comment : updateParamsClone.getComments()) { comments.add(new ClinicalComment(userId, comment.getMessage(), comment.getTags(), TimeUtils.getTimeMillis(calendar.getTime()))); calendar.add(Calendar.MILLISECOND, 1); @@ -1215,7 +1444,7 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli break; case REMOVE: case REPLACE: - for (ClinicalCommentParam comment : updateParams.getComments()) { + for (ClinicalCommentParam comment : updateParamsClone.getComments()) { if (StringUtils.isEmpty(comment.getDate())) { throw new CatalogException("Missing mandatory 'date' field. This field is mandatory when action is '" + action + "'."); @@ -1230,26 +1459,48 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.COMMENTS.key(), comments); } - if (parameters.get(InterpretationDBAdaptor.QueryParams.ANALYST.key()) != null) { - if (StringUtils.isNotEmpty(updateParams.getAnalyst().getId())) { - QueryOptions userOptions = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList(UserDBAdaptor.QueryParams.ID.key(), - UserDBAdaptor.QueryParams.NAME.key(), UserDBAdaptor.QueryParams.EMAIL.key())); - // Check user exists - OpenCGAResult userResult = userDBAdaptor.get(updateParams.getAnalyst().getId(), userOptions); - if (userResult.getNumResults() == 0) { - throw new CatalogException("User '" + updateParams.getAnalyst().getId() + "' not found"); - } + if (parameters.get(ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS.key()) != null) { + ParamUtils.BasicUpdateAction action = ParamUtils.BasicUpdateAction.from(actionMap, + ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS.key(), ParamUtils.BasicUpdateAction.ADD); + List analystList = updateParamsClone.getAnalysts(); + switch (action) { + case ADD: + case SET: + QueryOptions userOptions = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList(UserDBAdaptor.QueryParams.ID.key(), + UserDBAdaptor.QueryParams.NAME.key(), UserDBAdaptor.QueryParams.EMAIL.key())); - parameters.put(InterpretationDBAdaptor.QueryParams.ANALYST.key(), new ClinicalAnalyst(userResult.first().getId(), - userResult.first().getName(), userResult.first().getEmail(), userId, TimeUtils.getTime())); - } else { - // Remove assignee - parameters.put(InterpretationDBAdaptor.QueryParams.ANALYST.key(), new ClinicalAnalyst("", "", "", userId, - TimeUtils.getTime())); + Set analystIdList = new HashSet<>(); + for (ClinicalAnalystParam clinicalAnalystParam : analystList) { + analystIdList.add(clinicalAnalystParam.getId()); + } + + List clinicalAnalystList = new ArrayList<>(analystIdList.size()); + // Check analysts exist + if (!analystIdList.isEmpty()) { + Query query = new Query(UserDBAdaptor.QueryParams.ID.key(), analystIdList); + OpenCGAResult userResult = userDBAdaptor.get(query, userOptions); + if (userResult.getNumResults() < analystIdList.size()) { + throw new CatalogException("Some analysts were not found."); + } + for (User user : userResult.getResults()) { + clinicalAnalystList.add(new ClinicalAnalyst(user.getId(), user.getName(), user.getEmail(), userId, + Collections.emptyMap())); + } + } + parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS.key(), clinicalAnalystList); + break; + case REMOVE: + // Directly add those analysts. No need to check + List analysts = analystList.stream().map(ClinicalAnalystParam::toClinicalAnalyst) + .collect(Collectors.toList()); + parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS.key(), analysts); + break; + default: + throw new IllegalStateException("Unknown analysts action " + action); } } if (parameters.get(ClinicalAnalysisDBAdaptor.QueryParams.QUALITY_CONTROL.key()) != null) { - ClinicalAnalysisQualityControl qualityControl = updateParams.getQualityControl().toClinicalQualityControl(); + ClinicalAnalysisQualityControl qualityControl = updateParamsClone.getQualityControl().toClinicalQualityControl(); if (qualityControl.getComments() != null) { for (ClinicalComment comment : qualityControl.getComments()) { comment.setDate(TimeUtils.getTime()); @@ -1259,27 +1510,36 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.QUALITY_CONTROL.key(), qualityControl); } - if (updateParams.getFiles() != null && !updateParams.getFiles().isEmpty()) { - clinicalAnalysis.setFiles(updateParams.getFiles().stream().map(FileReferenceParam::toFile).collect(Collectors.toList())); + if (updateParamsClone.getReport() != null && CollectionUtils.isNotEmpty(updateParamsClone.getReport().getFiles())) { + parameters.putNested(ClinicalAnalysisDBAdaptor.QueryParams.REPORT_FILES.key(), updateParamsClone.getReport().getFiles(), false); + } + if (updateParamsClone.getReport() != null && CollectionUtils.isNotEmpty(updateParamsClone.getReport().getSupportingEvidences())) { + parameters.putNested(ClinicalAnalysisDBAdaptor.QueryParams.REPORT_SUPPORTING_EVIDENCES.key(), + updateParamsClone.getReport().getSupportingEvidences(), false); + } + if (updateParamsClone.getFiles() != null && !updateParamsClone.getFiles().isEmpty()) { + clinicalAnalysis.setFiles(updateParamsClone.getFiles().stream().map(FileReferenceParam::toFile).collect(Collectors.toList())); // Validate files validateFiles(study, clinicalAnalysis, userId); + parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.FILES.key(), clinicalAnalysis.getFiles()); } - if (CollectionUtils.isNotEmpty(updateParams.getPanels()) && updateParams.getPanelLock() != null && updateParams.getPanelLock()) { + if (CollectionUtils.isNotEmpty(updateParamsClone.getPanels()) && updateParamsClone.getPanelLock() != null + && updateParamsClone.getPanelLock()) { throw new CatalogException("Updating the list of panels and setting 'panelLock' to true at the same time is not allowed."); } - if (CollectionUtils.isNotEmpty(updateParams.getPanels())) { - if (clinicalAnalysis.isPanelLock() && (updateParams.getPanelLock() == null || updateParams.getPanelLock())) { + if (CollectionUtils.isNotEmpty(updateParamsClone.getPanels())) { + if (clinicalAnalysis.isPanelLock() && (updateParamsClone.getPanelLock() == null || updateParamsClone.getPanelLock())) { throw new CatalogException("Cannot update panels from ClinicalAnalysis '" + clinicalAnalysis.getId() + "'. " + "'panelLocked' field from ClinicalAnalysis is set to true."); } // Validate and get panels - List panelIds = updateParams.getPanels().stream().map(PanelReferenceParam::getId).collect(Collectors.toList()); + List panelIds = updateParamsClone.getPanels().stream().map(PanelReferenceParam::getId).collect(Collectors.toList()); Query query = new Query(PanelDBAdaptor.QueryParams.ID.key(), panelIds); - OpenCGAResult panelResult = + OpenCGAResult panelResult = panelDBAdaptor.get(study.getUid(), query, PanelManager.INCLUDE_PANEL_IDS, userId); if (panelResult.getNumResults() < panelIds.size()) { throw new CatalogException("Some panels were not found or user doesn't have permissions to see them."); @@ -1288,7 +1548,7 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.PANELS.key(), panelResult.getResults()); } - if (updateParams.getPanelLock() != null && updateParams.getPanelLock() && !clinicalAnalysis.isPanelLock()) { + if (updateParamsClone.getPanelLock() != null && updateParamsClone.getPanelLock() && !clinicalAnalysis.isPanelLock()) { // if user wants to set panelLock to true // We need to check if the CA has interpretations. If so, the interpretations should contain at least one of the case panels // in order to set panelLock to true. Otherwise, that action is not allowed. @@ -1323,17 +1583,13 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli } } - if (updateParams.getFiles() != null && !updateParams.getFiles().isEmpty()) { - parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.FILES.key(), clinicalAnalysis.getFiles()); - } - - if (CollectionUtils.isNotEmpty(updateParams.getPanels())) { + if (CollectionUtils.isNotEmpty(updateParamsClone.getPanels())) { // Get panels Query query = new Query(PanelDBAdaptor.QueryParams.ID.key(), - updateParams.getPanels().stream().map(PanelReferenceParam::getId).collect(Collectors.toList())); - OpenCGAResult panelResult = + updateParamsClone.getPanels().stream().map(PanelReferenceParam::getId).collect(Collectors.toList())); + OpenCGAResult panelResult = panelDBAdaptor.get(study.getUid(), query, PanelManager.INCLUDE_PANEL_IDS, userId); - if (panelResult.getNumResults() < updateParams.getPanels().size()) { + if (panelResult.getNumResults() < updateParamsClone.getPanels().size()) { throw new CatalogException("Some panels were not found or user doesn't have permissions to see them"); } @@ -1342,7 +1598,7 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli if (parameters.containsKey(ClinicalAnalysisDBAdaptor.QueryParams.DISORDER.key())) { // Assign the disorder to be updated to the clinicalAnalysis obtained from the DB so it can be checked in context - clinicalAnalysis.setDisorder(updateParams.getDisorder().toDisorder()); + clinicalAnalysis.setDisorder(updateParamsClone.getDisorder().toDisorder()); validateDisorder(clinicalAnalysis); // Fill parameter to be updated with complete disorder information parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.DISORDER.key(), clinicalAnalysis.getDisorder()); @@ -1358,7 +1614,7 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli // Validate user-defined parameters if (parameters.containsKey(ClinicalAnalysisDBAdaptor.QueryParams.PRIORITY.key())) { - clinicalAnalysis.setPriority(updateParams.getPriority().toClinicalPriorityAnnotation()); + clinicalAnalysis.setPriority(updateParamsClone.getPriority().toClinicalPriorityAnnotation()); validateCustomPriorityParameters(clinicalAnalysis, clinicalConfiguration); parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.PRIORITY.key(), clinicalAnalysis.getPriority()); } @@ -1368,7 +1624,8 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli currentFlags = clinicalAnalysis.getFlags().stream().map(FlagAnnotation::getId).collect(Collectors.toSet()); } - clinicalAnalysis.setFlags(updateParams.getFlags().stream().map(FlagValueParam::toFlagAnnotation).collect(Collectors.toList())); + clinicalAnalysis.setFlags(updateParamsClone.getFlags().stream().map(FlagValueParam::toFlagAnnotation) + .collect(Collectors.toList())); validateCustomFlagParameters(clinicalAnalysis, clinicalConfiguration); ParamUtils.BasicUpdateAction action = ParamUtils.BasicUpdateAction.from(actionMap, @@ -1389,22 +1646,22 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.FLAGS.key(), clinicalAnalysis.getFlags()); } if (parameters.containsKey(ClinicalAnalysisDBAdaptor.QueryParams.CONSENT.key())) { - clinicalAnalysis.setConsent(updateParams.getConsent().toClinicalConsentAnnotation()); + clinicalAnalysis.setConsent(updateParamsClone.getConsent().toClinicalConsentAnnotation()); validateCustomConsentParameters(clinicalAnalysis, clinicalConfiguration); parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.CONSENT.key(), clinicalAnalysis.getConsent()); } if (parameters.containsKey(ClinicalAnalysisDBAdaptor.QueryParams.STATUS.key())) { - clinicalAnalysis.setStatus(updateParams.getStatus().toStatus()); + clinicalAnalysis.setStatus(updateParamsClone.getStatus().toStatus()); validateStatusParameter(clinicalAnalysis, clinicalConfiguration); parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.STATUS.key(), clinicalAnalysis.getStatus()); - if (StringUtils.isNotEmpty(updateParams.getStatus().getId())) { + if (StringUtils.isNotEmpty(updateParamsClone.getStatus().getId())) { List clinicalStatusValues = clinicalConfiguration.getStatus().get(clinicalAnalysis.getType()); for (ClinicalStatusValue clinicalStatusValue : clinicalStatusValues) { - if (updateParams.getStatus().getId().equals(clinicalStatusValue.getId())) { + if (updateParamsClone.getStatus().getId().equals(clinicalStatusValue.getId())) { if (clinicalStatusValue.getType() == ClinicalStatusValue.ClinicalStatusType.CLOSED) { String msg = "User '" + userId + "' changed case '" + clinicalAnalysis.getId() + "' to status '" - + updateParams.getStatus().getId() + "', which is of type CLOSED. Automatically locking " + + updateParamsClone.getStatus().getId() + "', which is of type CLOSED. Automatically locking " + "ClinicalAnalysis"; logger.info(msg); parameters.put(ClinicalAnalysisDBAdaptor.QueryParams.LOCKED.key(), true); @@ -1415,9 +1672,11 @@ private OpenCGAResult update(Study study, ClinicalAnalysis cli } } + checkUpdateAnnotations(study, clinicalAnalysis, parameters, options, VariableSet.AnnotableDataModels.CLINICAL_ANALYSIS, + clinicalDBAdaptor, userId); ClinicalAudit clinicalAudit = new ClinicalAudit(userId, ClinicalAudit.Action.UPDATE_CLINICAL_ANALYSIS, "Update ClinicalAnalysis '" + clinicalAnalysis.getId() + "'", TimeUtils.getTime()); - OpenCGAResult update = clinicalDBAdaptor.update(clinicalAnalysis.getUid(), parameters, + OpenCGAResult update = clinicalDBAdaptor.update(clinicalAnalysis.getUid(), parameters, study.getVariableSets(), Collections.singletonList(clinicalAudit), options); update.addEvents(events); if (options.getBoolean(ParamConstants.INCLUDE_RESULT_PARAM)) { @@ -1494,9 +1753,11 @@ public OpenCGAResult search(String studyId, Query query, Query options = ParamUtils.defaultObject(options, QueryOptions::new); String userId = catalogManager.getUserManager().getUserId(token); - Study study = catalogManager.getStudyManager().resolveId(studyId, userId); + Study study = catalogManager.getStudyManager().resolveId(studyId, userId, StudyManager.INCLUDE_VARIABLE_SET); fixQueryObject(study, query, userId, token); + AnnotationUtils.fixQueryOptionAnnotation(options); + query.append(ClinicalAnalysisDBAdaptor.QueryParams.STUDY_UID.key(), study.getUid()); return clinicalDBAdaptor.get(study.getUid(), query, options, userId); @@ -1507,7 +1768,7 @@ public OpenCGAResult distinct(String studyId, List fields, Query quer query = ParamUtils.defaultObject(query, Query::new); String userId = userManager.getUserId(token); - Study study = catalogManager.getStudyManager().resolveId(studyId, userId); + Study study = catalogManager.getStudyManager().resolveId(studyId, userId, StudyManager.INCLUDE_VARIABLE_SET); ObjectMap auditParams = new ObjectMap() .append("studyId", studyId) @@ -1532,8 +1793,11 @@ public OpenCGAResult distinct(String studyId, List fields, Query quer } protected void fixQueryObject(Study study, Query query, String user, String token) throws CatalogException { + // Fix query if it contains any annotation + AnnotationUtils.fixQueryAnnotationSearch(study, query); + changeQueryId(query, ParamConstants.CLINICAL_DISORDER_PARAM, ClinicalAnalysisDBAdaptor.QueryParams.DISORDER.key()); - changeQueryId(query, ParamConstants.CLINICAL_ANALYST_ID_PARAM, ClinicalAnalysisDBAdaptor.QueryParams.ANALYST_ID.key()); + changeQueryId(query, ParamConstants.CLINICAL_ANALYST_ID_PARAM, ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS_ID.key()); changeQueryId(query, ParamConstants.CLINICAL_PRIORITY_PARAM, ClinicalAnalysisDBAdaptor.QueryParams.PRIORITY_ID.key()); changeQueryId(query, ParamConstants.CLINICAL_FLAGS_PARAM, ClinicalAnalysisDBAdaptor.QueryParams.FLAGS_ID.key()); changeQueryId(query, ParamConstants.CLINICAL_QUALITY_CONTROL_SUMMARY_PARAM, @@ -1689,7 +1953,7 @@ protected void fixQueryObject(Study study, Query query, String user, String toke public OpenCGAResult count(String studyId, Query query, String token) throws CatalogException { String userId = catalogManager.getUserManager().getUserId(token); - Study study = catalogManager.getStudyManager().resolveId(studyId, userId); + Study study = catalogManager.getStudyManager().resolveId(studyId, userId, StudyManager.INCLUDE_VARIABLE_SET); ObjectMap auditParams = new ObjectMap() .append("studyId", studyId) @@ -1837,7 +2101,7 @@ public OpenCGAResult delete(String studyStr, Query query, QueryOptions options, OpenCGAResult result = OpenCGAResult.empty(); String userId = catalogManager.getUserManager().getUserId(token); - Study study = catalogManager.getStudyManager().resolveId(studyStr, userId); + Study study = catalogManager.getStudyManager().resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET); String operationUuid = UuidUtils.generateOpenCgaUuid(UuidUtils.Entity.AUDIT); @@ -1855,6 +2119,7 @@ public OpenCGAResult delete(String studyStr, Query query, QueryOptions options, DBIterator iterator; try { fixQueryObject(study, finalQuery, userId, token); + AnnotationUtils.fixQueryOptionAnnotation(options); finalQuery.append(ClinicalAnalysisDBAdaptor.QueryParams.STUDY_UID.key(), study.getUid()); iterator = clinicalDBAdaptor.iterator(study.getUid(), finalQuery, INCLUDE_CLINICAL_INTERPRETATION_IDS, userId); @@ -1922,9 +2187,10 @@ public OpenCGAResult groupBy(@Nullable String studyStr, Query query, List updateAnnotations(String studyStr, String clinicalStr, String annotationSetId, + Map annotations, ParamUtils.CompleteUpdateAction action, + QueryOptions options, String token) throws CatalogException { + if (annotations == null || annotations.isEmpty()) { + throw new CatalogException("Missing array of annotations."); + } + ClinicalAnalysisUpdateParams clinicalUpdateParams = new ClinicalAnalysisUpdateParams() + .setAnnotationSets(Collections.singletonList(new AnnotationSet(annotationSetId, null, annotations))); + options = ParamUtils.defaultObject(options, QueryOptions::new); + options.put(Constants.ACTIONS, new ObjectMap(AnnotationSetManager.ANNOTATIONS, action)); + + return update(studyStr, clinicalStr, clinicalUpdateParams, options, token); + } + // ************************** ACLs ******************************** // public OpenCGAResult> getAcls( String studyStr, List clinicalList, String member, boolean ignoreException, String token) throws CatalogException { diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileUtils.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileUtils.java index 46423ef9a2f..02f5cae855b 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileUtils.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/FileUtils.java @@ -261,6 +261,7 @@ public static File.Bioformat detectBioformat(URI uri, File.Format format, File.C case BINARY: case UNKNOWN: case XML: + case PDF: return File.Bioformat.NONE; default: break; @@ -398,6 +399,8 @@ public static File.Format detectFormat(URI uri) { case "jpeg": case "tif": return File.Format.IMAGE; + case "pdf": + return File.Format.PDF; default: break; } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/InterpretationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/InterpretationManager.java index 4294998ca19..0ab582184d0 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/InterpretationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/InterpretationManager.java @@ -388,7 +388,7 @@ void validateNewInterpretation(Study study, Interpretation interpretation, Clini } user = result.first(); } - interpretation.setAnalyst(new ClinicalAnalyst(user.getId(), user.getName(), user.getEmail(), userId, TimeUtils.getTime())); + interpretation.setAnalyst(new ClinicalAnalyst(user.getId(), user.getName(), user.getEmail(), userId, Collections.emptyMap())); } public OpenCGAResult clear(String studyStr, String clinicalAnalysisId, List interpretationList, String token) @@ -984,11 +984,11 @@ private OpenCGAResult update(Study study, Interpretation interpretation, Interpr throw new CatalogException("User '" + updateParams.getAnalyst().getId() + "' not found"); } parameters.put(InterpretationDBAdaptor.QueryParams.ANALYST.key(), new ClinicalAnalyst(userResult.first().getId(), - userResult.first().getName(), userResult.first().getEmail(), userId, TimeUtils.getTime())); + userResult.first().getName(), userResult.first().getEmail(), userId, Collections.emptyMap())); } else { // Remove assignee parameters.put(InterpretationDBAdaptor.QueryParams.ANALYST.key(), new ClinicalAnalyst("", "", "", userId, - TimeUtils.getTime())); + Collections.emptyMap())); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/StudyManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/StudyManager.java index e9e48ab01f4..184bbe18b67 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/StudyManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/StudyManager.java @@ -110,9 +110,12 @@ public class StudyManager extends AbstractManager { public static final QueryOptions INCLUDE_STUDY_IDS = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList( StudyDBAdaptor.QueryParams.UID.key(), StudyDBAdaptor.QueryParams.ID.key(), StudyDBAdaptor.QueryParams.UUID.key(), StudyDBAdaptor.QueryParams.FQN.key())); - static final QueryOptions INCLUDE_VARIABLE_SET = new QueryOptions(QueryOptions.INCLUDE, StudyDBAdaptor.QueryParams.VARIABLE_SET.key()); - static final QueryOptions INCLUDE_CONFIGURATION = - new QueryOptions(QueryOptions.INCLUDE, StudyDBAdaptor.QueryParams.INTERNAL_CONFIGURATION.key()); + static final QueryOptions INCLUDE_VARIABLE_SET = keepFieldInQueryOptions(INCLUDE_STUDY_IDS, + StudyDBAdaptor.QueryParams.VARIABLE_SET.key()); + static final QueryOptions INCLUDE_CONFIGURATION = keepFieldInQueryOptions(INCLUDE_STUDY_IDS, + StudyDBAdaptor.QueryParams.INTERNAL_CONFIGURATION.key()); + static final QueryOptions INCLUDE_VARIABLE_SET_AND_CONFIGURATION = keepFieldsInQueryOptions(INCLUDE_STUDY_IDS, + Arrays.asList(StudyDBAdaptor.QueryParams.INTERNAL_CONFIGURATION.key(), StudyDBAdaptor.QueryParams.VARIABLE_SET.key())); protected Logger logger; diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractManagerTest.java index d29de6b553a..fbce0a4576f 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/AbstractManagerTest.java @@ -49,7 +49,7 @@ import java.util.List; @Category(MediumTests.class) -public class AbstractManagerTest extends GenericTest { +public class AbstractManagerTest extends GenericTest { @Rule public ExpectedException thrown = ExpectedException.none(); diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManagerTest.java index d703b598e44..e07d53338b9 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisManagerTest.java @@ -24,10 +24,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; -import org.opencb.biodata.models.clinical.ClinicalAudit; -import org.opencb.biodata.models.clinical.ClinicalComment; -import org.opencb.biodata.models.clinical.ClinicalDiscussion; -import org.opencb.biodata.models.clinical.Disorder; +import org.opencb.biodata.models.clinical.*; import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; import org.opencb.biodata.models.clinical.interpretation.ClinicalVariantEvidence; import org.opencb.biodata.models.clinical.interpretation.InterpretationMethod; @@ -52,16 +49,14 @@ import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.AclParams; import org.opencb.opencga.core.models.clinical.*; +import org.opencb.opencga.core.models.common.AnnotationSet; import org.opencb.opencga.core.models.common.FlagAnnotation; import org.opencb.opencga.core.models.common.FlagValue; import org.opencb.opencga.core.models.common.StatusParam; import org.opencb.opencga.core.models.family.Family; import org.opencb.opencga.core.models.family.FamilyPermissions; import org.opencb.opencga.core.models.family.FamilyUpdateParams; -import org.opencb.opencga.core.models.file.File; -import org.opencb.opencga.core.models.file.FileLinkParams; -import org.opencb.opencga.core.models.file.FileReferenceParam; -import org.opencb.opencga.core.models.file.FileUpdateParams; +import org.opencb.opencga.core.models.file.*; import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.individual.IndividualPermissions; import org.opencb.opencga.core.models.panel.Panel; @@ -70,9 +65,12 @@ import org.opencb.opencga.core.models.sample.SamplePermissions; import org.opencb.opencga.core.models.sample.SampleUpdateParams; import org.opencb.opencga.core.models.study.Study; +import org.opencb.opencga.core.models.study.Variable; +import org.opencb.opencga.core.models.study.VariableSet; import org.opencb.opencga.core.models.study.configuration.ClinicalConsent; import org.opencb.opencga.core.models.study.configuration.*; import org.opencb.opencga.core.models.user.Account; +import org.opencb.opencga.core.models.user.User; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; @@ -289,6 +287,196 @@ public void createMultipleCasesSameFamily() throws CatalogException { } } + @Test + public void updateClinicalAnalystsTest() throws CatalogException { + ClinicalAnalysis case1 = createDummyEnvironment(true, true).first(); + + catalogManager.getUserManager().create(new User().setId("u1").setName("u1").setAccount(new Account()), TestParamConstants.PASSWORD, opencgaToken); + catalogManager.getUserManager().create(new User().setId("u2").setName("u2").setAccount(new Account()), TestParamConstants.PASSWORD, opencgaToken); + + // Add analysts + OpenCGAResult result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setAnalysts( + Arrays.asList(new ClinicalAnalystParam("u1"), new ClinicalAnalystParam("u2"))), INCLUDE_RESULT, sessionIdUser); + assertEquals(3, result.first().getAnalysts().size()); + assertTrue(result.first().getAnalysts().stream().map(ClinicalAnalyst::getId).collect(Collectors.toSet()).containsAll(Arrays.asList("u1", "u2"))); + + // Check analyst params + for (ClinicalAnalyst analyst : result.first().getAnalysts()) { + assertNotNull(analyst.getId()); + assertNotNull(analyst.getName()); + assertNotNull(analyst.getRole()); + assertNotNull(analyst.getAttributes()); + } + + // Remove analysts + Map actionMap = new HashMap<>(); + actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS.key(), ParamUtils.BasicUpdateAction.REMOVE); + QueryOptions options = new QueryOptions() + .append(Constants.ACTIONS, actionMap) + .append(ParamConstants.INCLUDE_RESULT_PARAM, true); + result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setAnalysts( + Arrays.asList(new ClinicalAnalystParam("u1"), new ClinicalAnalystParam("u2"))), options, sessionIdUser); + assertEquals(1, result.first().getAnalysts().size()); + assertTrue(result.first().getAnalysts().stream().map(ClinicalAnalyst::getId).noneMatch(x -> Arrays.asList("u1", "u2").contains(x))); + + // Set analysts + actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS.key(), ParamUtils.BasicUpdateAction.SET); + options = new QueryOptions() + .append(Constants.ACTIONS, actionMap) + .append(ParamConstants.INCLUDE_RESULT_PARAM, true); + result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setAnalysts( + Arrays.asList(new ClinicalAnalystParam("u1"), new ClinicalAnalystParam("u2"))), options, sessionIdUser); + assertEquals(2, result.first().getAnalysts().size()); + assertTrue(result.first().getAnalysts().stream().map(ClinicalAnalyst::getId).allMatch(x -> Arrays.asList("u1", "u2").contains(x))); + + thrown.expect(CatalogException.class); + thrown.expectMessage("not found"); + catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setAnalysts( + Arrays.asList(new ClinicalAnalystParam("unknown"), new ClinicalAnalystParam("u2"))), options, sessionIdUser); + } + + @Test + public void updateClinicalAnalysisRequest() throws CatalogException { + ClinicalAnalysis case1 = createDummyEnvironment(true, true).first(); + assertTrue(StringUtils.isEmpty(case1.getRequest().getId())); + + catalogManager.getUserManager().create(new User().setId("u1").setName("u1").setEmail("mail@mail.com").setAccount(new Account()), + TestParamConstants.PASSWORD, opencgaToken); + + ClinicalRequest request = new ClinicalRequest("requestId", "bla", null, new ClinicalResponsible().setId("u1"), new HashMap<>()); + + // Change request + OpenCGAResult result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setRequest(request), INCLUDE_RESULT, sessionIdUser); + assertNotNull(result.first().getRequest()); + assertTrue(StringUtils.isNotEmpty(result.first().getRequest().getDate())); + assertEquals("requestId", result.first().getRequest().getId()); + assertEquals("u1", result.first().getRequest().getResponsible().getId()); + assertEquals("u1", result.first().getRequest().getResponsible().getName()); + assertEquals("mail@mail.com", result.first().getRequest().getResponsible().getEmail()); + + // Remove request responsible + request.setResponsible(null); + result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setRequest(request), INCLUDE_RESULT, sessionIdUser); + assertNotNull(result.first().getRequest()); + assertTrue(StringUtils.isNotEmpty(result.first().getRequest().getDate())); + assertEquals("requestId", result.first().getRequest().getId()); + assertNull(result.first().getRequest().getResponsible()); + + // Add non existing request responsible user id + request.setResponsible(new ClinicalResponsible().setId("unknown")); + thrown.expect(CatalogException.class); + thrown.expectMessage("not found"); + catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setRequest(request), INCLUDE_RESULT, sessionIdUser); + } + + @Test + public void updateClinicalAnalysisResponsible() throws CatalogException { + ClinicalAnalysis case1 = createDummyEnvironment(true, true).first(); + assertEquals("user", case1.getResponsible().getId()); + + catalogManager.getUserManager().create(new User().setId("u1").setName("u1").setEmail("mail@mail.com").setAccount(new Account()), + TestParamConstants.PASSWORD, opencgaToken); + + ClinicalResponsible responsible = new ClinicalResponsible().setId("u1"); + + // Change responsible + OpenCGAResult result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setResponsible(responsible), INCLUDE_RESULT, sessionIdUser); + assertNotNull(result.first().getResponsible()); + assertEquals("u1", result.first().getResponsible().getId()); + assertEquals("u1", result.first().getResponsible().getName()); + assertEquals("mail@mail.com", result.first().getResponsible().getEmail()); + + // Change to non existing request responsible user id + responsible.setId("unknown"); + thrown.expect(CatalogException.class); + thrown.expectMessage("not found"); + catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setResponsible(responsible), INCLUDE_RESULT, sessionIdUser); + } + + @Test + public void updateClinicalAnalysisReport() throws CatalogException { + ClinicalAnalysis case1 = createDummyEnvironment(true, true).first(); + assertNull(case1.getReport()); + + ClinicalReport report = new ClinicalReport() + .setTitle("my report") + .setComments(Arrays.asList(new ClinicalComment("author", "msg", null, null), new ClinicalComment("author2", "msg", null, null))); + + // Change report + OpenCGAResult result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setReport(report), INCLUDE_RESULT, sessionIdUser); + assertNotNull(result.first().getReport()); + assertEquals(report.getTitle(), result.first().getReport().getTitle()); + assertEquals(2, result.first().getReport().getComments().size()); + for (ClinicalComment comment : result.first().getReport().getComments()) { + assertEquals("user", comment.getAuthor()); + assertTrue(StringUtils.isNotEmpty(comment.getDate())); + } + + // Add files + catalogManager.getFileManager().create(STUDY, + new FileCreateParams() + .setContent(RandomStringUtils.randomAlphanumeric(1000)) + .setPath("/data/file1.txt") + .setType(File.Type.FILE), + true, sessionIdUser); + catalogManager.getFileManager().create(STUDY, + new FileCreateParams() + .setContent(RandomStringUtils.randomAlphanumeric(1000)) + .setPath("/data/file2.txt") + .setType(File.Type.FILE), + true, sessionIdUser); + + List fileList = Arrays.asList(new File().setId("data:file1.txt"), new File().setId("data:file2.txt")); + report.setSupportingEvidences(fileList); + result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setReport(report), INCLUDE_RESULT, sessionIdUser); + assertNotNull(result.first().getReport()); + assertEquals(report.getTitle(), result.first().getReport().getTitle()); + assertEquals(2, result.first().getReport().getComments().size()); + for (ClinicalComment comment : result.first().getReport().getComments()) { + assertEquals("user", comment.getAuthor()); + assertTrue(StringUtils.isNotEmpty(comment.getDate())); + } + assertEquals(2, result.first().getReport().getSupportingEvidences().size()); + assertEquals("data/file1.txt", result.first().getReport().getSupportingEvidences().get(0).getPath()); + assertEquals("data/file2.txt", result.first().getReport().getSupportingEvidences().get(1).getPath()); + assertNull(result.first().getReport().getFiles()); + + report.setFiles(fileList); + result = catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setReport(report), INCLUDE_RESULT, sessionIdUser); + assertNotNull(result.first().getReport()); + assertEquals(report.getTitle(), result.first().getReport().getTitle()); + assertEquals(2, result.first().getReport().getComments().size()); + for (ClinicalComment comment : result.first().getReport().getComments()) { + assertEquals("user", comment.getAuthor()); + assertTrue(StringUtils.isNotEmpty(comment.getDate())); + } + assertEquals(2, result.first().getReport().getSupportingEvidences().size()); + assertEquals("data/file1.txt", result.first().getReport().getSupportingEvidences().get(0).getPath()); + assertEquals("data/file2.txt", result.first().getReport().getSupportingEvidences().get(1).getPath()); + assertEquals(2, result.first().getReport().getFiles().size()); + assertEquals("data/file1.txt", result.first().getReport().getFiles().get(0).getPath()); + assertEquals("data/file2.txt", result.first().getReport().getFiles().get(1).getPath()); + + // Provide non existing file + report.setFiles(Collections.singletonList(new File().setId("nonexisting.txt"))); + thrown.expect(CatalogException.class); + thrown.expectMessage("not found"); + catalogManager.getClinicalAnalysisManager().update(STUDY, case1.getId(), + new ClinicalAnalysisUpdateParams().setReport(report), INCLUDE_RESULT, sessionIdUser); + } + @Test public void createAndUpdateClinicalAnalysisWithQualityControl() throws CatalogException, InterruptedException { Individual individual = new Individual().setId("child1").setSamples(Arrays.asList(new Sample().setId("sample2"))); @@ -3494,4 +3682,127 @@ public void fetchCasesWithSameProbandAndDifferentSample() throws CatalogExceptio assertEquals(1, result.getResults().get(1).getProband().getSamples().size()); assertEquals(proband.getSamples().get(1).getId(), result.getResults().get(1).getProband().getSamples().get(0).getId()); } + + // Annotation sets + @Test + public void searchByInternalAnnotationSetTest() throws CatalogException { + Set variables = new HashSet<>(); + variables.add(new Variable().setId("a").setType(Variable.VariableType.STRING)); + variables.add(new Variable().setId("b").setType(Variable.VariableType.MAP_INTEGER).setAllowedKeys(Arrays.asList("b1", "b2"))); + VariableSet variableSet = new VariableSet("myInternalVset", "", false, false, true, "", variables, null, 1, null); + catalogManager.getStudyManager().createVariableSet(STUDY, variableSet, sessionIdUser); + + Map annotations = new HashMap<>(); + annotations.put("a", "hello"); + annotations.put("b", new ObjectMap("b1", 2).append("b2", 3)); + AnnotationSet annotationSet = new AnnotationSet("annSet", variableSet.getId(), annotations); + + annotations = new HashMap<>(); + annotations.put("a", "bye"); + annotations.put("b", new ObjectMap("b1", Integer.MAX_VALUE + 1L).append("b2", 5)); + AnnotationSet annotationSet2 = new AnnotationSet("annSet2", variableSet.getId(), annotations); + + DataResult clinicalAnalysisDataResult = createDummyEnvironment(true, true); + ClinicalAnalysis clinicalAnalysis = catalogManager.getClinicalAnalysisManager().update(STUDY, clinicalAnalysisDataResult.first().getId(), + new ClinicalAnalysisUpdateParams().setAnnotationSets(Arrays.asList(annotationSet, annotationSet2)), INCLUDE_RESULT, sessionIdUser).first(); + assertEquals(0, clinicalAnalysis.getAnnotationSets().size()); + + // Create a different case with different annotations + annotations = new HashMap<>(); + annotations.put("a", "hi"); + annotations.put("b", new ObjectMap("b1", 12).append("b2", 13)); + annotationSet = new AnnotationSet("annSet", variableSet.getId(), annotations); + + annotations = new HashMap<>(); + annotations.put("a", "goodbye"); + annotations.put("b", new ObjectMap("b1", 14).append("b2", 15)); + annotationSet2 = new AnnotationSet("annSet2", variableSet.getId(), annotations); + + DataResult clinicalAnalysisDataResult2 = createDummyEnvironment(false, true); + ClinicalAnalysis clinicalAnalysis2 = catalogManager.getClinicalAnalysisManager().update(STUDY, clinicalAnalysisDataResult2.first().getId(), + new ClinicalAnalysisUpdateParams().setAnnotationSets(Arrays.asList(annotationSet, annotationSet2)), INCLUDE_RESULT, sessionIdUser).first(); + assertEquals(0, clinicalAnalysis2.getAnnotationSets().size()); + + // Query by one of the annotations + Query query = new Query(Constants.ANNOTATION, "myInternalVset:a=hello"); + assertEquals(1, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + assertEquals(clinicalAnalysis.getId(), catalogManager.getClinicalAnalysisManager().search(STUDY, query, ClinicalAnalysisManager.INCLUDE_CLINICAL_IDS, sessionIdUser).first() + .getId()); + + query = new Query(Constants.ANNOTATION, "myInternalVset:b.b1=" + (Integer.MAX_VALUE + 1L)); + assertEquals(1, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + assertEquals(clinicalAnalysis.getId(), catalogManager.getClinicalAnalysisManager().search(STUDY, query, ClinicalAnalysisManager.INCLUDE_CLINICAL_IDS, sessionIdUser).first() + .getId()); + + query = new Query(Constants.ANNOTATION, "b.b1=14"); + assertEquals(1, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + assertEquals(clinicalAnalysis2.getId(), catalogManager.getClinicalAnalysisManager().search(STUDY, query, ClinicalAnalysisManager.INCLUDE_CLINICAL_IDS, sessionIdUser).first() + .getId()); + + query = new Query(Constants.ANNOTATION, "a=goodbye"); + assertEquals(1, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + assertEquals(clinicalAnalysis2.getId(), catalogManager.getClinicalAnalysisManager().search(STUDY, query, ClinicalAnalysisManager.INCLUDE_CLINICAL_IDS, sessionIdUser).first() + .getId()); + + // Update sample annotation to be exactly the same as sample2 + ObjectMap action = new ObjectMap(ClinicalAnalysisDBAdaptor.QueryParams.ANNOTATION_SETS.key(), ParamUtils.BasicUpdateAction.SET); + QueryOptions options = new QueryOptions(Constants.ACTIONS, action); + catalogManager.getClinicalAnalysisManager().update(STUDY, clinicalAnalysis.getId(), + new ClinicalAnalysisUpdateParams().setAnnotationSets(Arrays.asList(annotationSet, annotationSet2)), options, sessionIdUser); + + query = new Query(Constants.ANNOTATION, "myInternalVset:a=hello"); + assertEquals(0, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + + query = new Query(Constants.ANNOTATION, "myInternalVset:b.b1=4"); + assertEquals(0, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + + query = new Query(Constants.ANNOTATION, "b.b1=14"); + assertEquals(2, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + assertTrue(Arrays.asList(clinicalAnalysis.getId(), clinicalAnalysis2.getId()) + .containsAll(catalogManager.getClinicalAnalysisManager().search(STUDY, query, ClinicalAnalysisManager.INCLUDE_CLINICAL_IDS, sessionIdUser) + .getResults().stream().map(ClinicalAnalysis::getId).collect(Collectors.toList()))); + + query = new Query(Constants.ANNOTATION, "a=goodbye"); + assertEquals(2, catalogManager.getClinicalAnalysisManager().count(STUDY, query, sessionIdUser).getNumMatches()); + assertTrue(Arrays.asList(clinicalAnalysis.getId(), clinicalAnalysis2.getId()) + .containsAll(catalogManager.getClinicalAnalysisManager().search(STUDY, query, ClinicalAnalysisManager.INCLUDE_CLINICAL_IDS, sessionIdUser) + .getResults().stream().map(ClinicalAnalysis::getId).collect(Collectors.toList()))); + } + + @Test + public void testSearchAnnotation() throws CatalogException { + List variables = new ArrayList<>(); + variables.add(new Variable("var_name", "", "", Variable.VariableType.STRING, "", true, false, Collections.emptyList(), null, 0, "", + "", null, Collections.emptyMap())); + variables.add(new Variable("AGE", "", "", Variable.VariableType.INTEGER, "", false, false, Collections.emptyList(), null, 0, "", "", + null, Collections.emptyMap())); + variables.add(new Variable("HEIGHT", "", "", Variable.VariableType.DOUBLE, "", false, false, Collections.emptyList(), null, 0, "", + "", null, Collections.emptyMap())); + variables.add(new Variable("OTHER", "", "", Variable.VariableType.OBJECT, null, false, false, null, null, 1, "", "", null, + Collections.emptyMap())); + VariableSet vs1 = catalogManager.getStudyManager().createVariableSet(STUDY, "vs1", "vs1", false, false, "", null, variables, + Collections.singletonList(VariableSet.AnnotableDataModels.CLINICAL_ANALYSIS), sessionIdUser).first(); + + ObjectMap annotations = new ObjectMap() + .append("var_name", "Joe") + .append("AGE", 25) + .append("HEIGHT", 180); + AnnotationSet annotationSet = new AnnotationSet("annotation1", vs1.getId(), annotations); + + DataResult clinicalAnalysisDataResult = createDummyEnvironment(true, true); + createDummyEnvironment(false, true); + catalogManager.getClinicalAnalysisManager().update(STUDY, clinicalAnalysisDataResult.first().getId(), + new ClinicalAnalysisUpdateParams().setAnnotationSets(Collections.singletonList(annotationSet)), QueryOptions.empty(), + sessionIdUser); + + Query query = new Query(Constants.ANNOTATION, "var_name=Joe;" + vs1.getId() + ":AGE=25"); + DataResult annotDataResult = catalogManager.getClinicalAnalysisManager().search(STUDY, query, + QueryOptions.empty(), sessionIdUser); + assertEquals(1, annotDataResult.getNumResults()); + + query.put(Constants.ANNOTATION, "var_name=Joe;" + vs1.getId() + ":AGE=23"); + annotDataResult = catalogManager.getClinicalAnalysisManager().search(STUDY, query, QueryOptions.empty(), sessionIdUser); + assertEquals(0, annotDataResult.getNumResults()); + } + } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java b/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java index 4cccaaae80b..36fce194f36 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/api/FieldConstants.java @@ -187,8 +187,10 @@ public class FieldConstants { public static final String CLINICAL_ANALYSIS_INTERPRETATION = "Interpretation of the clinical analysis."; public static final String CLINICAL_ANALYSIS_SECONDARY_INTERPRETATION = "List of Interpretations containing the second and consecutive."; public static final String CLINICAL_ANALYSIS_CONSENT = "Object contains consent annotations of clinical analysis."; - public static final String CLINICAL_ANALYSIS_ANALYST = "The analyst of the clinical analysis."; + public static final String CLINICAL_ANALYSIS_ANALYST = "The analysts of the clinical analysis."; public static final String CLINICAL_ANALYSIS_REPORT = "Report of the clinical analysis."; + public static final String CLINICAL_ANALYSIS_REQUEST = "Request of the clinical analysis."; + public static final String CLINICAL_ANALYSIS_RESPONSIBLE = "Responsible of the clinical analysis."; public static final String CLINICAL_ANALYSIS_PRIORITY = "Priority of the clinical analysis."; public static final String CLINICAL_ANALYSIS_FLAGS = "List of flags for the clinical analysis."; public static final String CLINICAL_ANALYSIS_DUE_DATE_DESCRIPTION = "Due date of the clinical analysis."; @@ -212,6 +214,9 @@ public class FieldConstants { public static final String CLINICAL_REPORT_SIGNED_BY = "Indicates who has signed the report."; public static final String CLINICAL_REPORT_SIGNATURE = "Report signature."; public static final String CLINICAL_REPORT_DATE = "Report date."; + public static final String CLINICAL_REPORT_COMMENTS = "Report comments."; + public static final String CLINICAL_REPORT_SUPPORTING_EVIDENCES = "Report supporting evidences."; + public static final String CLINICAL_REPORT_FILES = "Report files."; //ClinicalPriorityAnnotation public static final String CLINICAL_PRIORITY_ANNOTATION_RANK_DESCRIPTION = "ClinicalPriorityAnnotation rank."; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysis.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysis.java index 1ba86bb4d1d..1c9ffd04027 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysis.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysis.java @@ -24,7 +24,8 @@ import org.opencb.commons.annotations.DataClass; import org.opencb.commons.annotations.DataField; import org.opencb.opencga.core.api.FieldConstants; -import org.opencb.opencga.core.models.PrivateStudyUid; +import org.opencb.opencga.core.models.common.Annotable; +import org.opencb.opencga.core.models.common.AnnotationSet; import org.opencb.opencga.core.models.common.FlagAnnotation; import org.opencb.opencga.core.models.family.Family; import org.opencb.opencga.core.models.file.File; @@ -41,7 +42,7 @@ */ @DataClass(id = "ClinicalAnalysis", since = "1.0", description = "ClinicalAnalysis data model hosts information about any analysis.") -public class ClinicalAnalysis extends PrivateStudyUid { +public class ClinicalAnalysis extends Annotable { /** * ClinicalAnalysis ID is a mandatory parameter when creating a new ClinicalAnalysis, this ID cannot be changed at the moment. @@ -116,14 +117,22 @@ public class ClinicalAnalysis extends PrivateStudyUid { description = FieldConstants.CLINICAL_ANALYSIS_CONSENT) private ClinicalConsentAnnotation consent; - @DataField(id = "analyst", indexed = true, + @DataField(id = "analysts", indexed = true, description = FieldConstants.CLINICAL_ANALYSIS_ANALYST) - private ClinicalAnalyst analyst; + private List analysts; @DataField(id = "report", indexed = true, description = FieldConstants.CLINICAL_ANALYSIS_REPORT) private ClinicalReport report; + @DataField(id = "request", since = "2.12.0", + description = FieldConstants.CLINICAL_ANALYSIS_REPORT) + private ClinicalRequest request; + + @DataField(id = "responsible", since = "2.12.0", + description = FieldConstants.CLINICAL_ANALYSIS_RESPONSIBLE) + private ClinicalResponsible responsible; + @DataField(id = "priority", indexed = true, description = FieldConstants.CLINICAL_ANALYSIS_PRIORITY) private ClinicalPriorityAnnotation priority; @@ -208,10 +217,11 @@ public ClinicalAnalysis() { public ClinicalAnalysis(String id, String description, Type type, Disorder disorder, List files, Individual proband, Family family, List panels, boolean panelLock, boolean locked, Interpretation interpretation, - List secondaryInterpretations, ClinicalConsentAnnotation consent, ClinicalAnalyst analyst, - ClinicalReport report, ClinicalPriorityAnnotation priority, List flags, String creationDate, - String modificationDate, String dueDate, int release, List comments, - ClinicalAnalysisQualityControl qualityControl, List audit, ClinicalAnalysisInternal internal, + List secondaryInterpretations, ClinicalConsentAnnotation consent, + List analysts, ClinicalReport report, ClinicalRequest request, ClinicalResponsible responsible, + ClinicalPriorityAnnotation priority, List flags, String creationDate, String modificationDate, + String dueDate, int release, List comments, ClinicalAnalysisQualityControl qualityControl, + List audit, ClinicalAnalysisInternal internal, List annotationSets, Map attributes, Status status) { this.id = id; this.description = description; @@ -226,8 +236,10 @@ public ClinicalAnalysis(String id, String description, Type type, Disorder disor this.interpretation = interpretation; this.secondaryInterpretations = secondaryInterpretations; this.consent = consent; - this.analyst = analyst; + this.analysts = analysts; this.report = report; + this.request = request; + this.responsible = responsible; this.priority = priority; this.flags = flags; this.creationDate = creationDate; @@ -238,6 +250,7 @@ public ClinicalAnalysis(String id, String description, Type type, Disorder disor this.comments = comments; this.audit = audit; this.internal = internal; + this.annotationSets = annotationSets; this.attributes = attributes; this.status = status; } @@ -259,8 +272,10 @@ public String toString() { sb.append(", interpretation=").append(interpretation); sb.append(", secondaryInterpretations=").append(secondaryInterpretations); sb.append(", consent=").append(consent); - sb.append(", analyst=").append(analyst); + sb.append(", analysts=").append(analysts); sb.append(", report=").append(report); + sb.append(", request=").append(request); + sb.append(", responsible=").append(responsible); sb.append(", priority=").append(priority); sb.append(", flags=").append(flags); sb.append(", creationDate='").append(creationDate).append('\''); @@ -406,12 +421,12 @@ public ClinicalAnalysis setConsent(ClinicalConsentAnnotation consent) { return this; } - public ClinicalAnalyst getAnalyst() { - return analyst; + public List getAnalysts() { + return analysts; } - public ClinicalAnalysis setAnalyst(ClinicalAnalyst analyst) { - this.analyst = analyst; + public ClinicalAnalysis setAnalysts(List analysts) { + this.analysts = analysts; return this; } @@ -424,6 +439,24 @@ public ClinicalAnalysis setReport(ClinicalReport report) { return this; } + public ClinicalRequest getRequest() { + return request; + } + + public ClinicalAnalysis setRequest(ClinicalRequest request) { + this.request = request; + return this; + } + + public ClinicalResponsible getResponsible() { + return responsible; + } + + public ClinicalAnalysis setResponsible(ClinicalResponsible responsible) { + this.responsible = responsible; + return this; + } + public ClinicalPriorityAnnotation getPriority() { return priority; } @@ -514,6 +547,12 @@ public ClinicalAnalysis setInternal(ClinicalAnalysisInternal internal) { return this; } + @Override + public ClinicalAnalysis setAnnotationSets(List annotationSets) { + super.setAnnotationSets(annotationSets); + return this; + } + public Map getAttributes() { return attributes; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisCreateParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisCreateParams.java index cd0d5243f7b..ee4ab6d703b 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisCreateParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisCreateParams.java @@ -17,7 +17,7 @@ package org.opencb.opencga.core.models.clinical; import org.opencb.biodata.models.clinical.ClinicalAnalyst; -import org.opencb.opencga.core.common.TimeUtils; +import org.opencb.opencga.core.models.common.AnnotationSet; import org.opencb.opencga.core.models.common.StatusParam; import org.opencb.opencga.core.models.family.Family; import org.opencb.opencga.core.models.file.File; @@ -47,8 +47,10 @@ public class ClinicalAnalysisCreateParams { private List panels; private Boolean panelLock; - private ClinicalAnalystParam analyst; + private List analysts; private ClinicalReport report; + private ClinicalRequest request; + private ClinicalResponsible responsible; private InterpretationCreateParams interpretation; private ClinicalAnalysisQualityControlUpdateParam qualityControl; @@ -61,6 +63,7 @@ public class ClinicalAnalysisCreateParams { private PriorityParam priority; private List flags; + private List annotationSets; private Map attributes; private StatusParam status; @@ -69,12 +72,13 @@ public ClinicalAnalysisCreateParams() { public ClinicalAnalysisCreateParams(String id, String description, ClinicalAnalysis.Type type, DisorderReferenceParam disorder, List files, ProbandParam proband, FamilyParam family, - List panels, Boolean panelLock, ClinicalAnalystParam analyst, - ClinicalReport report, InterpretationCreateParams interpretation, - ClinicalConsentAnnotationParam consent, String creationDate, String modificationDate, - String dueDate, List comments, + List panels, Boolean panelLock, List analysts, + ClinicalReport report, ClinicalRequest request, ClinicalResponsible responsible, + InterpretationCreateParams interpretation, ClinicalConsentAnnotationParam consent, + String creationDate, String modificationDate, String dueDate, List comments, ClinicalAnalysisQualityControlUpdateParam qualityControl, PriorityParam priority, - List flags, Map attributes, StatusParam status) { + List flags, List annotationSets, Map attributes, + StatusParam status) { this.id = id; this.description = description; this.type = type; @@ -85,7 +89,9 @@ public ClinicalAnalysisCreateParams(String id, String description, ClinicalAnaly this.panels = panels; this.panelLock = panelLock; this.report = report; - this.analyst = analyst; + this.request = request; + this.responsible = responsible; + this.analysts = analysts; this.interpretation = interpretation; this.consent = consent; this.creationDate = creationDate; @@ -95,6 +101,7 @@ public ClinicalAnalysisCreateParams(String id, String description, ClinicalAnaly this.qualityControl = qualityControl; this.priority = priority; this.flags = flags; + this.annotationSets = annotationSets; this.attributes = attributes; this.status = status; } @@ -111,8 +118,10 @@ public static ClinicalAnalysisCreateParams of(ClinicalAnalysis clinicalAnalysis) ? clinicalAnalysis.getPanels().stream().map(p -> new PanelReferenceParam(p.getId())).collect(Collectors.toList()) : null, clinicalAnalysis.isPanelLock(), - clinicalAnalysis.getAnalyst() != null ? ClinicalAnalystParam.of(clinicalAnalysis.getAnalyst()) : null, - clinicalAnalysis.getReport(), + clinicalAnalysis.getAnalysts() != null + ? clinicalAnalysis.getAnalysts().stream().map(ClinicalAnalystParam::of).collect(Collectors.toList()) + : null, + clinicalAnalysis.getReport(), clinicalAnalysis.getRequest(), clinicalAnalysis.getResponsible(), clinicalAnalysis.getInterpretation() != null ? InterpretationCreateParams.of(clinicalAnalysis.getInterpretation()) : null, @@ -128,6 +137,7 @@ public static ClinicalAnalysisCreateParams of(ClinicalAnalysis clinicalAnalysis) clinicalAnalysis.getFlags() != null ? clinicalAnalysis.getFlags().stream().map(FlagValueParam::of).collect(Collectors.toList()) : null, + clinicalAnalysis.getAnnotationSets(), clinicalAnalysis.getAttributes(), StatusParam.of(clinicalAnalysis.getStatus())); } @@ -143,8 +153,10 @@ public String toString() { sb.append(", family=").append(family); sb.append(", panels=").append(panels); sb.append(", panelLock=").append(panelLock); - sb.append(", analyst=").append(analyst); + sb.append(", analysts=").append(analysts); sb.append(", report=").append(report); + sb.append(", request=").append(request); + sb.append(", responsible=").append(responsible); sb.append(", interpretation=").append(interpretation); sb.append(", qualityControl=").append(qualityControl); sb.append(", consent=").append(consent); @@ -154,6 +166,7 @@ public String toString() { sb.append(", comments=").append(comments); sb.append(", priority=").append(priority); sb.append(", flags=").append(flags); + sb.append(", annotationSets=").append(annotationSets); sb.append(", attributes=").append(attributes); sb.append(", status=").append(status); sb.append('}'); @@ -192,7 +205,13 @@ public ClinicalAnalysis toClinicalAnalysis() { Interpretation primaryInterpretation = interpretation != null ? interpretation.toClinicalInterpretation() : null; - String assignee = analyst != null ? analyst.getId() : ""; + List clinicalAnalystList = null; + if (analysts != null) { + clinicalAnalystList = new ArrayList<>(analysts.size()); + for (ClinicalAnalystParam analyst : analysts) { + clinicalAnalystList.add(new ClinicalAnalyst(analyst.getId(), analyst.getId(), "", "", Collections.emptyMap())); + } + } List caFiles = new LinkedList<>(); if (files != null) { @@ -211,12 +230,12 @@ public ClinicalAnalysis toClinicalAnalysis() { return new ClinicalAnalysis(id, description, type, disorder != null ? disorder.toDisorder() : null, caFiles, individual, f, diseasePanelList, panelLock != null ? panelLock : false, false, primaryInterpretation, new LinkedList<>(), consent != null ? consent.toClinicalConsentAnnotation() : null, - new ClinicalAnalyst(assignee, assignee, "", "", TimeUtils.getTime()), report, - priority != null ? priority.toClinicalPriorityAnnotation() : null, + clinicalAnalystList, report, request, responsible, priority != null ? priority.toClinicalPriorityAnnotation() : null, flags != null ? flags.stream().map(FlagValueParam::toFlagAnnotation).collect(Collectors.toList()) : null, creationDate, modificationDate, dueDate, 1, - comments != null ? comments.stream().map(ClinicalCommentParam::toClinicalComment).collect(Collectors.toList()) : null, qualityControl != null ? qualityControl.toClinicalQualityControl() : null, new LinkedList<>(), null, - attributes, status != null ? status.toStatus() : null); + comments != null ? comments.stream().map(ClinicalCommentParam::toClinicalComment).collect(Collectors.toList()) : null, + qualityControl != null ? qualityControl.toClinicalQualityControl() : null, new LinkedList<>(), null, + annotationSets, attributes, status != null ? status.toStatus() : null); } public String getId() { @@ -300,12 +319,12 @@ public ClinicalAnalysisCreateParams setPanelLock(Boolean panelLock) { return this; } - public ClinicalAnalystParam getAnalyst() { - return analyst; + public List getAnalysts() { + return analysts; } - public ClinicalAnalysisCreateParams setAnalyst(ClinicalAnalystParam analyst) { - this.analyst = analyst; + public ClinicalAnalysisCreateParams setAnalysts(List analysts) { + this.analysts = analysts; return this; } @@ -318,6 +337,24 @@ public ClinicalAnalysisCreateParams setReport(ClinicalReport report) { return this; } + public ClinicalRequest getRequest() { + return request; + } + + public ClinicalAnalysisCreateParams setRequest(ClinicalRequest request) { + this.request = request; + return this; + } + + public ClinicalResponsible getResponsible() { + return responsible; + } + + public ClinicalAnalysisCreateParams setResponsible(ClinicalResponsible responsible) { + this.responsible = responsible; + return this; + } + public InterpretationCreateParams getInterpretation() { return interpretation; } @@ -399,6 +436,15 @@ public ClinicalAnalysisCreateParams setFlags(List flags) { return this; } + public List getAnnotationSets() { + return annotationSets; + } + + public ClinicalAnalysisCreateParams setAnnotationSets(List annotationSets) { + this.annotationSets = annotationSets; + return this; + } + public Map getAttributes() { return attributes; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisPermissions.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisPermissions.java index af45797dbf2..e0cdfadd0c0 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisPermissions.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisPermissions.java @@ -6,7 +6,10 @@ public enum ClinicalAnalysisPermissions { NONE(Collections.emptyList()), VIEW(Collections.emptyList()), WRITE(Collections.singletonList(VIEW)), - DELETE(Arrays.asList(VIEW, WRITE)); + DELETE(Arrays.asList(VIEW, WRITE)), + VIEW_ANNOTATIONS(Collections.singletonList(VIEW)), + WRITE_ANNOTATIONS(Arrays.asList(VIEW_ANNOTATIONS, VIEW)), + DELETE_ANNOTATIONS(Arrays.asList(VIEW_ANNOTATIONS, WRITE_ANNOTATIONS, VIEW)); private final List implicitPermissions; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisQualityControlUpdateParam.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisQualityControlUpdateParam.java index d2634228641..98e957bc234 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisQualityControlUpdateParam.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisQualityControlUpdateParam.java @@ -49,6 +49,7 @@ public String toString() { final StringBuilder sb = new StringBuilder("ClinicalAnalysisQualityControlUpdateParam{"); sb.append("summary=").append(summary); sb.append(", comments=").append(comments); + sb.append(", files=").append(files); sb.append('}'); return sb.toString(); } @@ -61,4 +62,22 @@ public ClinicalAnalysisQualityControlUpdateParam setSummary(ClinicalAnalysisQual this.summary = summary; return this; } + + public List getComments() { + return comments; + } + + public ClinicalAnalysisQualityControlUpdateParam setComments(List comments) { + this.comments = comments; + return this; + } + + public List getFiles() { + return files; + } + + public ClinicalAnalysisQualityControlUpdateParam setFiles(List files) { + this.files = files; + return this; + } } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisUpdateParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisUpdateParams.java index a30f469e7a6..b32711af25b 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisUpdateParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisUpdateParams.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.JsonProcessingException; import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.opencga.core.models.common.AnnotationSet; import org.opencb.opencga.core.models.common.StatusParam; import org.opencb.opencga.core.models.file.FileReferenceParam; import org.opencb.opencga.core.models.panel.Panel; @@ -47,9 +48,10 @@ public class ClinicalAnalysisUpdateParams { private FamilyParam family; private Boolean locked; - private ClinicalAnalystParam analyst; + private List analysts; private ClinicalReport report; - + private ClinicalRequest request; + private ClinicalResponsible responsible; private ClinicalAnalysisQualityControlUpdateParam qualityControl; @@ -62,6 +64,7 @@ public class ClinicalAnalysisUpdateParams { private PriorityParam priority; // id private List flags; // id + private List annotationSets; private Map attributes; private StatusParam status; @@ -70,11 +73,13 @@ public ClinicalAnalysisUpdateParams() { public ClinicalAnalysisUpdateParams(String id, String description, ClinicalAnalysis.Type type, DisorderReferenceParam disorder, List files, ProbandParam proband, FamilyParam family, - List panels, Boolean panelLock, Boolean locked, ClinicalAnalystParam analyst, - ClinicalReport report, ClinicalAnalysisQualityControlUpdateParam qualityControl, + List panels, Boolean panelLock, Boolean locked, + List analysts, ClinicalReport report, ClinicalRequest request, + ClinicalResponsible responsible, ClinicalAnalysisQualityControlUpdateParam qualityControl, ClinicalConsentAnnotationParam consent, String creationDate, String modificationDate, String dueDate, List comments, PriorityParam priority, - List flags, Map attributes, StatusParam status) { + List flags, List annotationSets, Map attributes, + StatusParam status) { this.id = id; this.description = description; this.type = type; @@ -85,8 +90,10 @@ public ClinicalAnalysisUpdateParams(String id, String description, ClinicalAnaly this.panels = panels; this.panelLock = panelLock; this.locked = locked; - this.analyst = analyst; + this.analysts = analysts; this.report = report; + this.request = request; + this.responsible = responsible; this.qualityControl = qualityControl; this.consent = consent; this.creationDate = creationDate; @@ -95,6 +102,7 @@ public ClinicalAnalysisUpdateParams(String id, String description, ClinicalAnaly this.comments = comments; this.priority = priority; this.flags = flags; + this.annotationSets = annotationSets; this.attributes = attributes; this.status = status; } @@ -115,12 +123,16 @@ public ClinicalAnalysis toClinicalAnalysis() { locked != null && locked, null, null, consent != null ? consent.toClinicalConsentAnnotation() : null, - analyst != null ? analyst.toClinicalAnalyst() : null, report, + analysts != null + ? analysts.stream().map(ClinicalAnalystParam::toClinicalAnalyst).collect(Collectors.toList()) + : null, + report, request, responsible, priority != null ? priority.toClinicalPriorityAnnotation() : null, flags != null ? flags.stream().map(FlagValueParam::toFlagAnnotation).collect(Collectors.toList()) : null, creationDate, modificationDate, dueDate, 1, comments != null ? comments.stream().map(ClinicalCommentParam::toClinicalComment).collect(Collectors.toList()) : null, - qualityControl != null ? qualityControl.toClinicalQualityControl() : null, null, null, attributes, status != null ? status.toStatus() : null); + qualityControl != null ? qualityControl.toClinicalQualityControl() : null, null, null, annotationSets, attributes, + status != null ? status.toStatus() : null); } @Override @@ -136,8 +148,10 @@ public String toString() { sb.append(", proband=").append(proband); sb.append(", family=").append(family); sb.append(", locked=").append(locked); - sb.append(", analyst=").append(analyst); + sb.append(", analysts=").append(analysts); sb.append(", report=").append(report); + sb.append(", request=").append(request); + sb.append(", responsible=").append(responsible); sb.append(", qualityControl=").append(qualityControl); sb.append(", consent=").append(consent); sb.append(", creationDate='").append(creationDate).append('\''); @@ -146,6 +160,7 @@ public String toString() { sb.append(", comments=").append(comments); sb.append(", priority=").append(priority); sb.append(", flags=").append(flags); + sb.append(", annotationSets=").append(annotationSets); sb.append(", attributes=").append(attributes); sb.append(", status=").append(status); sb.append('}'); @@ -242,12 +257,12 @@ public ClinicalAnalysisUpdateParams setLocked(Boolean locked) { return this; } - public ClinicalAnalystParam getAnalyst() { - return analyst; + public List getAnalysts() { + return analysts; } - public ClinicalAnalysisUpdateParams setAnalyst(ClinicalAnalystParam analyst) { - this.analyst = analyst; + public ClinicalAnalysisUpdateParams setAnalysts(List analysts) { + this.analysts = analysts; return this; } @@ -323,6 +338,15 @@ public ClinicalAnalysisUpdateParams setFlags(List flags) { return this; } + public List getAnnotationSets() { + return annotationSets; + } + + public ClinicalAnalysisUpdateParams setAnnotationSets(List annotationSets) { + this.annotationSets = annotationSets; + return this; + } + public Map getAttributes() { return attributes; } @@ -350,4 +374,21 @@ public ClinicalAnalysisUpdateParams setReport(ClinicalReport report) { return this; } + public ClinicalRequest getRequest() { + return request; + } + + public ClinicalAnalysisUpdateParams setRequest(ClinicalRequest request) { + this.request = request; + return this; + } + + public ClinicalResponsible getResponsible() { + return responsible; + } + + public ClinicalAnalysisUpdateParams setResponsible(ClinicalResponsible responsible) { + this.responsible = responsible; + return this; + } } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalystParam.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalystParam.java index 91861b29d0b..ad3a3cd13f7 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalystParam.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalystParam.java @@ -1,7 +1,8 @@ package org.opencb.opencga.core.models.clinical; import org.opencb.biodata.models.clinical.ClinicalAnalyst; -import org.opencb.opencga.core.common.TimeUtils; + +import java.util.Collections; public class ClinicalAnalystParam { @@ -23,7 +24,7 @@ public static ClinicalAnalystParam of(ClinicalAnalyst clinicalAnalyst) { } public ClinicalAnalyst toClinicalAnalyst() { - return new ClinicalAnalyst(id != null ? id : "", "", "", "", TimeUtils.getTime()); + return new ClinicalAnalyst(id != null ? id : "", "", "", "", Collections.emptyMap()); } @Override diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalReport.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalReport.java index 8303fdc7a1b..fca6fd5c418 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalReport.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalReport.java @@ -1,8 +1,12 @@ package org.opencb.opencga.core.models.clinical; +import org.opencb.biodata.models.clinical.ClinicalComment; import org.opencb.biodata.models.clinical.ClinicalDiscussion; import org.opencb.commons.annotations.DataField; import org.opencb.opencga.core.api.FieldConstants; +import org.opencb.opencga.core.models.file.File; + +import java.util.List; public class ClinicalReport { @@ -34,11 +38,20 @@ public class ClinicalReport { description = FieldConstants.CLINICAL_REPORT_DATE) private String date; + @DataField(id = "comments", description = FieldConstants.CLINICAL_REPORT_COMMENTS, since = "2.12.0") + private List comments; + + @DataField(id = "supportingEvidences", description = FieldConstants.CLINICAL_REPORT_SUPPORTING_EVIDENCES, since = "2.12.0") + private List supportingEvidences; + + @DataField(id = "files", description = FieldConstants.CLINICAL_REPORT_FILES, since = "2.12.0") + private List files; + public ClinicalReport() { } - public ClinicalReport(String title, String overview, ClinicalDiscussion discussion, String logo, String signedBy, - String signature, String date) { + public ClinicalReport(String title, String overview, ClinicalDiscussion discussion, String logo, String signedBy, String signature, + String date, List comments, List supportingEvidences, List files) { this.title = title; this.overview = overview; this.discussion = discussion; @@ -46,6 +59,9 @@ public ClinicalReport(String title, String overview, ClinicalDiscussion discussi this.signedBy = signedBy; this.signature = signature; this.date = date; + this.comments = comments; + this.supportingEvidences = supportingEvidences; + this.files = files; } @Override @@ -53,11 +69,14 @@ public String toString() { final StringBuilder sb = new StringBuilder("ClinicalReport{"); sb.append("title='").append(title).append('\''); sb.append(", overview='").append(overview).append('\''); - sb.append(", discussion='").append(discussion).append('\''); + sb.append(", discussion=").append(discussion); sb.append(", logo='").append(logo).append('\''); sb.append(", signedBy='").append(signedBy).append('\''); sb.append(", signature='").append(signature).append('\''); sb.append(", date='").append(date).append('\''); + sb.append(", comments=").append(comments); + sb.append(", supportingEvidences=").append(supportingEvidences); + sb.append(", files=").append(files); sb.append('}'); return sb.toString(); } @@ -124,4 +143,31 @@ public ClinicalReport setDate(String date) { this.date = date; return this; } + + public List getComments() { + return comments; + } + + public ClinicalReport setComments(List comments) { + this.comments = comments; + return this; + } + + public List getSupportingEvidences() { + return supportingEvidences; + } + + public ClinicalReport setSupportingEvidences(List supportingEvidences) { + this.supportingEvidences = supportingEvidences; + return this; + } + + public List getFiles() { + return files; + } + + public ClinicalReport setFiles(List files) { + this.files = files; + return this; + } } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalRequest.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalRequest.java new file mode 100644 index 00000000000..38998b4bb0d --- /dev/null +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalRequest.java @@ -0,0 +1,80 @@ +package org.opencb.opencga.core.models.clinical; + +import java.util.Map; + +public class ClinicalRequest { + + private String id; + private String justification; + private String date; + private ClinicalResponsible responsible; + private Map attributes; + + public ClinicalRequest() { + } + + public ClinicalRequest(String id, String justification, String date, ClinicalResponsible responsible, Map attributes) { + this.id = id; + this.justification = justification; + this.date = date; + this.responsible = responsible; + this.attributes = attributes; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ClinicalRequest{"); + sb.append("id='").append(id).append('\''); + sb.append(", justification='").append(justification).append('\''); + sb.append(", date='").append(date).append('\''); + sb.append(", responsible=").append(responsible); + sb.append(", attributes=").append(attributes); + sb.append('}'); + return sb.toString(); + } + + public String getId() { + return id; + } + + public ClinicalRequest setId(String id) { + this.id = id; + return this; + } + + public String getJustification() { + return justification; + } + + public ClinicalRequest setJustification(String justification) { + this.justification = justification; + return this; + } + + public String getDate() { + return date; + } + + public ClinicalRequest setDate(String date) { + this.date = date; + return this; + } + + public ClinicalResponsible getResponsible() { + return responsible; + } + + public ClinicalRequest setResponsible(ClinicalResponsible responsible) { + this.responsible = responsible; + return this; + } + + public Map getAttributes() { + return attributes; + } + + public ClinicalRequest setAttributes(Map attributes) { + this.attributes = attributes; + return this; + } +} diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalResponsible.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalResponsible.java new file mode 100644 index 00000000000..3e8967b552a --- /dev/null +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalResponsible.java @@ -0,0 +1,115 @@ +package org.opencb.opencga.core.models.clinical; + +public class ClinicalResponsible { + + private String id; + private String name; + private String email; + private String organization; + private String department; + private String address; + private String city; + private String postcode; + + public ClinicalResponsible() { + } + + public ClinicalResponsible(String id, String name, String email, String organization, String department, String address, String city, + String postcode) { + this.id = id; + this.name = name; + this.email = email; + this.organization = organization; + this.department = department; + this.address = address; + this.city = city; + this.postcode = postcode; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ClinicalResponsible{"); + sb.append("id='").append(id).append('\''); + sb.append(", name='").append(name).append('\''); + sb.append(", email='").append(email).append('\''); + sb.append(", organization='").append(organization).append('\''); + sb.append(", department='").append(department).append('\''); + sb.append(", address='").append(address).append('\''); + sb.append(", city='").append(city).append('\''); + sb.append(", postcode='").append(postcode).append('\''); + sb.append('}'); + return sb.toString(); + } + + public String getId() { + return id; + } + + public ClinicalResponsible setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public ClinicalResponsible setName(String name) { + this.name = name; + return this; + } + + public String getEmail() { + return email; + } + + public ClinicalResponsible setEmail(String email) { + this.email = email; + return this; + } + + public String getOrganization() { + return organization; + } + + public ClinicalResponsible setOrganization(String organization) { + this.organization = organization; + return this; + } + + public String getDepartment() { + return department; + } + + public ClinicalResponsible setDepartment(String department) { + this.department = department; + return this; + } + + public String getAddress() { + return address; + } + + public ClinicalResponsible setAddress(String address) { + this.address = address; + return this; + } + + public String getCity() { + return city; + } + + public ClinicalResponsible setCity(String city) { + this.city = city; + return this; + } + + public String getPostcode() { + return postcode; + } + + public ClinicalResponsible setPostcode(String postcode) { + this.postcode = postcode; + return this; + } +} diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/file/File.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/file/File.java index 28a2a05980f..581cc6d6dd1 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/file/File.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/file/File.java @@ -562,6 +562,7 @@ public enum Format { TAB_SEPARATED_VALUES, COMMA_SEPARATED_VALUES, XML, PROTOCOL_BUFFER, JSON, AVRO, PARQUET, //Serialization formats + PDF, IMAGE, PLAIN, BINARY, diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/study/StudyPermissions.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/study/StudyPermissions.java index 3eefb1a5c63..059af8182ab 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/study/StudyPermissions.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/study/StudyPermissions.java @@ -103,7 +103,13 @@ public enum Permissions { WRITE_CLINICAL_ANALYSIS(Collections.singletonList(VIEW_CLINICAL_ANALYSIS), ClinicalAnalysisPermissions.WRITE.name(), CLINICAL_ANALYSIS), DELETE_CLINICAL_ANALYSIS(Arrays.asList(VIEW_CLINICAL_ANALYSIS, WRITE_CLINICAL_ANALYSIS), - ClinicalAnalysisPermissions.DELETE.name(), CLINICAL_ANALYSIS); + ClinicalAnalysisPermissions.DELETE.name(), CLINICAL_ANALYSIS), + VIEW_CLINICAL_ANNOTATIONS(Collections.singletonList(VIEW_CLINICAL_ANALYSIS), ClinicalAnalysisPermissions.VIEW_ANNOTATIONS.name(), + CLINICAL_ANALYSIS), + WRITE_CLINICAL_ANNOTATIONS(Arrays.asList(VIEW_CLINICAL_ANALYSIS, VIEW_CLINICAL_ANNOTATIONS), + ClinicalAnalysisPermissions.WRITE_ANNOTATIONS.name(), CLINICAL_ANALYSIS), + DELETE_CLINICAL_ANNOTATIONS(Arrays.asList(VIEW_CLINICAL_ANALYSIS, VIEW_CLINICAL_ANNOTATIONS, WRITE_CLINICAL_ANNOTATIONS), + ClinicalAnalysisPermissions.DELETE_ANNOTATIONS.name(), CLINICAL_ANALYSIS); private final static Map map; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/study/VariableSet.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/study/VariableSet.java index 7ffdb8a460c..c2ac1f3ba43 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/study/VariableSet.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/study/VariableSet.java @@ -45,7 +45,8 @@ public enum AnnotableDataModels { COHORT, INDIVIDUAL, FAMILY, - FILE + FILE, + CLINICAL_ANALYSIS } public VariableSet() { diff --git a/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/ExecutionDaemon.java b/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/ExecutionDaemon.java index 602257b4fb2..5ab2fee0840 100644 --- a/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/ExecutionDaemon.java +++ b/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/ExecutionDaemon.java @@ -26,6 +26,7 @@ import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.opencga.analysis.clinical.ClinicalTsvAnnotationLoader; import org.opencb.opencga.analysis.clinical.exomiser.ExomiserInterpretationAnalysis; import org.opencb.opencga.analysis.clinical.rga.AuxiliarRgaAnalysis; import org.opencb.opencga.analysis.clinical.rga.RgaAnalysis; @@ -242,6 +243,7 @@ public class ExecutionDaemon extends MonitorParentDaemon { put(RgaAnalysis.ID, "clinical " + RgaAnalysis.ID + "-run"); put(AuxiliarRgaAnalysis.ID, "clinical " + AuxiliarRgaAnalysis.ID + "-run"); + put(ClinicalTsvAnnotationLoader.ID, "clinical tsv-load"); put(JulieTool.ID, "variant julie-run"); diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/ClinicalWebService.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/ClinicalWebService.java index 7906588ad23..fdaa1e84449 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/ClinicalWebService.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/analysis/ClinicalWebService.java @@ -21,6 +21,7 @@ import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; import org.opencb.commons.datastore.core.*; import org.opencb.opencga.analysis.clinical.ClinicalInterpretationManager; +import org.opencb.opencga.analysis.clinical.ClinicalTsvAnnotationLoader; import org.opencb.opencga.analysis.clinical.exomiser.ExomiserInterpretationAnalysis; import org.opencb.opencga.analysis.clinical.rga.AuxiliarRgaAnalysis; import org.opencb.opencga.analysis.clinical.rga.RgaAnalysis; @@ -43,7 +44,9 @@ import org.opencb.opencga.core.models.AclParams; import org.opencb.opencga.core.models.analysis.knockout.*; import org.opencb.opencga.core.models.clinical.*; +import org.opencb.opencga.core.models.common.TsvAnnotationParams; import org.opencb.opencga.core.models.job.Job; +import org.opencb.opencga.core.models.sample.Sample; import org.opencb.opencga.core.models.study.configuration.ClinicalAnalysisStudyConfiguration; import org.opencb.opencga.core.tools.annotations.*; @@ -208,13 +211,20 @@ public Response update( @QueryParam("commentsAction") ParamUtils.AddRemoveReplaceAction commentsAction, @ApiParam(value = "Action to be performed if the array of flags is being updated.", allowableValues = "ADD,SET,REMOVE", defaultValue = "ADD") @QueryParam("flagsAction") ParamUtils.BasicUpdateAction flagsAction, + @ApiParam(value = "Action to be performed if the array of analysts is being updated.", allowableValues = "ADD,SET,REMOVE", defaultValue = "ADD") + @QueryParam("analystsAction") ParamUtils.BasicUpdateAction analystsAction, @ApiParam(value = "Action to be performed if the array of files is being updated.", allowableValues = "ADD,SET,REMOVE", defaultValue = "ADD") @QueryParam("filesAction") ParamUtils.BasicUpdateAction filesAction, @ApiParam(value = "Action to be performed if the array of panels is being updated.", allowableValues = "ADD,SET,REMOVE", defaultValue = "ADD") @QueryParam("panelsAction") ParamUtils.BasicUpdateAction panelsAction, + @ApiParam(value = "Action to be performed if the array of annotationSets is being updated.", allowableValues = "ADD,SET,REMOVE", defaultValue = "ADD") + @QueryParam("annotationSetsAction") ParamUtils.BasicUpdateAction annotationSetsAction, @ApiParam(value = ParamConstants.INCLUDE_RESULT_DESCRIPTION, defaultValue = "false") @QueryParam(ParamConstants.INCLUDE_RESULT_PARAM) boolean includeResult, @ApiParam(name = "body", value = "JSON containing clinical analysis information", required = true) ClinicalAnalysisUpdateParams params) { try { + if (annotationSetsAction == null) { + annotationSetsAction = ParamUtils.BasicUpdateAction.ADD; + } if (commentsAction == null) { commentsAction = ParamUtils.AddRemoveReplaceAction.ADD; } @@ -227,12 +237,17 @@ public Response update( if (panelsAction == null) { panelsAction = ParamUtils.BasicUpdateAction.ADD; } + if (analystsAction == null) { + analystsAction = ParamUtils.BasicUpdateAction.ADD; + } Map actionMap = new HashMap<>(); + actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.ANNOTATION_SETS.key(), annotationSetsAction); actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.COMMENTS.key(), commentsAction); actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.FLAGS.key(), flagsAction); actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.FILES.key(), filesAction); actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.PANELS.key(), panelsAction); + actionMap.put(ClinicalAnalysisDBAdaptor.QueryParams.ANALYSTS.key(), analystsAction); queryOptions.put(Constants.ACTIONS, actionMap); return createOkResponse(clinicalManager.update(studyStr, getIdList(clinicalAnalysisStr), params, true, queryOptions, token)); @@ -241,6 +256,55 @@ public Response update( } } + @POST + @Path("/annotationSets/load") + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Load annotation sets from a TSV file", response = Job.class) + public Response loadTsvAnnotations( + @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @QueryParam(ParamConstants.STUDY_PARAM) String studyStr, + @ApiParam(value = ParamConstants.VARIABLE_SET_DESCRIPTION, required = true) @QueryParam("variableSetId") String variableSetId, + @ApiParam(value = "Path where the TSV file is located in OpenCGA or where it should be located.", required = true) + @QueryParam("path") String path, + @ApiParam(value = "Flag indicating whether to create parent directories if they don't exist (only when TSV file was not " + + "previously associated).") + @DefaultValue("false") @QueryParam("parents") boolean parents, + @ApiParam(value = "Annotation set id. If not provided, variableSetId will be used.") @QueryParam("annotationSetId") String annotationSetId, + @ApiParam(value = ParamConstants.TSV_ANNOTATION_DESCRIPTION) TsvAnnotationParams params) { + try { + ObjectMap additionalParams = new ObjectMap() + .append("parents", parents) + .append("annotationSetId", annotationSetId); + + return createOkResponse(catalogManager.getClinicalAnalysisManager().loadTsvAnnotations(studyStr, variableSetId, path, params, + additionalParams, ClinicalTsvAnnotationLoader.ID, token)); + } catch (Exception e) { + return createErrorResponse(e); + } + } + + @POST + @Path("/{clinicalAnalysis}/annotationSets/{annotationSet}/annotations/update") + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Update annotations from an annotationSet", response = Sample.class) + public Response updateAnnotations( + @ApiParam(value = "Clinical analysis ID") @PathParam("clinicalAnalysis") String clinicalId, + @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @QueryParam(ParamConstants.STUDY_PARAM) String studyStr, + @ApiParam(value = ParamConstants.ANNOTATION_SET_ID) @PathParam("annotationSet") String annotationSetId, + @ApiParam(value = ParamConstants.ANNOTATION_SET_UPDATE_ACTION_DESCRIPTION, allowableValues = "ADD,SET,REMOVE,RESET,REPLACE", + defaultValue = "ADD") + @QueryParam("action") ParamUtils.CompleteUpdateAction action, + @ApiParam(value = ParamConstants.ANNOTATION_SET_UPDATE_PARAMS_DESCRIPTION) Map updateParams) { + try { + if (action == null) { + action = ParamUtils.CompleteUpdateAction.ADD; + } + return createOkResponse(catalogManager.getClinicalAnalysisManager().updateAnnotations(studyStr, clinicalId, annotationSetId, + updateParams, action, queryOptions, token)); + } catch (Exception e) { + return createErrorResponse(e); + } + } + @DELETE @Path("/{clinicalAnalyses}/delete") @ApiOperation(value = "Delete clinical analyses", response = ClinicalAnalysis.class) @@ -263,7 +327,9 @@ public Response delete( @ApiImplicitParam(name = QueryOptions.INCLUDE, value = ParamConstants.INCLUDE_DESCRIPTION, example = "name,attributes", dataType = "string", paramType = "query"), @ApiImplicitParam(name = QueryOptions.EXCLUDE, value = ParamConstants.EXCLUDE_DESCRIPTION, - example = "id,status", dataType = "string", paramType = "query") + example = "id,status", dataType = "string", paramType = "query"), + @ApiImplicitParam(name = ParamConstants.FLATTEN_ANNOTATIONS, value = "Flatten the annotations?", defaultValue = "false", + dataType = "boolean", paramType = "query") }) public Response info( @ApiParam(value = ParamConstants.CLINICAL_ANALYSES_DESCRIPTION) @PathParam(value = "clinicalAnalysis") String clinicalAnalysisStr, @@ -289,7 +355,9 @@ public Response info( @ApiImplicitParam(name = QueryOptions.EXCLUDE, value = ParamConstants.EXCLUDE_DESCRIPTION, example = "id,status", dataType = "string", paramType = "query"), @ApiImplicitParam(name = QueryOptions.LIMIT, value = ParamConstants.LIMIT_DESCRIPTION, dataType = "integer", paramType = "query"), @ApiImplicitParam(name = QueryOptions.SKIP, value = ParamConstants.SKIP_DESCRIPTION, dataType = "integer", paramType = "query"), - @ApiImplicitParam(name = QueryOptions.COUNT, value = ParamConstants.COUNT_DESCRIPTION, defaultValue = "false", dataType = "boolean", paramType = "query") + @ApiImplicitParam(name = QueryOptions.COUNT, value = ParamConstants.COUNT_DESCRIPTION, defaultValue = "false", dataType = "boolean", paramType = "query"), + @ApiImplicitParam(name = ParamConstants.FLATTEN_ANNOTATIONS, value = "Flatten the annotations?", defaultValue = "false", + dataType = "boolean", paramType = "query") }) public Response search( @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @QueryParam(ParamConstants.STUDY_PARAM) String studyStr, @@ -317,6 +385,7 @@ public Response search( @ApiParam(value = ParamConstants.CLINICAL_RELEASE_DESCRIPTION) @QueryParam(ParamConstants.CLINICAL_RELEASE_PARAM) String release, @ApiParam(value = ParamConstants.CLINICAL_STATUS_DESCRIPTION) @QueryParam(ParamConstants.CLINICAL_STATUS_PARAM) String status, @ApiParam(value = ParamConstants.CLINICAL_INTERNAL_STATUS_DESCRIPTION) @QueryParam(ParamConstants.CLINICAL_INTERNAL_STATUS_PARAM) String internalStatus, + @ApiParam(value = ParamConstants.ANNOTATION_DESCRIPTION) @QueryParam(Constants.ANNOTATION) String annotation, @ApiParam(value = ParamConstants.DELETED_DESCRIPTION) @QueryParam(ParamConstants.DELETED_PARAM) boolean deleted) { try { query.remove(ParamConstants.STUDY_PARAM); @@ -356,6 +425,7 @@ public Response distinct( @ApiParam(value = ParamConstants.CLINICAL_RELEASE_DESCRIPTION) @QueryParam(ParamConstants.CLINICAL_RELEASE_PARAM) String release, @ApiParam(value = ParamConstants.CLINICAL_STATUS_DESCRIPTION) @QueryParam(ParamConstants.CLINICAL_STATUS_PARAM) String status, @ApiParam(value = ParamConstants.CLINICAL_INTERNAL_STATUS_DESCRIPTION) @QueryParam(ParamConstants.CLINICAL_INTERNAL_STATUS_PARAM) String internalStatus, + @ApiParam(value = ParamConstants.ANNOTATION_DESCRIPTION) @QueryParam(Constants.ANNOTATION) String annotation, @ApiParam(value = ParamConstants.DELETED_DESCRIPTION) @QueryParam(ParamConstants.DELETED_PARAM) boolean deleted, @ApiParam(value = ParamConstants.DISTINCT_FIELD_DESCRIPTION, required = true) @QueryParam(ParamConstants.DISTINCT_FIELD_PARAM) String field) { try { diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/annotators/VariantAnnotatorTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/annotators/VariantAnnotatorTest.java index 02b84fc38a9..ecc48098006 100644 --- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/annotators/VariantAnnotatorTest.java +++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/annotation/annotators/VariantAnnotatorTest.java @@ -1,20 +1,19 @@ package org.opencb.opencga.storage.core.variant.annotation.annotators; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.opencb.biodata.models.variant.Variant; -import org.opencb.biodata.models.variant.avro.EvidenceEntry; import org.opencb.biodata.models.variant.avro.VariantAnnotation; import org.opencb.cellbase.core.result.CellBaseDataResult; import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.opencb.opencga.core.testclassification.duration.ShortTests; import org.opencb.opencga.storage.core.StorageEngine; -import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.opencb.opencga.storage.core.metadata.models.ProjectMetadata; import org.opencb.opencga.storage.core.variant.VariantStorageOptions; import org.opencb.opencga.storage.core.variant.annotation.VariantAnnotatorException; @@ -111,6 +110,7 @@ public void testErrorVariant() throws VariantAnnotatorException { testAnnotator.annotate(Arrays.asList(new Variant("10:999:A:C"), new Variant("10:1000:A:C"), new Variant("10:1001:A:C"))); } + @Ignore @Test public void useCellBaseApiKeys() throws VariantAnnotatorException { storageConfiguration.getCellbase().setUrl("https://uk.ws.zettagenomics.com/cellbase/");