From d6f754aece36f3570a467bf76349ad7429397806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Wed, 5 Apr 2023 11:30:55 +0200 Subject: [PATCH 01/18] storage: minor improvement in variant search converter, #TASK-4158 --- .../variant/search/VariantSearchToVariantConverter.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java index d34f7f64c8e..fb9485c59bb 100644 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java +++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java @@ -799,7 +799,12 @@ public VariantSearchModel convertToStorageType(Variant variant) { // Remove 'SO:' prefix to Store SO Accessions as integers and also store the gene - SO acc relation for (SequenceOntologyTerm sequenceOntologyTerm : conseqType.getSequenceOntologyTerms()) { - int soIdInt = Integer.parseInt(sequenceOntologyTerm.getAccession().substring(3)); + int soIdInt; + try { + soIdInt = Integer.parseInt(sequenceOntologyTerm.getAccession().substring(3)); + } catch (NumberFormatException e) { + soIdInt = ConsequenceTypeMappings.termToAccession.get(sequenceOntologyTerm.getName()); + } soAccessions.add(soIdInt); if (StringUtils.isNotEmpty(gene)) { From 40e57872fc96a3cc360f9f1151f72566d3c36f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Tue, 18 Apr 2023 08:46:17 +0200 Subject: [PATCH 02/18] Remove unused files, #TASK-4158 --- .../ClinicalInterpretationManager.java | 398 +++++++++--------- .../core/clinical/ClinicalVariantEngine.java | 151 ------- .../clinical/ClinicalVariantException.java | 28 -- .../clinical/ClinicalVariantIterator.java | 25 -- .../clinical/ClinicalVariantQueryParam.java | 248 ----------- 5 files changed, 199 insertions(+), 651 deletions(-) delete mode 100644 opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantEngine.java delete mode 100644 opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantException.java delete mode 100644 opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantIterator.java delete mode 100644 opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantQueryParam.java 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 2e9f8a022aa..85bd348a185 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 @@ -57,15 +57,15 @@ import org.opencb.opencga.core.models.clinical.Interpretation; import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.project.Project; -import org.opencb.opencga.core.models.study.Group; +//import org.opencb.opencga.core.models.study.Group; import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.user.User; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.response.VariantQueryResult; import org.opencb.opencga.storage.core.StorageEngineFactory; -import org.opencb.opencga.storage.core.clinical.ClinicalVariantEngine; -import org.opencb.opencga.storage.core.clinical.ClinicalVariantException; -import org.opencb.opencga.storage.core.clinical.ClinicalVariantIterator; +//import org.opencb.opencga.storage.core.clinical.ClinicalVariantEngine; +//import org.opencb.opencga.storage.core.clinical.ClinicalVariantException; +//import org.opencb.opencga.storage.core.clinical.ClinicalVariantIterator; import org.opencb.opencga.storage.core.exceptions.StorageEngineException; import org.opencb.opencga.storage.core.metadata.models.SampleMetadata; import org.opencb.opencga.storage.core.metadata.models.TaskMetadata; @@ -94,7 +94,7 @@ public class ClinicalInterpretationManager extends StorageManager { private InterpretationAnalysisConfiguration config; private ClinicalAnalysisManager clinicalAnalysisManager; - private ClinicalVariantEngine clinicalVariantEngine; +// private ClinicalVariantEngine clinicalVariantEngine; private VariantStorageManager variantStorageManager; protected AlignmentStorageManager alignmentStorageManager; @@ -158,35 +158,35 @@ public ClinicalInterpretationManager(CatalogManager catalogManager, StorageEngin @Override public void testConnection() throws StorageEngineException { } - - public DataResult index(String token) { - return null; - } - - public DataResult index(String study, String token) throws IOException, ClinicalVariantException, CatalogException { - DBIterator clinicalAnalysisDBIterator = - clinicalAnalysisManager.iterator(study, new Query(), QueryOptions.empty(), token); - - while (clinicalAnalysisDBIterator.hasNext()) { - ClinicalAnalysis clinicalAnalysis = clinicalAnalysisDBIterator.next(); - if (clinicalAnalysis.getInterpretation() != null) { - clinicalAnalysis.getInterpretation().getAttributes().put("OPENCGA_CLINICAL_ANALYSIS", clinicalAnalysis); - - this.clinicalVariantEngine.insert(clinicalAnalysis.getInterpretation(), database); - - } - } - return null; - } - - public DataResult query(Query query, QueryOptions options, String token) - throws IOException, ClinicalVariantException, CatalogException { - // Check permissions - query = checkQueryPermissions(query, token); - - return clinicalVariantEngine.query(query, options, ""); - } - +// +// public DataResult index(String token) { +// return null; +// } +// +// public DataResult index(String study, String token) throws IOException, ClinicalVariantException, CatalogException { +// DBIterator clinicalAnalysisDBIterator = +// clinicalAnalysisManager.iterator(study, new Query(), QueryOptions.empty(), token); +// +// while (clinicalAnalysisDBIterator.hasNext()) { +// ClinicalAnalysis clinicalAnalysis = clinicalAnalysisDBIterator.next(); +// if (clinicalAnalysis.getInterpretation() != null) { +// clinicalAnalysis.getInterpretation().getAttributes().put("OPENCGA_CLINICAL_ANALYSIS", clinicalAnalysis); +// +// this.clinicalVariantEngine.insert(clinicalAnalysis.getInterpretation(), database); +// +// } +// } +// return null; +// } +// +// public DataResult query(Query query, QueryOptions options, String token) +// throws IOException, ClinicalVariantException, CatalogException { +// // Check permissions +// query = checkQueryPermissions(query, token); +// +// return clinicalVariantEngine.query(query, options, ""); +// } +// // public DataResult interpretationQuery(Query query, QueryOptions options, String token) // throws IOException, ClinicalVariantException, CatalogException { // // Check permissions @@ -194,38 +194,38 @@ public DataResult query(Query query, QueryOptions options, Stri // // return clinicalVariantEngine.interpretationQuery(query, options, ""); // } - - public DataResult facet(Query query, QueryOptions queryOptions, String token) - throws IOException, ClinicalVariantException, CatalogException { - // Check permissions - query = checkQueryPermissions(query, token); - - return clinicalVariantEngine.facet(query, queryOptions, ""); - } - - public ClinicalVariantIterator iterator(Query query, QueryOptions options, String token) - throws IOException, ClinicalVariantException, CatalogException { - // Check permissions - query = checkQueryPermissions(query, token); - - return clinicalVariantEngine.iterator(query, options, ""); - } - - public void addInterpretationComment(String study, long interpretationId, ClinicalComment comment, String token) - throws IOException, ClinicalVariantException, CatalogException { - // Check permissions - checkInterpretationPermissions(study, interpretationId, token); - - clinicalVariantEngine.addInterpretationComment(interpretationId, comment, ""); - } - - public void addClinicalVariantComment(String study, long interpretationId, String variantId, ClinicalComment comment, String token) - throws IOException, ClinicalVariantException, CatalogException { - // Check permissions - checkInterpretationPermissions(study, interpretationId, token); - - clinicalVariantEngine.addClinicalVariantComment(interpretationId, variantId, comment, ""); - } +// +// public DataResult facet(Query query, QueryOptions queryOptions, String token) +// throws IOException, ClinicalVariantException, CatalogException { +// // Check permissions +// query = checkQueryPermissions(query, token); +// +// return clinicalVariantEngine.facet(query, queryOptions, ""); +// } +// +// public ClinicalVariantIterator iterator(Query query, QueryOptions options, String token) +// throws IOException, ClinicalVariantException, CatalogException { +// // Check permissions +// query = checkQueryPermissions(query, token); +// +// return clinicalVariantEngine.iterator(query, options, ""); +// } +// +// public void addInterpretationComment(String study, long interpretationId, ClinicalComment comment, String token) +// throws IOException, ClinicalVariantException, CatalogException { +// // Check permissions +// checkInterpretationPermissions(study, interpretationId, token); +// +// clinicalVariantEngine.addInterpretationComment(interpretationId, comment, ""); +// } +// +// public void addClinicalVariantComment(String study, long interpretationId, String variantId, ClinicalComment comment, String token) +// throws IOException, ClinicalVariantException, CatalogException { +// // Check permissions +// checkInterpretationPermissions(study, interpretationId, token); +// +// clinicalVariantEngine.addClinicalVariantComment(interpretationId, variantId, comment, ""); +// } /*--------------------------------------------------------------------------*/ /* Get clinical variants */ @@ -834,139 +834,139 @@ public VariantStorageManager getVariantStorageManager() { /* P R I V A T E M E T H O D S */ /*--------------------------------------------------------------------------*/ - // FIXME Class path to a new section in storage-configuration.yml file - private void init() { - try { - this.database = catalogManager.getConfiguration().getDatabasePrefix() + "_clinical"; - - this.clinicalVariantEngine = getClinicalStorageEngine(); - } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) { - e.printStackTrace(); - } - - } - - private ClinicalVariantEngine getClinicalStorageEngine() throws ClassNotFoundException, IllegalAccessException, InstantiationException { - String clazz = this.storageConfiguration.getClinical().getManager(); - ClinicalVariantEngine storageEngine = (ClinicalVariantEngine) Class.forName(clazz).newInstance(); - storageEngine.setStorageConfiguration(this.storageConfiguration); - return storageEngine; - } - - private Query checkQueryPermissions(Query query, String token) throws ClinicalVariantException, CatalogException { - if (query == null) { - throw new ClinicalVariantException("Query object is null"); - } - - // Get userId from token and Study numeric IDs from the query - String userId = catalogManager.getUserManager().getUserId(token); - List studyIds = getStudyIds(userId, query); - - // If one specific clinical analysis, sample or individual is provided we expect a single valid study as well - if (isCaseProvided(query)) { - if (studyIds.size() == 1) { - // This checks that the user has permission to the clinical analysis, family, sample or individual - DataResult clinicalAnalysisQueryResult = catalogManager.getClinicalAnalysisManager() - .search(studyIds.get(0), query, QueryOptions.empty(), token); - - if (clinicalAnalysisQueryResult.getResults().isEmpty()) { - throw new ClinicalVariantException("Either the ID does not exist or the user does not have permissions to view it"); - } else { - if (!query.containsKey(ClinicalVariantEngine.QueryParams.CLINICAL_ANALYSIS_ID.key())) { - query.remove(ClinicalVariantEngine.QueryParams.FAMILY.key()); - query.remove(ClinicalVariantEngine.QueryParams.SAMPLE.key()); - query.remove(ClinicalVariantEngine.QueryParams.SUBJECT.key()); - String clinicalAnalysisList = StringUtils.join( - clinicalAnalysisQueryResult.getResults().stream().map(ClinicalAnalysis::getId).collect(Collectors.toList()), - ","); - query.put("clinicalAnalysisId", clinicalAnalysisList); - } - } - } else { - throw new ClinicalVariantException("No single valid study provided: " - + query.getString(ClinicalVariantEngine.QueryParams.STUDY.key())); - } - } else { - // Get the owner of all the studies - Set users = new HashSet<>(); - for (String studyFqn : studyIds) { - users.add(StringUtils.split(studyFqn, "@")[0]); - } - - // There must be one single owner for all the studies, we do nt allow to query multiple databases - if (users.size() == 1) { - Query studyQuery = new Query(StudyDBAdaptor.QueryParams.ID.key(), StringUtils.join(studyIds, ",")); - DataResult studyQueryResult = catalogManager.getStudyManager().search(studyQuery, QueryOptions.empty(), token); - - // If the user is the owner we do not have to check anything else - List studyAliases = new ArrayList<>(studyIds.size()); - if (users.contains(userId)) { - for (Study study : studyQueryResult.getResults()) { - studyAliases.add(study.getAlias()); - } - } else { - for (Study study : studyQueryResult.getResults()) { - for (Group group : study.getGroups()) { - if (group.getId().equalsIgnoreCase("@admins") && group.getUserIds().contains(userId)) { - studyAliases.add(study.getAlias()); - break; - } - } - } - } - - if (studyAliases.isEmpty()) { - throw new ClinicalVariantException("This user is not owner or admins for the provided studies"); - } else { - query.put(ClinicalVariantEngine.QueryParams.STUDY.key(), StringUtils.join(studyAliases, ",")); - } - } else { - throw new ClinicalVariantException(""); - } - } - return query; - } - - private void checkInterpretationPermissions(String study, long interpretationId, String token) - throws CatalogException, ClinicalVariantException { - // Get user ID from token and study numeric ID - String studyId = catalogManager.getStudyManager().get(study, StudyManager.INCLUDE_STUDY_IDS, token).first().getFqn(); - - // This checks that the user has permission to this interpretation - Query query = new Query(ClinicalAnalysisDBAdaptor.QueryParams.INTERPRETATION_ID.key(), interpretationId); - DataResult clinicalAnalysisQueryResult = catalogManager.getClinicalAnalysisManager() - .search(studyId, query, QueryOptions.empty(), token); - - if (clinicalAnalysisQueryResult.getResults().isEmpty()) { - throw new ClinicalVariantException("Either the interpretation ID (" + interpretationId + ") does not exist or the user does" - + " not have access permissions"); - } - } - - private List getStudyIds(String userId, Query query) throws CatalogException { - List studyIds = new ArrayList<>(); - - if (query != null && query.containsKey(ClinicalVariantEngine.QueryParams.STUDY.key())) { - String study = query.getString(ClinicalVariantEngine.QueryParams.STUDY.key()); - List studies = Arrays.asList(study.split(",")); - studyIds = catalogManager.getStudyManager().get(studies, StudyManager.INCLUDE_STUDY_IDS, false, userId) - .getResults() - .stream() - .map(Study::getFqn) - .collect(Collectors.toList()); - } - return studyIds; - } - - private boolean isCaseProvided(Query query) { - if (query != null) { - return query.containsKey(ClinicalVariantEngine.QueryParams.CLINICAL_ANALYSIS_ID.key()) - || query.containsKey(ClinicalVariantEngine.QueryParams.FAMILY.key()) - || query.containsKey(ClinicalVariantEngine.QueryParams.SUBJECT.key()) - || query.containsKey(ClinicalVariantEngine.QueryParams.SAMPLE.key()); - } - return false; - } +// // FIXME Class path to a new section in storage-configuration.yml file +// private void init() { +// try { +// this.database = catalogManager.getConfiguration().getDatabasePrefix() + "_clinical"; +// +// this.clinicalVariantEngine = getClinicalStorageEngine(); +// } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) { +// e.printStackTrace(); +// } +// +// } +// +// private ClinicalVariantEngine getClinicalStorageEngine() throws ClassNotFoundException, IllegalAccessException, InstantiationException { +// String clazz = this.storageConfiguration.getClinical().getManager(); +// ClinicalVariantEngine storageEngine = (ClinicalVariantEngine) Class.forName(clazz).newInstance(); +// storageEngine.setStorageConfiguration(this.storageConfiguration); +// return storageEngine; +// } +// +// private Query checkQueryPermissions(Query query, String token) throws ClinicalVariantException, CatalogException { +// if (query == null) { +// throw new ClinicalVariantException("Query object is null"); +// } +// +// // Get userId from token and Study numeric IDs from the query +// String userId = catalogManager.getUserManager().getUserId(token); +// List studyIds = getStudyIds(userId, query); +// +// // If one specific clinical analysis, sample or individual is provided we expect a single valid study as well +// if (isCaseProvided(query)) { +// if (studyIds.size() == 1) { +// // This checks that the user has permission to the clinical analysis, family, sample or individual +// DataResult clinicalAnalysisQueryResult = catalogManager.getClinicalAnalysisManager() +// .search(studyIds.get(0), query, QueryOptions.empty(), token); +// +// if (clinicalAnalysisQueryResult.getResults().isEmpty()) { +// throw new ClinicalVariantException("Either the ID does not exist or the user does not have permissions to view it"); +// } else { +// if (!query.containsKey(ClinicalVariantEngine.QueryParams.CLINICAL_ANALYSIS_ID.key())) { +// query.remove(ClinicalVariantEngine.QueryParams.FAMILY.key()); +// query.remove(ClinicalVariantEngine.QueryParams.SAMPLE.key()); +// query.remove(ClinicalVariantEngine.QueryParams.SUBJECT.key()); +// String clinicalAnalysisList = StringUtils.join( +// clinicalAnalysisQueryResult.getResults().stream().map(ClinicalAnalysis::getId).collect(Collectors.toList()), +// ","); +// query.put("clinicalAnalysisId", clinicalAnalysisList); +// } +// } +// } else { +// throw new ClinicalVariantException("No single valid study provided: " +// + query.getString(ClinicalVariantEngine.QueryParams.STUDY.key())); +// } +// } else { +// // Get the owner of all the studies +// Set users = new HashSet<>(); +// for (String studyFqn : studyIds) { +// users.add(StringUtils.split(studyFqn, "@")[0]); +// } +// +// // There must be one single owner for all the studies, we do nt allow to query multiple databases +// if (users.size() == 1) { +// Query studyQuery = new Query(StudyDBAdaptor.QueryParams.ID.key(), StringUtils.join(studyIds, ",")); +// DataResult studyQueryResult = catalogManager.getStudyManager().search(studyQuery, QueryOptions.empty(), token); +// +// // If the user is the owner we do not have to check anything else +// List studyAliases = new ArrayList<>(studyIds.size()); +// if (users.contains(userId)) { +// for (Study study : studyQueryResult.getResults()) { +// studyAliases.add(study.getAlias()); +// } +// } else { +// for (Study study : studyQueryResult.getResults()) { +// for (Group group : study.getGroups()) { +// if (group.getId().equalsIgnoreCase("@admins") && group.getUserIds().contains(userId)) { +// studyAliases.add(study.getAlias()); +// break; +// } +// } +// } +// } +// +// if (studyAliases.isEmpty()) { +// throw new ClinicalVariantException("This user is not owner or admins for the provided studies"); +// } else { +// query.put(ClinicalVariantEngine.QueryParams.STUDY.key(), StringUtils.join(studyAliases, ",")); +// } +// } else { +// throw new ClinicalVariantException(""); +// } +// } +// return query; +// } +// +// private void checkInterpretationPermissions(String study, long interpretationId, String token) +// throws CatalogException, ClinicalVariantException { +// // Get user ID from token and study numeric ID +// String studyId = catalogManager.getStudyManager().get(study, StudyManager.INCLUDE_STUDY_IDS, token).first().getFqn(); +// +// // This checks that the user has permission to this interpretation +// Query query = new Query(ClinicalAnalysisDBAdaptor.QueryParams.INTERPRETATION_ID.key(), interpretationId); +// DataResult clinicalAnalysisQueryResult = catalogManager.getClinicalAnalysisManager() +// .search(studyId, query, QueryOptions.empty(), token); +// +// if (clinicalAnalysisQueryResult.getResults().isEmpty()) { +// throw new ClinicalVariantException("Either the interpretation ID (" + interpretationId + ") does not exist or the user does" +// + " not have access permissions"); +// } +// } +// +// private List getStudyIds(String userId, Query query) throws CatalogException { +// List studyIds = new ArrayList<>(); +// +// if (query != null && query.containsKey(ClinicalVariantEngine.QueryParams.STUDY.key())) { +// String study = query.getString(ClinicalVariantEngine.QueryParams.STUDY.key()); +// List studies = Arrays.asList(study.split(",")); +// studyIds = catalogManager.getStudyManager().get(studies, StudyManager.INCLUDE_STUDY_IDS, false, userId) +// .getResults() +// .stream() +// .map(Study::getFqn) +// .collect(Collectors.toList()); +// } +// return studyIds; +// } +// +// private boolean isCaseProvided(Query query) { +// if (query != null) { +// return query.containsKey(ClinicalVariantEngine.QueryParams.CLINICAL_ANALYSIS_ID.key()) +// || query.containsKey(ClinicalVariantEngine.QueryParams.FAMILY.key()) +// || query.containsKey(ClinicalVariantEngine.QueryParams.SUBJECT.key()) +// || query.containsKey(ClinicalVariantEngine.QueryParams.SAMPLE.key()); +// } +// return false; +// } private void cleanQuery(Query query) { if (query.containsKey(VariantQueryParam.GENOTYPE.key())) { diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantEngine.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantEngine.java deleted file mode 100644 index 4181d6608c0..00000000000 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantEngine.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2015-2017 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.storage.core.clinical; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; -import org.opencb.biodata.models.clinical.ClinicalComment; -import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; -import org.opencb.biodata.models.clinical.interpretation.Interpretation; -import org.opencb.commons.datastore.core.*; -import org.opencb.commons.utils.FileUtils; -import org.opencb.commons.utils.ListUtils; -import org.opencb.opencga.core.config.storage.StorageConfiguration; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import static org.opencb.commons.datastore.core.QueryParam.Type.*; - -public interface ClinicalVariantEngine { - - enum QueryParams implements QueryParam { - CLINICAL_ANALYSIS_ID("clinicalAnalysisId", INTEGER, ""), - FAMILY("family", TEXT_ARRAY, ""), - SUBJECT("subject", TEXT_ARRAY, ""), - SAMPLE("sample", TEXT_ARRAY, ""), - - STUDY_ID("studyId", INTEGER_ARRAY, ""), - STUDY("study", INTEGER_ARRAY, ""); // Alias to studyId in the database. Only for the webservices. - - private static Map map; - static { - map = new LinkedHashMap<>(); - for (QueryParams params : QueryParams.values()) { - map.put(params.key(), params); - } - } - - private final String key; - private Type type; - private String description; - - QueryParams(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 QueryParams getParam(String key) { - return map.get(key); - } - } - - void create(String dbName) throws ClinicalVariantException; - - boolean isAlive(String collection) throws ClinicalVariantException; - - boolean exists(String dbName) throws ClinicalVariantException; - - void insert(Interpretation interpretation, String collection) throws IOException, ClinicalVariantException; - - /** - * Insert a list of Interpretation objects into Solr: previously each Interpretation object is - * converted to multiple ClinicalVariantSearchModel objects and they will be stored in Solr. - * - * @param interpretations List of Interpretation objects to insert - * @param collection Solr collection where to insert - * @throws IOException IOException - * @throws ClinicalVariantException ClinicalVariantException - */ - default void insert(List interpretations, String collection) throws IOException, ClinicalVariantException { - if (ListUtils.isNotEmpty(interpretations)) { - for (Interpretation interpretation: interpretations) { - insert(interpretation, collection); - } - } - } - - /** - * Load a JSON file containing Interpretation object into the Solr core/collection. - * - * @param interpretationJsonPath Path to the JSON file containing the Interpretation objects - * @param collection Solr collection where to insert - * @throws IOException IOException - * @throws ClinicalVariantException ClinicalVariantException - */ - default void insert(Path interpretationJsonPath, String collection) throws IOException, ClinicalVariantException { - FileUtils.checkFile(interpretationJsonPath); - - ObjectReader objectReader = new ObjectMapper().readerFor(Interpretation.class); - Interpretation interpretation = objectReader.readValue(interpretationJsonPath.toFile()); - insert(interpretation, collection); - } - - DataResult query(Query query, QueryOptions options, String collection) - throws IOException, ClinicalVariantException; - - DataResult interpretationQuery(Query query, QueryOptions options, String collection) - throws IOException, ClinicalVariantException; - - DataResult facet(Query query, QueryOptions queryOptions, String collection) - throws IOException, ClinicalVariantException; - - ClinicalVariantIterator iterator(Query query, QueryOptions options, String collection) - throws ClinicalVariantException, IOException; - - - void addInterpretationComment(long interpretationId, ClinicalComment comment, String collection) - throws IOException, ClinicalVariantException; - - void addClinicalVariantComment(long interpretationId, String variantId, ClinicalComment comment, String collection) - throws IOException, ClinicalVariantException; - - void setStorageConfiguration(StorageConfiguration storageConfiguration); -} diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantException.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantException.java deleted file mode 100644 index 093b5c214a6..00000000000 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2015-2017 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.storage.core.clinical; - -public class ClinicalVariantException extends Exception { - - public ClinicalVariantException(String message) { - super(message); - } - - public ClinicalVariantException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantIterator.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantIterator.java deleted file mode 100644 index 34491193106..00000000000 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantIterator.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2015-2017 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.storage.core.clinical; - - -import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; - -import java.util.Iterator; - -public interface ClinicalVariantIterator extends Iterator, AutoCloseable { -} diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantQueryParam.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantQueryParam.java deleted file mode 100644 index f3fb4255c58..00000000000 --- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/clinical/ClinicalVariantQueryParam.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2015-2017 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.storage.core.clinical; - -import org.opencb.commons.datastore.core.QueryParam; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.opencb.commons.datastore.core.QueryParam.Type.STRING; -import static org.opencb.commons.datastore.core.QueryParam.Type.TEXT_ARRAY; -import static org.opencb.opencga.storage.core.variant.query.VariantQueryUtils.*; - -public final class ClinicalVariantQueryParam implements QueryParam { - - private final String key; - private final Type type; - private final String description; - - private static final List VALUES = new ArrayList<>(); - private static final String ACCEPTS_ALL_NONE = "Accepts '" + ALL + "' and '" + NONE + "'."; - private static final String ACCEPTS_AND_OR = "Accepts AND (" + AND + ") and OR (" + OR + ") operators."; - - // ---------- Catalog - - public static final String PROJECT_ID_DESCR = "List of project IDs"; - public static final ClinicalVariantQueryParam PROJECT_ID = new ClinicalVariantQueryParam("projectId", TEXT_ARRAY, PROJECT_ID_DESCR); - - public static final String ASSEMBLY_DESCR = "List of assemblies"; - public static final ClinicalVariantQueryParam ASSEMBLY = new ClinicalVariantQueryParam("assembly", TEXT_ARRAY, ASSEMBLY_DESCR); - - public static final String STUDY_ID_DESCR = "List of study IDs"; - public static final ClinicalVariantQueryParam STUDY_ID = new ClinicalVariantQueryParam("studyId", TEXT_ARRAY, - STUDY_ID_DESCR); - - // ---------- Clinical Analysis (aka CA) - - public static final String CA_ID_DESCR = "List of clinical analysis IDs"; - public static final ClinicalVariantQueryParam CA_ID = new ClinicalVariantQueryParam("caId", TEXT_ARRAY, - CA_ID_DESCR); - - public static final String CA_NAME_DESCR = "List of clinical analysis names"; - public static final ClinicalVariantQueryParam CA_NAME = new ClinicalVariantQueryParam("caName", TEXT_ARRAY, - CA_NAME_DESCR); - - public static final String CA_DESCRIPTION_DESCR = "Clinical analysis description"; - public static final ClinicalVariantQueryParam CA_DESCRIPTION = new ClinicalVariantQueryParam("caDescription", - TEXT_ARRAY, CA_DESCRIPTION_DESCR); - - public static final String CA_DISORDER_DESCR = "List of clinical analysis disorders"; - public static final ClinicalVariantQueryParam CA_DISORDER = new ClinicalVariantQueryParam("caDisorderId", - TEXT_ARRAY, CA_DISORDER_DESCR); - - public static final String CA_FILE_DESCR = "List of clinical analysis files"; - public static final ClinicalVariantQueryParam CA_FILE = new ClinicalVariantQueryParam("caFiles", TEXT_ARRAY, - CA_FILE_DESCR); - - public static final String CA_PROBAND_ID_DESCR = "List of proband IDs"; - public static final ClinicalVariantQueryParam CA_PROBAND_ID = new ClinicalVariantQueryParam("caProbandId", - TEXT_ARRAY, CA_PROBAND_ID_DESCR); - - public static final String CA_PROBAND_DISORDERS_DESCR = "List of proband disorders"; - public static final ClinicalVariantQueryParam CA_PROBAND_DISORDERS = new ClinicalVariantQueryParam("caProbandDisorders", - TEXT_ARRAY, CA_PROBAND_DISORDERS_DESCR); - - public static final String CA_PROBAND_PHENOTYPES_DESCR = "List of proband phenotypes"; - public static final ClinicalVariantQueryParam CA_PROBAND_PHENOTYPES = new ClinicalVariantQueryParam("caProbandPhenotypes", - TEXT_ARRAY, CA_PROBAND_PHENOTYPES_DESCR); - - public static final String CA_FAMILY_ID_DESCR = "List of family IDs"; - public static final ClinicalVariantQueryParam CA_FAMILY_ID = new ClinicalVariantQueryParam("caFamilyId", TEXT_ARRAY, - CA_FAMILY_ID_DESCR); - - public static final String CA_FAMILY_MEMBER_IDS_DESCR = "List of clinical analysis family member IDs"; - public static final ClinicalVariantQueryParam CA_FAMILY_MEMBER_IDS = new ClinicalVariantQueryParam("caFamilyMemberIds", TEXT_ARRAY, - CA_FAMILY_MEMBER_IDS_DESCR); - - public static final String CA_COMMENTS_DESCR = "List of clinical analysis comments"; - public static final ClinicalVariantQueryParam CA_COMMENTS = new ClinicalVariantQueryParam("caComments", TEXT_ARRAY, - CA_COMMENTS_DESCR); - - public static final String CA_INFO_DESCR = ""; - public static final ClinicalVariantQueryParam CA_INFO = new ClinicalVariantQueryParam("caInfo", TEXT_ARRAY, CA_INFO_DESCR); - - // ---------- Interpretation (aka INT) - - public static final String INT_ID_DESCR = "List of interpretation IDs"; - public static final ClinicalVariantQueryParam INT_ID = new ClinicalVariantQueryParam("intId", TEXT_ARRAY, INT_ID_DESCR); - - public static final String INT_SOFTWARE_NAME_DESCR = "List of interpretation software names"; - public static final ClinicalVariantQueryParam INT_SOFTWARE_NAME = new ClinicalVariantQueryParam("intSoftwareName", TEXT_ARRAY, - INT_SOFTWARE_NAME_DESCR); - - public static final String INT_SOFTWARE_VERSION_DESCR = "List of interpretation software versions"; - public static final ClinicalVariantQueryParam INT_SOFTWARE_VERSION = new ClinicalVariantQueryParam("intSoftwareVersion", TEXT_ARRAY, - INT_SOFTWARE_VERSION_DESCR); - - public static final String INT_ANALYST_NAME_DESCR = "List of interpretation analysist names"; - public static final ClinicalVariantQueryParam INT_ANALYST_NAME = new ClinicalVariantQueryParam("intAnalystName", TEXT_ARRAY, - INT_ANALYST_NAME_DESCR); - - public static final String INT_PANELS_DESCR = "List of interpretation panels"; - public static final ClinicalVariantQueryParam INT_PANELS = new ClinicalVariantQueryParam("intPanels", TEXT_ARRAY, - INT_PANELS_DESCR); - - public static final String INT_INFO_DESCR = ""; - public static final ClinicalVariantQueryParam INT_INFO = new ClinicalVariantQueryParam("intInfo", TEXT_ARRAY, INT_INFO_DESCR); - - public static final String INT_DESCRIPTION_DESCR = "Interpretation description"; - public static final ClinicalVariantQueryParam INT_DESCRIPTION = new ClinicalVariantQueryParam("intDescription", TEXT_ARRAY, - INT_DESCRIPTION_DESCR); - - public static final String INT_DEPENDENCY_DESCR = "List of interpretation dependency, format: name:version, e.g. cellbase:4.0"; - public static final ClinicalVariantQueryParam INT_DEPENDENCY = new ClinicalVariantQueryParam("intDependency", TEXT_ARRAY, - INT_DEPENDENCY_DESCR); - - public static final String INT_FILTERS_DESCR = "List of interpretation filters"; - public static final ClinicalVariantQueryParam INT_FILTERS = new ClinicalVariantQueryParam("intFilters", TEXT_ARRAY, - INT_FILTERS_DESCR); - - public static final String INT_COMMENTS_DESCR = "List of interpretation comments"; - public static final ClinicalVariantQueryParam INT_COMMENTS = new ClinicalVariantQueryParam("intComments", TEXT_ARRAY, - INT_COMMENTS_DESCR); - - public static final String INT_CREATION_DATE_DESCR = "Iinterpretation creation date (including date ranges)"; - public static final ClinicalVariantQueryParam INT_CREATION_DATE = new ClinicalVariantQueryParam("intCreationDate", STRING, - INT_CREATION_DATE_DESCR); - - // ---------- Reported variant (aka RV) - - public static final String RV_DE_NOVO_QUALITY_SCORE_DESCR = "List of reported variant de novo quality scores"; - public static final ClinicalVariantQueryParam RV_DE_NOVO_QUALITY_SCORE = new ClinicalVariantQueryParam("rvDeNovoQualityScore", - TEXT_ARRAY, RV_DE_NOVO_QUALITY_SCORE_DESCR); - - public static final String RV_COMMENTS_DESCR = "List of reported variant comments"; - public static final ClinicalVariantQueryParam RV_COMMENTS = new ClinicalVariantQueryParam("rvComments", TEXT_ARRAY, - RV_COMMENTS_DESCR); - - // ---------- Reported event (aka RE) - - public static final String RE_PHENOTYPE_NAMES_DESCR = "List of reported event phenotype names"; - public static final ClinicalVariantQueryParam RE_PHENOTYPE_NAMES = new ClinicalVariantQueryParam("rePhenotypeNames", TEXT_ARRAY, - RE_PHENOTYPE_NAMES_DESCR); - - public static final String RE_CONSEQUENCE_TYPE_IDS_DESCR = "List of reported event consequence type IDs"; - public static final ClinicalVariantQueryParam RE_CONSEQUENCE_TYPE_IDS = new ClinicalVariantQueryParam("reConsequenceTypeIds", - TEXT_ARRAY, RE_CONSEQUENCE_TYPE_IDS_DESCR); - - public static final String RE_GENE_NAMES_DESCR = "List of reported event gene names"; - public static final ClinicalVariantQueryParam RE_GENE_NAMES = new ClinicalVariantQueryParam("reGeneNames", TEXT_ARRAY, - RE_GENE_NAMES_DESCR); - - public static final String RE_XREFS_DESCR = "List of reported event phenotype xRefs"; - public static final ClinicalVariantQueryParam RE_XREFS = new ClinicalVariantQueryParam("reXrefs", TEXT_ARRAY, RE_XREFS_DESCR); - - public static final String RE_PANEL_IDS_DESCR = "List of reported event panel IDs"; - public static final ClinicalVariantQueryParam RE_PANEL_IDS = new ClinicalVariantQueryParam("rePanelNames", TEXT_ARRAY, - RE_PANEL_IDS_DESCR); - - public static final String RE_ACMG_DESCR = "List of reported event ACMG"; - public static final ClinicalVariantQueryParam RE_ACMG = new ClinicalVariantQueryParam("reAcmg", TEXT_ARRAY, RE_ACMG_DESCR); - - public static final String RE_CLINICAL_SIGNIFICANCE_DESCR = "List of reported event clinical significance"; - public static final ClinicalVariantQueryParam RE_CLINICAL_SIGNIFICANCE = new ClinicalVariantQueryParam("reClinicalSignificance", - TEXT_ARRAY, RE_CLINICAL_SIGNIFICANCE_DESCR); - - public static final String RE_DRUG_RESPONSE_DESCR = "List of reported event drug response"; - public static final ClinicalVariantQueryParam RE_DRUG_RESPONSE = new ClinicalVariantQueryParam("reDrugResponse", TEXT_ARRAY, - RE_DRUG_RESPONSE_DESCR); - - public static final String RE_TRAIT_ASSOCIATION_DESCR = "List of reported event trait association"; - public static final ClinicalVariantQueryParam RE_TRAIT_ASSOCIATION = new ClinicalVariantQueryParam("reTraitAssociation", TEXT_ARRAY, - RE_TRAIT_ASSOCIATION_DESCR); - - public static final String RE_FUNCTIONAL_EFFECT_DESCR = "List of reported event functional effect"; - public static final ClinicalVariantQueryParam RE_FUNCTIONAL_EFFECT = new ClinicalVariantQueryParam("reFunctionalEffect", TEXT_ARRAY, - RE_FUNCTIONAL_EFFECT_DESCR); - - public static final String RE_TUMORIGENESIS_DESCR = "List of reported event tumorigenesis"; - public static final ClinicalVariantQueryParam RE_TUMORIGENESIS = new ClinicalVariantQueryParam("reTumorigenesis", TEXT_ARRAY, - RE_TUMORIGENESIS_DESCR); - - public static final String RE_OTHER_CLASSIFICATION_DESCR = "List of reported event other classification"; - public static final ClinicalVariantQueryParam RE_OTHER_CLASSIFICATION = new ClinicalVariantQueryParam("reOtherClassification", - TEXT_ARRAY, RE_OTHER_CLASSIFICATION_DESCR); - - public static final String RE_ROLES_IN_CANCER_DESCR = "List of reported event roles in cancer"; - public static final ClinicalVariantQueryParam RE_ROLES_IN_CANCER = new ClinicalVariantQueryParam("reRolesInCancer", TEXT_ARRAY, - RE_ROLES_IN_CANCER_DESCR); - - public static final String RE_TIER_DESCR = "List of reported event tier"; - public static final ClinicalVariantQueryParam RE_TIER = new ClinicalVariantQueryParam("reTier", TEXT_ARRAY, RE_TIER_DESCR); - - public static final String RE_JUSTIFICATION_DESCR = "List of reported event justification"; - public static final ClinicalVariantQueryParam RE_JUSTIFICATION = new ClinicalVariantQueryParam("reJustification", TEXT_ARRAY, - RE_JUSTIFICATION_DESCR); - - public static final String RE_AUX_DESCR = ""; - public static final ClinicalVariantQueryParam RE_AUX = new ClinicalVariantQueryParam("reAux", TEXT_ARRAY, RE_AUX_DESCR); - - // Constructor - private ClinicalVariantQueryParam(String key, Type type, String description) { - this.key = key; - this.type = type; - this.description = description; - - VALUES.add(this); - } - - @Override - public String key() { - return key; - } - - @Override - public String description() { - return description; - } - - @Override - public Type type() { - return type; - } - - @Override - public String toString() { - return key() + " [" + type() + "] : " + description(); - } - - public static List values() { - return Collections.unmodifiableList(VALUES); - } -} From 5e87324a5ac74f0c93c866d1adebc1ecac28eda1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Tue, 20 Jun 2023 12:33:31 +0200 Subject: [PATCH 03/18] app: implement CLI to load clinical analyses from a file, #TASK-4610, #TASK-4158 --- .../clinical/ClinicalAnalysisLoadTask.java | 41 ++++++ .../internal/InternalCliOptionsParser.java | 2 + .../executors/ClinicalCommandExecutor.java | 29 ++++- .../options/ClinicalCommandOptions.java | 18 +++ .../app/cli/main/OpenCgaCompleter.java | 4 +- .../app/cli/main/OpencgaCliOptionsParser.java | 3 +- ...CustomAnalysisClinicalCommandExecutor.java | 59 +++++++++ .../CustomAnalysisClinicalCommandOptions.java | 53 ++++++++ .../custom/CustomFilesCommandOptions.java | 9 -- .../AnalysisClinicalCommandExecutor.java | 56 +++++++++ .../AnalysisClinicalCommandOptions.java | 11 +- .../managers/ClinicalAnalysisManager.java | 118 +++++++++++++++++- .../managers/ClinicalAnalysisTest.java | 58 +++++++++ .../resources/load-clinical-analyses.conf | 5 + opencga-client/src/main/R/R/Admin-methods.R | 2 +- .../src/main/R/R/Alignment-methods.R | 2 +- opencga-client/src/main/R/R/AllGenerics.R | 16 +-- .../src/main/R/R/Clinical-methods.R | 16 ++- opencga-client/src/main/R/R/Cohort-methods.R | 4 +- opencga-client/src/main/R/R/Family-methods.R | 4 +- opencga-client/src/main/R/R/File-methods.R | 4 +- opencga-client/src/main/R/R/GA4GH-methods.R | 2 +- .../src/main/R/R/Individual-methods.R | 4 +- opencga-client/src/main/R/R/Job-methods.R | 2 +- opencga-client/src/main/R/R/Meta-methods.R | 2 +- .../src/main/R/R/Operation-methods.R | 2 +- opencga-client/src/main/R/R/Panel-methods.R | 2 +- opencga-client/src/main/R/R/Project-methods.R | 2 +- opencga-client/src/main/R/R/Sample-methods.R | 4 +- opencga-client/src/main/R/R/Study-methods.R | 4 +- opencga-client/src/main/R/R/User-methods.R | 4 +- opencga-client/src/main/R/R/Variant-methods.R | 2 +- .../client/rest/clients/AdminClient.java | 2 +- .../client/rest/clients/AlignmentClient.java | 2 +- .../rest/clients/ClinicalAnalysisClient.java | 21 +++- .../client/rest/clients/CohortClient.java | 2 +- .../rest/clients/DiseasePanelClient.java | 2 +- .../client/rest/clients/FamilyClient.java | 2 +- .../client/rest/clients/FileClient.java | 2 +- .../client/rest/clients/GA4GHClient.java | 2 +- .../client/rest/clients/IndividualClient.java | 2 +- .../client/rest/clients/JobClient.java | 2 +- .../client/rest/clients/MetaClient.java | 2 +- .../client/rest/clients/ProjectClient.java | 2 +- .../client/rest/clients/SampleClient.java | 2 +- .../client/rest/clients/StudyClient.java | 2 +- .../client/rest/clients/UserClient.java | 2 +- .../client/rest/clients/VariantClient.java | 2 +- .../rest/clients/VariantOperationClient.java | 2 +- opencga-client/src/main/javascript/Admin.js | 2 +- .../src/main/javascript/Alignment.js | 2 +- .../src/main/javascript/ClinicalAnalysis.js | 17 ++- opencga-client/src/main/javascript/Cohort.js | 2 +- .../src/main/javascript/DiseasePanel.js | 2 +- opencga-client/src/main/javascript/Family.js | 2 +- opencga-client/src/main/javascript/File.js | 2 +- opencga-client/src/main/javascript/GA4GH.js | 2 +- .../src/main/javascript/Individual.js | 2 +- opencga-client/src/main/javascript/Job.js | 2 +- opencga-client/src/main/javascript/Meta.js | 2 +- opencga-client/src/main/javascript/Project.js | 2 +- opencga-client/src/main/javascript/Sample.js | 2 +- opencga-client/src/main/javascript/Study.js | 2 +- opencga-client/src/main/javascript/User.js | 2 +- opencga-client/src/main/javascript/Variant.js | 2 +- .../src/main/javascript/VariantOperation.js | 2 +- .../pyopencga/rest_clients/admin_client.py | 2 +- .../rest_clients/alignment_client.py | 2 +- .../rest_clients/clinical_analysis_client.py | 21 +++- .../pyopencga/rest_clients/cohort_client.py | 2 +- .../rest_clients/disease_panel_client.py | 2 +- .../pyopencga/rest_clients/family_client.py | 2 +- .../pyopencga/rest_clients/file_client.py | 2 +- .../pyopencga/rest_clients/ga4gh_client.py | 2 +- .../rest_clients/individual_client.py | 2 +- .../pyopencga/rest_clients/job_client.py | 2 +- .../pyopencga/rest_clients/meta_client.py | 2 +- .../pyopencga/rest_clients/project_client.py | 2 +- .../pyopencga/rest_clients/sample_client.py | 2 +- .../pyopencga/rest_clients/study_client.py | 2 +- .../pyopencga/rest_clients/user_client.py | 2 +- .../pyopencga/rest_clients/variant_client.py | 2 +- .../rest_clients/variant_operation_client.py | 2 +- .../clinical/ClinicalAnalysisLoadParams.java | 37 ++++++ .../rest/analysis/ClinicalWebService.java | 55 +++++++- .../src/main/resources/cli-config.yaml | 5 + 86 files changed, 686 insertions(+), 110 deletions(-) create mode 100644 opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java create mode 100644 opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java create mode 100644 opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java create mode 100644 opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java create mode 100644 opencga-catalog/src/test/resources/load-clinical-analyses.conf create mode 100644 opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java new file mode 100644 index 00000000000..e460463bd48 --- /dev/null +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java @@ -0,0 +1,41 @@ +package org.opencb.opencga.analysis.clinical; + +import org.apache.commons.lang3.StringUtils; +import org.opencb.opencga.analysis.tools.OpenCgaToolScopeStudy; +import org.opencb.opencga.core.exceptions.ToolException; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysisLoadParams; +import org.opencb.opencga.core.models.common.Enums; +import org.opencb.opencga.core.models.panel.PanelImportParams; +import org.opencb.opencga.core.tools.annotations.Tool; +import org.opencb.opencga.core.tools.annotations.ToolParams; + +@Tool(id = ClinicalAnalysisLoadTask.ID, resource = Enums.Resource.DISEASE_PANEL, description = ClinicalAnalysisLoadTask.DESCRIPTION) +public class ClinicalAnalysisLoadTask extends OpenCgaToolScopeStudy { + public final static String ID = "load"; + public static final String DESCRIPTION = "Load clinical analyses from a file"; + + private String studyFqn; + + @ToolParams + protected ClinicalAnalysisLoadParams params = new ClinicalAnalysisLoadParams(); + + @Override + protected void check() throws Exception { + super.check(); + studyFqn = getStudy(); + + if (StringUtils.isEmpty(params.getPath().toString())) { + throw new ToolException("Missing input file when loading clinical analyses."); + } + + if (!params.getPath().toFile().exists()) { + throw new ToolException("Input file '" + params.getPath() + "' does not exist."); + } + } + + @Override + protected void run() throws Exception { + step(() -> catalogManager.getClinicalAnalysisManager().load(studyFqn, params.getPath(), 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 f11d519c846..dfd35997152 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 @@ -35,6 +35,7 @@ import static org.opencb.opencga.app.cli.internal.options.AlignmentCommandOptions.SamtoolsCommandOptions.SAMTOOLS_RUN_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.CancerTieringCommandOptions.CANCER_TIERING_INTERPRETATION_RUN_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.ExomiserInterpretationCommandOptions.EXOMISER_INTERPRETATION_RUN_COMMAND; +import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.ImportClinicalAnalysesCommandOptions.IMPORT_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.RgaAuxiliarSecondaryIndexCommandOptions.RGA_AUX_INDEX_RUN_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.RgaSecondaryIndexCommandOptions.RGA_INDEX_RUN_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.TeamCommandOptions.TEAM_INTERPRETATION_RUN_COMMAND; @@ -226,6 +227,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(IMPORT_COMMAND, clinicalCommandOptions.importClinicalAnalysesCommandOptions); 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..43571e7c6ca 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 @@ -17,6 +17,7 @@ package org.opencb.opencga.app.cli.internal.executors; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -37,14 +38,33 @@ import org.opencb.opencga.analysis.clinical.zetta.ZettaInterpretationConfiguration; import org.opencb.opencga.analysis.variant.manager.VariantCatalogQueryUtils; import org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions; +import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.managers.ClinicalAnalysisManager; +import org.opencb.opencga.catalog.managers.SampleManager; +import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.common.JacksonUtils; import org.opencb.opencga.core.exceptions.ToolException; -import org.opencb.opencga.core.models.clinical.RgaAnalysisParams; - +import org.opencb.opencga.core.models.clinical.*; +import org.opencb.opencga.core.models.family.FamilyCreateParams; +import org.opencb.opencga.core.models.individual.Individual; +import org.opencb.opencga.core.models.individual.IndividualUpdateParams; +import org.opencb.opencga.core.models.panel.Panel; +import org.opencb.opencga.core.models.panel.PanelCreateParams; +import org.opencb.opencga.core.models.sample.Sample; +import org.opencb.opencga.core.models.sample.SampleCreateParams; +import org.opencb.opencga.core.models.sample.SampleReferenceParam; +import org.opencb.opencga.core.response.RestResponse; + +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.CancerTieringCommandOptions.CANCER_TIERING_INTERPRETATION_RUN_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.ExomiserInterpretationCommandOptions.EXOMISER_INTERPRETATION_RUN_COMMAND; @@ -53,6 +73,8 @@ import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.TeamCommandOptions.TEAM_INTERPRETATION_RUN_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.TieringCommandOptions.TIERING_INTERPRETATION_RUN_COMMAND; import static org.opencb.opencga.app.cli.internal.options.ClinicalCommandOptions.ZettaCommandOptions.ZETTA_INTERPRETATION_RUN_COMMAND; +import static org.opencb.opencga.catalog.utils.ParamUtils.SaveInterpretationAs.PRIMARY; +import static org.opencb.opencga.catalog.utils.ParamUtils.SaveInterpretationAs.SECONDARY; /** * Created on 01/04/20 @@ -96,6 +118,9 @@ public void execute() throws Exception { case EXOMISER_INTERPRETATION_RUN_COMMAND: exomiserInterpretation(); break; + case ClinicalCommandOptions.ImportClinicalAnalysesCommandOptions.IMPORT_COMMAND: + //importClinicalAnalyses(); + break; default: logger.error("Subcommand not valid"); break; 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..a8f9617b494 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 @@ -33,6 +33,7 @@ public class ClinicalCommandOptions { public final RgaSecondaryIndexCommandOptions rgaSecondaryIndexCommandOptions; public final RgaAuxiliarSecondaryIndexCommandOptions rgaAuxiliarSecondaryIndexCommandOptions; public final ExomiserInterpretationCommandOptions exomiserInterpretationCommandOptions; + public final ImportClinicalAnalysesCommandOptions importClinicalAnalysesCommandOptions; public JCommander jCommander; public GeneralCliOptions.CommonCommandOptions commonCommandOptions; @@ -53,6 +54,7 @@ public ClinicalCommandOptions(GeneralCliOptions.CommonCommandOptions commonComma this.rgaSecondaryIndexCommandOptions = new RgaSecondaryIndexCommandOptions(); this.rgaAuxiliarSecondaryIndexCommandOptions = new RgaAuxiliarSecondaryIndexCommandOptions(); this.exomiserInterpretationCommandOptions = new ExomiserInterpretationCommandOptions(); + this.importClinicalAnalysesCommandOptions = new ImportClinicalAnalysesCommandOptions(); } @Parameters(commandNames = {TieringCommandOptions.TIERING_INTERPRETATION_RUN_COMMAND}, commandDescription = @@ -335,4 +337,20 @@ 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 = {ImportClinicalAnalysesCommandOptions.IMPORT_COMMAND}, + commandDescription = "Import clinical analyses from a folder") + public class ImportClinicalAnalysesCommandOptions extends GeneralCliOptions.StudyOption { + + public static final String IMPORT_COMMAND = "import"; + + @ParametersDelegate + public GeneralCliOptions.CommonCommandOptions commonOptions = commonCommandOptions; + + @ParametersDelegate + public InternalCliOptionsParser.JobOptions jobOptions = internalJobOptions; + + @Parameter(names = {"-i", "--input"}, description = "Input directory where clinical analysis JSON files are located", arity = 1) + public String input; + } } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java index b97ba444f13..42eb28c22bb 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java @@ -1,5 +1,5 @@ /* -* Copyright 2015-2023-03-29 OpenCB +* Copyright 2015-2023-06-20 OpenCB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ public abstract class OpenCgaCompleter implements Completer { .map(Candidate::new) .collect(toList()); - private List clinicalList = asList( "acl-update","clinical-configuration-update","create","distinct","interpretation-distinct","interpretation-search","interpretation-info","interpreter-cancer-tiering-run","interpreter-exomiser-run","interpreter-team-run","interpreter-tiering-run","interpreter-zetta-run","rga-aggregation-stats","rga-gene-query","rga-gene-summary","rga-index-run","rga-individual-query","rga-individual-summary","rga-variant-query","rga-variant-summary","search","variant-query","acl","delete","update","info","interpretation-create","interpretation-clear","interpretation-delete","interpretation-revert","interpretation-update") + private List clinicalList = asList( "acl-update","clinical-configuration-update","create","distinct","interpretation-distinct","interpretation-search","interpretation-info","interpreter-cancer-tiering-run","interpreter-exomiser-run","interpreter-team-run","interpreter-tiering-run","interpreter-zetta-run","load","rga-aggregation-stats","rga-gene-query","rga-gene-summary","rga-index-run","rga-individual-query","rga-individual-summary","rga-variant-query","rga-variant-summary","search","variant-query","acl","delete","update","info","interpretation-create","interpretation-clear","interpretation-delete","interpretation-revert","interpretation-update") .stream() .map(Candidate::new) .collect(toList()); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java index 79c411f8b82..025c93cc261 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java @@ -1,5 +1,5 @@ /* -* Copyright 2015-2023-03-29 OpenCB +* Copyright 2015-2023-06-20 OpenCB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -139,6 +139,7 @@ public OpencgaCliOptionsParser() { analysisClinicalSubCommands.addCommand("interpreter-team-run", analysisClinicalCommandOptions.runInterpreterTeamCommandOptions); analysisClinicalSubCommands.addCommand("interpreter-tiering-run", analysisClinicalCommandOptions.runInterpreterTieringCommandOptions); analysisClinicalSubCommands.addCommand("interpreter-zetta-run", analysisClinicalCommandOptions.runInterpreterZettaCommandOptions); + analysisClinicalSubCommands.addCommand("load", analysisClinicalCommandOptions.loadCommandOptions); analysisClinicalSubCommands.addCommand("rga-aggregation-stats", analysisClinicalCommandOptions.aggregationStatsRgaCommandOptions); analysisClinicalSubCommands.addCommand("rga-gene-query", analysisClinicalCommandOptions.queryRgaGeneCommandOptions); analysisClinicalSubCommands.addCommand("rga-gene-summary", analysisClinicalCommandOptions.summaryRgaGeneCommandOptions); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java new file mode 100644 index 00000000000..839fae794c6 --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015-2017 OpenCB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this project 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.app.cli.main.custom; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.opencb.commons.datastore.core.ObjectMap; +import org.opencb.opencga.analysis.panel.PanelImportTask; +import org.opencb.opencga.app.cli.session.SessionManager; +import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.io.IOManager; +import org.opencb.opencga.catalog.io.IOManagerFactory; +import org.opencb.opencga.client.config.ClientConfiguration; +import org.opencb.opencga.client.rest.OpenCGAClient; +import org.opencb.opencga.core.models.job.Job; +import org.opencb.opencga.core.models.study.TemplateParams; +import org.opencb.opencga.core.response.RestResponse; +import org.slf4j.Logger; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.LinkedList; +import java.util.List; + +public class CustomAnalysisClinicalCommandExecutor extends CustomCommandExecutor { + + public CustomAnalysisClinicalCommandExecutor(ObjectMap options, String token, ClientConfiguration clientConfiguration, + SessionManager session, String appHome, Logger logger) { + super(options, token, clientConfiguration, session, appHome, logger); + } + + public CustomAnalysisClinicalCommandExecutor(ObjectMap options, String token, ClientConfiguration clientConfiguration, + SessionManager session, String appHome, Logger logger, OpenCGAClient openCGAClient) { + super(options, token, clientConfiguration, session, appHome, logger, openCGAClient); + } + + public RestResponse load() throws Exception { + logger.debug("Load clinical analyses from file"); + return openCGAClient.getClinicalAnalysisClient().load(options.getString("study"), options); + } +} diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java new file mode 100644 index 00000000000..72229cb1142 --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java @@ -0,0 +1,53 @@ +package org.opencb.opencga.app.cli.main.custom; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import com.beust.jcommander.ParametersDelegate; +import org.opencb.opencga.analysis.clinical.ClinicalAnalysisLoadTask; + +import static org.opencb.opencga.app.cli.GeneralCliOptions.CommonCommandOptions; + +/** + * This class contains methods for the Files command line. OpenCGA version: 2.2.0-SNAPSHOT PATH: /{apiVersion}/files + */ +@Parameters(commandNames = {"studies"}, commandDescription = "Studies commands") +public class CustomAnalysisClinicalCommandOptions { + + public JCommander jCommander; + public CommonCommandOptions commonCommandOptions; + public LoadCommandOptions loadCommandOptions; + + + public CustomAnalysisClinicalCommandOptions(CommonCommandOptions commonCommandOptions, JCommander jCommander) { + + this.jCommander = jCommander; + this.commonCommandOptions = commonCommandOptions; + this.loadCommandOptions = new LoadCommandOptions(); + } + + @Parameters(commandNames = ClinicalAnalysisLoadTask.ID, commandDescription = ClinicalAnalysisLoadTask.DESCRIPTION) + public class LoadCommandOptions { + @ParametersDelegate + public CommonCommandOptions commonOptions = commonCommandOptions; + + @Parameter(names = {"--study", "-s"}, description = "Study [[user@]project:]study where study and project can be either the ID or" + + " UUID", required = false, arity = 1) + public String study; + + @Parameter(names = {"-i", "--input"}, description = "GZIP file containing the clinical analyses", required = true, arity = 1) + public String file; + + @Parameter(names = {"--jobId"}, description = "Job id.", arity = 1) + public String jobId; + + @Parameter(names = {"--jobDependsOn"}, description = "Job depends on.", arity = 1) + public String jobDependsOn; + + @Parameter(names = {"--jobDescription"}, description = "Job description.", arity = 1) + public String jobDescription; + + @Parameter(names = {"--jobTags"}, description = "Job tags.", arity = 1) + public String jobTags; + } +} \ No newline at end of file diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomFilesCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomFilesCommandOptions.java index 63d8640d6a8..30e7759c518 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomFilesCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomFilesCommandOptions.java @@ -7,15 +7,6 @@ import org.opencb.opencga.app.cli.GeneralCliOptions; import static org.opencb.opencga.app.cli.GeneralCliOptions.CommonCommandOptions; -/* - * WARNING: AUTOGENERATED CODE - * - * This code was generated by a tool. - * Autogenerated on: 2021-10-11 - * - * Manual changes to this file may cause unexpected behavior in your application. - * Manual changes to this file will be overwritten if the code is regenerated. - */ /** * This class contains methods for the Files command line. OpenCGA version: 2.2.0-SNAPSHOT PATH: /{apiVersion}/files diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java index 3039ab1cc68..828b035a676 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java @@ -18,6 +18,40 @@ import org.opencb.opencga.app.cli.main.options.AnalysisClinicalCommandOptions; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandOptions; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; +import java.io.InputStream; import org.opencb.biodata.models.clinical.ClinicalDiscussion; import org.opencb.biodata.models.clinical.ClinicalProperty; import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; @@ -133,6 +167,9 @@ public void execute() throws Exception { case "interpreter-zetta-run": queryResponse = runInterpreterZetta(); break; + case "load": + queryResponse = load(); + break; case "rga-aggregation-stats": queryResponse = aggregationStatsRga(); break; @@ -689,6 +726,25 @@ private RestResponse runInterpreterZetta() throws Exception { return openCGAClient.getClinicalAnalysisClient().runInterpreterZetta(zettaInterpretationAnalysisParams, queryParams); } + private RestResponse load() throws Exception { + logger.debug("Executing load in Analysis - Clinical command line"); + + CustomAnalysisClinicalCommandOptions.LoadCommandOptions commandOptions = analysisClinicalCommandOptions.loadCommandOptions; + ObjectMap queryParams = new ObjectMap(); + queryParams.putIfNotNull("file", commandOptions.file); + queryParams.putIfNotEmpty("study", commandOptions.study); + queryParams.putIfNotEmpty("study", commandOptions.study); + queryParams.putIfNotEmpty("jobId", commandOptions.jobId); + queryParams.putIfNotEmpty("jobDescription", commandOptions.jobDescription); + queryParams.putIfNotEmpty("jobDependsOn", commandOptions.jobDependsOn); + queryParams.putIfNotEmpty("jobTags", commandOptions.jobTags); + if (queryParams.get("study") == null && OpencgaMain.isShellMode()) { + queryParams.putIfNotEmpty("study", sessionManager.getSession().getCurrentStudy()); + } + CustomAnalysisClinicalCommandExecutor customAnalysisClinicalCommandExecutor = new CustomAnalysisClinicalCommandExecutor(queryParams, token, clientConfiguration, getSessionManager(), appHome, getLogger()); + return customAnalysisClinicalCommandExecutor.load(); + } + private RestResponse aggregationStatsRga() throws Exception { logger.debug("Executing aggregationStatsRga in Analysis - Clinical command line"); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java index 48261ca8a3c..2e026d35b83 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java @@ -10,6 +10,8 @@ import java.util.Map; import java.util.List; +import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandOptions; + import static org.opencb.opencga.app.cli.GeneralCliOptions.*; @@ -28,10 +30,8 @@ * PATH: /{apiVersion}/analysis/clinical */ @Parameters(commandNames = {"clinical"}, commandDescription = "Analysis - Clinical commands") -public class AnalysisClinicalCommandOptions { +public class AnalysisClinicalCommandOptions extends CustomAnalysisClinicalCommandOptions { - public JCommander jCommander; - public CommonCommandOptions commonCommandOptions; public UpdateAclCommandOptions updateAclCommandOptions; public UpdateClinicalConfigurationCommandOptions updateClinicalConfigurationCommandOptions; @@ -45,6 +45,7 @@ public class AnalysisClinicalCommandOptions { public RunInterpreterTeamCommandOptions runInterpreterTeamCommandOptions; public RunInterpreterTieringCommandOptions runInterpreterTieringCommandOptions; public RunInterpreterZettaCommandOptions runInterpreterZettaCommandOptions; + public LoadCommandOptions loadCommandOptions; public AggregationStatsRgaCommandOptions aggregationStatsRgaCommandOptions; public QueryRgaGeneCommandOptions queryRgaGeneCommandOptions; public SummaryRgaGeneCommandOptions summaryRgaGeneCommandOptions; @@ -68,8 +69,7 @@ public class AnalysisClinicalCommandOptions { public AnalysisClinicalCommandOptions(CommonCommandOptions commonCommandOptions, JCommander jCommander) { - this.jCommander = jCommander; - this.commonCommandOptions = commonCommandOptions; + super(commonCommandOptions,jCommander); this.updateAclCommandOptions = new UpdateAclCommandOptions(); this.updateClinicalConfigurationCommandOptions = new UpdateClinicalConfigurationCommandOptions(); this.createCommandOptions = new CreateCommandOptions(); @@ -82,6 +82,7 @@ public AnalysisClinicalCommandOptions(CommonCommandOptions commonCommandOptions, this.runInterpreterTeamCommandOptions = new RunInterpreterTeamCommandOptions(); this.runInterpreterTieringCommandOptions = new RunInterpreterTieringCommandOptions(); this.runInterpreterZettaCommandOptions = new RunInterpreterZettaCommandOptions(); + this.loadCommandOptions = new LoadCommandOptions(); this.aggregationStatsRgaCommandOptions = new AggregationStatsRgaCommandOptions(); this.queryRgaGeneCommandOptions = new QueryRgaGeneCommandOptions(); this.summaryRgaGeneCommandOptions = new SummaryRgaGeneCommandOptions(); 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..6d6ba53bb95 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 @@ -17,15 +17,20 @@ package org.opencb.opencga.catalog.managers; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectReader; 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; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.core.result.Error; +import org.opencb.commons.utils.FileUtils; import org.opencb.commons.utils.ListUtils; import org.opencb.opencga.catalog.auth.authorization.AuthorizationManager; import org.opencb.opencga.catalog.db.DBAdaptorFactory; @@ -38,6 +43,7 @@ import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.catalog.utils.UuidUtils; 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; @@ -51,9 +57,11 @@ import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.file.FileReferenceParam; import org.opencb.opencga.core.models.individual.Individual; +import org.opencb.opencga.core.models.individual.IndividualUpdateParams; import org.opencb.opencga.core.models.panel.Panel; import org.opencb.opencga.core.models.panel.PanelReferenceParam; import org.opencb.opencga.core.models.sample.Sample; +import org.opencb.opencga.core.models.sample.SampleReferenceParam; import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.study.StudyPermissions; import org.opencb.opencga.core.models.study.configuration.ClinicalConsent; @@ -64,12 +72,17 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Path; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.opencb.opencga.catalog.auth.authorization.CatalogAuthorizationManager.checkPermissions; +import static org.opencb.opencga.catalog.utils.ParamUtils.SaveInterpretationAs.PRIMARY; +import static org.opencb.opencga.catalog.utils.ParamUtils.SaveInterpretationAs.SECONDARY; import static org.opencb.opencga.core.common.JacksonUtils.getUpdateObjectMapper; /** @@ -582,6 +595,109 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis } } + public int load(String studyStr, Path filePath, String token) throws CatalogException, IOException { + String userId = catalogManager.getUserManager().getUserId(token); + Study study = catalogManager.getStudyManager().resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET); + + // Check gzip format + int counter = 0; + ObjectReader objectReader = JacksonUtils.getDefaultObjectMapper().readerFor(ClinicalAnalysis.class); + + try (BufferedReader br = FileUtils.newBufferedReader(filePath)) { + while (true) { + String line = br.readLine(); + if (line == null) { + break; + } + ClinicalAnalysis clinicalAnalysis = null; + try { + clinicalAnalysis = objectReader.readValue(line); + logger.info("Loading clinical analysis {}...", clinicalAnalysis.getId()); + load(clinicalAnalysis, studyStr, token); + logger.info("... clinical analysis {} loaded !", clinicalAnalysis.getId()); + counter++; + } catch (Exception e) { + logger.error("Error loading clinical analysis" + (clinicalAnalysis != null ? (": " + clinicalAnalysis.getId()) : "") + + ": " + e.getMessage()); + } + } + } + return counter; + } + + private void load(ClinicalAnalysis clinicalAnalysis, String study, String token) throws CatalogException { + Map> individualSamples = new HashMap<>(); + + // Create samples + for (Individual member : clinicalAnalysis.getFamily().getMembers()) { + if (CollectionUtils.isNotEmpty(member.getSamples())) { + individualSamples.put(member.getId(), member.getSamples().stream().map(Sample::getId).collect(Collectors.toList())); + for (Sample sample : member.getSamples()) { + try { + catalogManager.getSampleManager().create(study, sample, QueryOptions.empty(), token); + } catch (CatalogException e) { + if (!e.getMessage().contains("already exists")) { + throw e; + } + } + } + member.setSamples(null); + } + } + + // Create family with individuals + try { + catalogManager.getFamilyManager().create(study, clinicalAnalysis.getFamily(), QueryOptions.empty(), token); + } catch (CatalogException e) { + if (!e.getMessage().contains("already exists")) { + throw e; + } + } + + // Associate individuals and samples + for (Map.Entry> entry : individualSamples.entrySet()) { + catalogManager.getIndividualManager().update(study, entry.getKey(), new IndividualUpdateParams().setSamples( + entry.getValue().stream().map(s -> new SampleReferenceParam().setId(s)).collect(Collectors.toList())), + QueryOptions.empty(), token); + } + + for (Panel panel : clinicalAnalysis.getPanels()) { + try { + catalogManager.getPanelManager().create(study, panel, QueryOptions.empty(), token); + } catch (CatalogException e) { + if (!e.getMessage().contains("already exists")) { + throw e; + } + } + } + + List interpretationList = clinicalAnalysis.getSecondaryInterpretations(); + clinicalAnalysis.setSecondaryInterpretations(null); + + Interpretation interpretation = clinicalAnalysis.getInterpretation(); + clinicalAnalysis.setInterpretation(null); + + // Create Clinical Analysis + catalogManager.getClinicalAnalysisManager().create(study, clinicalAnalysis, QueryOptions.empty(), token); + + if (interpretation == null) { + interpretation = interpretationList.get(0); + interpretationList = interpretationList.subList(1, interpretationList.size()); + } + + // Add interpretations + interpretation.setId(null); + catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), interpretation, PRIMARY, QueryOptions.empty(), + token); + + for (int i = 0, interpretationListSize = interpretationList.size(); i < interpretationListSize; i++) { + Interpretation tmpInterpretation = interpretationList.get(i); + tmpInterpretation.setId(null); + catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), tmpInterpretation, SECONDARY, + QueryOptions.empty(), token); + } + } + private void validateStatusParameter(ClinicalAnalysis clinicalAnalysis, ClinicalAnalysisStudyConfiguration clinicalConfiguration) throws CatalogException { // Status diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java new file mode 100644 index 00000000000..5255570707f --- /dev/null +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java @@ -0,0 +1,58 @@ +package org.opencb.opencga.catalog.managers; + +import org.apache.commons.io.FileUtils; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.core.config.Configuration; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +public class ClinicalAnalysisTest { + + private String sessionIdUser; + private CatalogManager catalogManager; + + private File testFile; + + private String study; + private Path clinicalAnalysesDir; + + @Before + public void init() throws CatalogException, IOException { + // line #1: username + // line #2: password + // line #3: study + // line #4: path to the OpenCGA configuration file + // line #5: path to the folder where the clinical analyses to load are located at + testFile = Paths.get("/tmp/load-clinical-analyses.conf").toFile(); + Assume.assumeTrue(testFile.exists()); + + List lines = FileUtils.readLines(testFile); + String user = lines.get(0); + String pass = lines.get(1); + study = lines.get(2); + String confPath = lines.get(3); + clinicalAnalysesDir = Paths.get(lines.get(4)); + + Configuration configuration = Configuration.load(new FileInputStream(confPath)); + catalogManager = new CatalogManager(configuration); + sessionIdUser = catalogManager.getUserManager().login(user, pass).getToken(); + } + + @Test + public void loadClinicalAnalysesTest() throws CatalogException, IOException { + Assume.assumeTrue(clinicalAnalysesDir.toFile().exists()); + for (File file : clinicalAnalysesDir.toFile().listFiles()) { + System.out.println("Loading clinical analyses file: " + file.getAbsolutePath() + " ...."); + int numLoaded = catalogManager.getClinicalAnalysisManager().load(study, file.toPath(), sessionIdUser); + System.out.println("\t\t.... " + numLoaded + " clinical analyses loaded from file " + file.getAbsolutePath()); + } + } +} \ No newline at end of file diff --git a/opencga-catalog/src/test/resources/load-clinical-analyses.conf b/opencga-catalog/src/test/resources/load-clinical-analyses.conf new file mode 100644 index 00000000000..875947aae79 --- /dev/null +++ b/opencga-catalog/src/test/resources/load-clinical-analyses.conf @@ -0,0 +1,5 @@ +titi +Titi123456; +titi@p1:s1 +/opt/opencga/conf/configuration.yml +/home/jtarraga/reanalysis/test3 \ No newline at end of file diff --git a/opencga-client/src/main/R/R/Admin-methods.R b/opencga-client/src/main/R/R/Admin-methods.R index 636adc07e4f..72a454677fc 100644 --- a/opencga-client/src/main/R/R/Admin-methods.R +++ b/opencga-client/src/main/R/R/Admin-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Alignment-methods.R b/opencga-client/src/main/R/R/Alignment-methods.R index 6c6283f4411..c2b022cd654 100644 --- a/opencga-client/src/main/R/R/Alignment-methods.R +++ b/opencga-client/src/main/R/R/Alignment-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/AllGenerics.R b/opencga-client/src/main/R/R/AllGenerics.R index 824bc739a99..fa31e4b0e54 100644 --- a/opencga-client/src/main/R/R/AllGenerics.R +++ b/opencga-client/src/main/R/R/AllGenerics.R @@ -1,6 +1,6 @@ # ############################################################################## ## UserClient -setGeneric("userClient", function(OpencgaR, user, users, filterId, endpointName, params=NULL, ...) +setGeneric("userClient", function(OpencgaR, filterId, user, users, endpointName, params=NULL, ...) standardGeneric("userClient")) # ############################################################################## @@ -10,12 +10,12 @@ setGeneric("projectClient", function(OpencgaR, project, projects, endpointName, # ############################################################################## ## StudyClient -setGeneric("studyClient", function(OpencgaR, members, study, variableSet, templateId, studies, group, endpointName, params=NULL, ...) +setGeneric("studyClient", function(OpencgaR, group, members, study, studies, templateId, variableSet, endpointName, params=NULL, ...) standardGeneric("studyClient")) # ############################################################################## ## FileClient -setGeneric("fileClient", function(OpencgaR, members, file, files, folder, annotationSet, endpointName, params=NULL, ...) +setGeneric("fileClient", function(OpencgaR, members, annotationSet, folder, file, files, endpointName, params=NULL, ...) standardGeneric("fileClient")) # ############################################################################## @@ -25,22 +25,22 @@ setGeneric("jobClient", function(OpencgaR, members, jobs, job, endpointName, par # ############################################################################## ## SampleClient -setGeneric("sampleClient", function(OpencgaR, sample, annotationSet, members, samples, endpointName, params=NULL, ...) +setGeneric("sampleClient", function(OpencgaR, annotationSet, samples, members, sample, endpointName, params=NULL, ...) standardGeneric("sampleClient")) # ############################################################################## ## IndividualClient -setGeneric("individualClient", function(OpencgaR, individual, annotationSet, members, individuals, endpointName, params=NULL, ...) +setGeneric("individualClient", function(OpencgaR, individual, members, individuals, annotationSet, endpointName, params=NULL, ...) standardGeneric("individualClient")) # ############################################################################## ## FamilyClient -setGeneric("familyClient", function(OpencgaR, annotationSet, members, family, families, endpointName, params=NULL, ...) +setGeneric("familyClient", function(OpencgaR, family, families, members, annotationSet, endpointName, params=NULL, ...) standardGeneric("familyClient")) # ############################################################################## ## CohortClient -setGeneric("cohortClient", function(OpencgaR, annotationSet, members, cohorts, cohort, endpointName, params=NULL, ...) +setGeneric("cohortClient", function(OpencgaR, cohort, members, cohorts, annotationSet, endpointName, params=NULL, ...) standardGeneric("cohortClient")) # ############################################################################## @@ -60,7 +60,7 @@ setGeneric("variantClient", function(OpencgaR, endpointName, params=NULL, ...) # ############################################################################## ## ClinicalClient -setGeneric("clinicalClient", function(OpencgaR, members, clinicalAnalyses, interpretations, clinicalAnalysis, interpretation, endpointName, params=NULL, ...) +setGeneric("clinicalClient", function(OpencgaR, members, study, clinicalAnalyses, interpretation, interpretations, clinicalAnalysis, endpointName, params=NULL, ...) standardGeneric("clinicalClient")) # ############################################################################## diff --git a/opencga-client/src/main/R/R/Clinical-methods.R b/opencga-client/src/main/R/R/Clinical-methods.R index d1ab9660873..749e08065c2 100644 --- a/opencga-client/src/main/R/R/Clinical-methods.R +++ b/opencga-client/src/main/R/R/Clinical-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -32,6 +32,7 @@ #' | runInterpreterTeam | /{apiVersion}/analysis/clinical/interpreter/team/run | study, jobId, jobDescription, jobDependsOn, jobTags, body[*] | #' | runInterpreterTiering | /{apiVersion}/analysis/clinical/interpreter/tiering/run | study, jobId, jobDescription, jobDependsOn, jobTags, body[*] | #' | runInterpreterZetta | /{apiVersion}/analysis/clinical/interpreter/zetta/run | study, jobId, jobDescription, jobDependsOn, jobTags, body[*] | +#' | load | /{apiVersion}/analysis/clinical/load | file, study[*], study, jobId, jobDescription, jobDependsOn, jobTags | #' | aggregationStatsRga | /{apiVersion}/analysis/clinical/rga/aggregationStats | limit, skip, sampleId, individualId, sex, phenotypes, disorders, numParents, geneId, geneName, chromosome, start, end, transcriptId, variants, dbSnps, knockoutType, filter, type, clinicalSignificance, populationFrequency, consequenceType, study, field[*] | #' | queryRgaGene | /{apiVersion}/analysis/clinical/rga/gene/query | include, exclude, limit, skip, count, includeIndividual, skipIndividual, limitIndividual, sampleId, individualId, sex, phenotypes, disorders, numParents, geneId, geneName, chromosome, start, end, transcriptId, variants, dbSnps, knockoutType, filter, type, clinicalSignificance, populationFrequency, consequenceType, study | #' | summaryRgaGene | /{apiVersion}/analysis/clinical/rga/gene/summary | limit, skip, count, sampleId, individualId, sex, phenotypes, disorders, numParents, geneId, geneName, chromosome, start, end, transcriptId, variants, dbSnps, knockoutType, filter, type, clinicalSignificance, populationFrequency, consequenceType, study | @@ -58,7 +59,7 @@ #' [*]: Required parameter #' @export -setMethod("clinicalClient", "OpencgaR", function(OpencgaR, members, clinicalAnalyses, interpretations, clinicalAnalysis, interpretation, endpointName, params=NULL, ...) { +setMethod("clinicalClient", "OpencgaR", function(OpencgaR, members, study, clinicalAnalyses, interpretation, interpretations, clinicalAnalysis, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/analysis/clinical/acl/{members}/update: @@ -241,6 +242,17 @@ setMethod("clinicalClient", "OpencgaR", function(OpencgaR, members, clinicalAnal subcategory="clinical/interpreter/zetta", subcategoryId=NULL, action="run", params=params, httpMethod="POST", as.queryParam=NULL, ...), + #' @section Endpoint /{apiVersion}/analysis/clinical/load: + #' Gzipped file containing clinical analyses. + #' @param file Gzipped file. + #' @param study Study [[user@]project:]study where study and project can be either the ID or UUID. + #' @param jobId Job ID. It must be a unique string within the study. An ID will be autogenerated automatically if not provided. + #' @param jobDescription Job description. + #' @param jobDependsOn Comma separated list of existing job IDs the job will depend on. + #' @param jobTags Job tags. + load=fetchOpenCGA(object=OpencgaR, category="analysis", categoryId=NULL, subcategory="clinical", + subcategoryId=NULL, action="load", params=params, httpMethod="POST", as.queryParam=c("study"), ...), + #' @section Endpoint /{apiVersion}/analysis/clinical/rga/aggregationStats: #' RGA aggregation stats. #' @param limit Number of results to be returned. diff --git a/opencga-client/src/main/R/R/Cohort-methods.R b/opencga-client/src/main/R/R/Cohort-methods.R index 06867054e07..349157b1bb2 100644 --- a/opencga-client/src/main/R/R/Cohort-methods.R +++ b/opencga-client/src/main/R/R/Cohort-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("cohortClient", "OpencgaR", function(OpencgaR, annotationSet, members, cohorts, cohort, endpointName, params=NULL, ...) { +setMethod("cohortClient", "OpencgaR", function(OpencgaR, cohort, members, cohorts, annotationSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/cohorts/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Family-methods.R b/opencga-client/src/main/R/R/Family-methods.R index 1a3949a90f4..fbe18b63578 100644 --- a/opencga-client/src/main/R/R/Family-methods.R +++ b/opencga-client/src/main/R/R/Family-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -38,7 +38,7 @@ #' [*]: Required parameter #' @export -setMethod("familyClient", "OpencgaR", function(OpencgaR, annotationSet, members, family, families, endpointName, params=NULL, ...) { +setMethod("familyClient", "OpencgaR", function(OpencgaR, family, families, members, annotationSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/families/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/File-methods.R b/opencga-client/src/main/R/R/File-methods.R index 9d6921847e7..0653378cdb1 100644 --- a/opencga-client/src/main/R/R/File-methods.R +++ b/opencga-client/src/main/R/R/File-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -54,7 +54,7 @@ #' [*]: Required parameter #' @export -setMethod("fileClient", "OpencgaR", function(OpencgaR, members, file, files, folder, annotationSet, endpointName, params=NULL, ...) { +setMethod("fileClient", "OpencgaR", function(OpencgaR, members, annotationSet, folder, file, files, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/files/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/GA4GH-methods.R b/opencga-client/src/main/R/R/GA4GH-methods.R index 800bb33a3f1..0923b1d47a8 100644 --- a/opencga-client/src/main/R/R/GA4GH-methods.R +++ b/opencga-client/src/main/R/R/GA4GH-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Individual-methods.R b/opencga-client/src/main/R/R/Individual-methods.R index 009d93736b2..bfcdf057737 100644 --- a/opencga-client/src/main/R/R/Individual-methods.R +++ b/opencga-client/src/main/R/R/Individual-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("individualClient", "OpencgaR", function(OpencgaR, individual, annotationSet, members, individuals, endpointName, params=NULL, ...) { +setMethod("individualClient", "OpencgaR", function(OpencgaR, individual, members, individuals, annotationSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/individuals/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Job-methods.R b/opencga-client/src/main/R/R/Job-methods.R index 552b717b4a7..263677f4931 100644 --- a/opencga-client/src/main/R/R/Job-methods.R +++ b/opencga-client/src/main/R/R/Job-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Meta-methods.R b/opencga-client/src/main/R/R/Meta-methods.R index c8994252f11..1335c0c193a 100644 --- a/opencga-client/src/main/R/R/Meta-methods.R +++ b/opencga-client/src/main/R/R/Meta-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Operation-methods.R b/opencga-client/src/main/R/R/Operation-methods.R index 11e5dbc0cb8..cda5cbd3374 100644 --- a/opencga-client/src/main/R/R/Operation-methods.R +++ b/opencga-client/src/main/R/R/Operation-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Panel-methods.R b/opencga-client/src/main/R/R/Panel-methods.R index e45d600549c..dc38ee2adce 100644 --- a/opencga-client/src/main/R/R/Panel-methods.R +++ b/opencga-client/src/main/R/R/Panel-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Project-methods.R b/opencga-client/src/main/R/R/Project-methods.R index 8746b1d66be..fbe4ea3fb18 100644 --- a/opencga-client/src/main/R/R/Project-methods.R +++ b/opencga-client/src/main/R/R/Project-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Sample-methods.R b/opencga-client/src/main/R/R/Sample-methods.R index be51f5035ee..10efb240730 100644 --- a/opencga-client/src/main/R/R/Sample-methods.R +++ b/opencga-client/src/main/R/R/Sample-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("sampleClient", "OpencgaR", function(OpencgaR, sample, annotationSet, members, samples, endpointName, params=NULL, ...) { +setMethod("sampleClient", "OpencgaR", function(OpencgaR, annotationSet, samples, members, sample, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/samples/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Study-methods.R b/opencga-client/src/main/R/R/Study-methods.R index 8fd99f5a952..b7b92f439f0 100644 --- a/opencga-client/src/main/R/R/Study-methods.R +++ b/opencga-client/src/main/R/R/Study-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -46,7 +46,7 @@ #' [*]: Required parameter #' @export -setMethod("studyClient", "OpencgaR", function(OpencgaR, members, study, variableSet, templateId, studies, group, endpointName, params=NULL, ...) { +setMethod("studyClient", "OpencgaR", function(OpencgaR, group, members, study, studies, templateId, variableSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/studies/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/User-methods.R b/opencga-client/src/main/R/R/User-methods.R index 81ed4d9fdfa..4c566fb6168 100644 --- a/opencga-client/src/main/R/R/User-methods.R +++ b/opencga-client/src/main/R/R/User-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -38,7 +38,7 @@ #' [*]: Required parameter #' @export -setMethod("userClient", "OpencgaR", function(OpencgaR, user, users, filterId, endpointName, params=NULL, ...) { +setMethod("userClient", "OpencgaR", function(OpencgaR, filterId, user, users, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/users/login: diff --git a/opencga-client/src/main/R/R/Variant-methods.R b/opencga-client/src/main/R/R/Variant-methods.R index 7f2e555bab8..c95d67afe6c 100644 --- a/opencga-client/src/main/R/R/Variant-methods.R +++ b/opencga-client/src/main/R/R/Variant-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-03-29 +# Autogenerated on: 2023-06-20 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java index aeb96913372..6b892cbbef1 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java @@ -35,7 +35,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java index ad712ffe4fd..7bf9e3e0c54 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java @@ -40,7 +40,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java index b475a553c80..9e5d0fcdaa5 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java @@ -51,7 +51,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -327,6 +327,25 @@ public RestResponse runInterpreterZetta(ZettaInterpretationAnalysisParams d return execute("analysis", null, "clinical/interpreter/zetta", null, "run", params, POST, Job.class); } + /** + * Gzipped file containing clinical analyses. + * @param study Study [[user@]project:]study where study and project can be either the ID or UUID. + * @param params Map containing any of the following optional parameters. + * file: Gzipped file. + * study: Study [[user@]project:]study where study and project can be either the ID or UUID. + * jobId: Job ID. It must be a unique string within the study. An ID will be autogenerated automatically if not provided. + * jobDescription: Job description. + * jobDependsOn: Comma separated list of existing job IDs the job will depend on. + * jobTags: Job tags. + * @return a RestResponse object. + * @throws ClientException ClientException if there is any server error. + */ + public RestResponse load(String study, ObjectMap params) throws ClientException { + params = params != null ? params : new ObjectMap(); + params.putIfNotNull("study", study); + return execute("analysis", null, "clinical", null, "load", params, POST, Job.class); + } + /** * RGA aggregation stats. * @param field List of fields separated by semicolons, e.g.: clinicalSignificances;type. For nested fields use >>, e.g.: diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java index 528801b0085..3a618a31e35 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java @@ -37,7 +37,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java index 82cb1a4fd26..d332ae1f1b0 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java @@ -35,7 +35,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java index 64360a07935..8a7bdd12213 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java index 1b05516fcf0..18466a68c01 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java @@ -43,7 +43,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java index 272685ee71c..220e06d8974 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java @@ -27,7 +27,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java index a0c28821d8f..623f9b95400 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java index 988d7c59b07..c6019a4ebda 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java @@ -37,7 +37,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java index 211ed9c751f..dbb6f1e812d 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java @@ -28,7 +28,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java index 8e85c32bd7e..c7616e94996 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java @@ -32,7 +32,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java index bb0b07d63a0..6d1ae04afa9 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java index 276fc7cb114..3eeb7d111a6 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java @@ -45,7 +45,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java index b2ff3602dcd..60e7958e510 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java index ac879d02d9e..ec198df47a9 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java @@ -62,7 +62,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java index fefa5159ed9..c54342365a6 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java @@ -50,7 +50,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-03-29 +* Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Admin.js b/opencga-client/src/main/javascript/Admin.js index 5f04bd8757e..6b96aa2127a 100644 --- a/opencga-client/src/main/javascript/Admin.js +++ b/opencga-client/src/main/javascript/Admin.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Alignment.js b/opencga-client/src/main/javascript/Alignment.js index d35d62534b1..65b78fedf64 100644 --- a/opencga-client/src/main/javascript/Alignment.js +++ b/opencga-client/src/main/javascript/Alignment.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/ClinicalAnalysis.js b/opencga-client/src/main/javascript/ClinicalAnalysis.js index 8afc4eb3572..2cea25d06c7 100644 --- a/opencga-client/src/main/javascript/ClinicalAnalysis.js +++ b/opencga-client/src/main/javascript/ClinicalAnalysis.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -250,6 +250,21 @@ export default class ClinicalAnalysis extends OpenCGAParentClass { return this._post("analysis", null, "clinical/interpreter/zetta", null, "run", data, params); } + /** Gzipped file containing clinical analyses + * @param {String} study - Study [[user@]project:]study where study and project can be either the ID or UUID. + * @param {Object} [params] - The Object containing the following optional parameters: + * @param {InputStream} [params.file] - Gzipped file. + * @param {String} [params.jobId] - Job ID. It must be a unique string within the study. An ID will be autogenerated automatically if not + * provided. + * @param {String} [params.jobDescription] - Job description. + * @param {String} [params.jobDependsOn] - Comma separated list of existing job IDs the job will depend on. + * @param {String} [params.jobTags] - Job tags. + * @returns {Promise} Promise object in the form of RestResponse instance. + */ + load(study, params) { + return this._post("analysis", null, "clinical", null, "load", {study, ...params}); + } + /** RGA aggregation stats * @param {String} field - List of fields separated by semicolons, e.g.: clinicalSignificances;type. For nested fields use >>, e.g.: * type>>clinicalSignificances;knockoutType. diff --git a/opencga-client/src/main/javascript/Cohort.js b/opencga-client/src/main/javascript/Cohort.js index 8cd087a5b8e..209c4394597 100644 --- a/opencga-client/src/main/javascript/Cohort.js +++ b/opencga-client/src/main/javascript/Cohort.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/DiseasePanel.js b/opencga-client/src/main/javascript/DiseasePanel.js index 7c27ecb6956..d6c8491dbe0 100644 --- a/opencga-client/src/main/javascript/DiseasePanel.js +++ b/opencga-client/src/main/javascript/DiseasePanel.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Family.js b/opencga-client/src/main/javascript/Family.js index 0932611cb55..2043c5e52eb 100644 --- a/opencga-client/src/main/javascript/Family.js +++ b/opencga-client/src/main/javascript/Family.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/File.js b/opencga-client/src/main/javascript/File.js index 770609791d6..5a955364f5c 100644 --- a/opencga-client/src/main/javascript/File.js +++ b/opencga-client/src/main/javascript/File.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/GA4GH.js b/opencga-client/src/main/javascript/GA4GH.js index c4604ea36f0..633b14637d0 100644 --- a/opencga-client/src/main/javascript/GA4GH.js +++ b/opencga-client/src/main/javascript/GA4GH.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Individual.js b/opencga-client/src/main/javascript/Individual.js index b695072f0e7..4aba6b863e1 100644 --- a/opencga-client/src/main/javascript/Individual.js +++ b/opencga-client/src/main/javascript/Individual.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Job.js b/opencga-client/src/main/javascript/Job.js index f4133eb756e..ad1b8559ca1 100644 --- a/opencga-client/src/main/javascript/Job.js +++ b/opencga-client/src/main/javascript/Job.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Meta.js b/opencga-client/src/main/javascript/Meta.js index b0b1bc096fb..c31b0d1e86e 100644 --- a/opencga-client/src/main/javascript/Meta.js +++ b/opencga-client/src/main/javascript/Meta.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Project.js b/opencga-client/src/main/javascript/Project.js index d01e0e3efba..660341b3b21 100644 --- a/opencga-client/src/main/javascript/Project.js +++ b/opencga-client/src/main/javascript/Project.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Sample.js b/opencga-client/src/main/javascript/Sample.js index 3ac93f3d1ad..b33326ca3a9 100644 --- a/opencga-client/src/main/javascript/Sample.js +++ b/opencga-client/src/main/javascript/Sample.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Study.js b/opencga-client/src/main/javascript/Study.js index 113daf2634d..c178b0f3eef 100644 --- a/opencga-client/src/main/javascript/Study.js +++ b/opencga-client/src/main/javascript/Study.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/User.js b/opencga-client/src/main/javascript/User.js index 0162c0d9b6d..006b76561d9 100644 --- a/opencga-client/src/main/javascript/User.js +++ b/opencga-client/src/main/javascript/User.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Variant.js b/opencga-client/src/main/javascript/Variant.js index 5d08654b5ca..29091899c09 100644 --- a/opencga-client/src/main/javascript/Variant.js +++ b/opencga-client/src/main/javascript/Variant.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/VariantOperation.js b/opencga-client/src/main/javascript/VariantOperation.js index c4327775816..4f31ffcb62d 100644 --- a/opencga-client/src/main/javascript/VariantOperation.js +++ b/opencga-client/src/main/javascript/VariantOperation.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-03-29 + * Autogenerated on: 2023-06-20 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py index 59c4f94a84c..0410c0629cc 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py index 0e383d3df19..f066b1c5be9 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py index e07a1529453..4b96be4b489 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -301,6 +301,25 @@ def run_interpreter_zetta(self, data=None, **options): return self._post(category='analysis', resource='run', subcategory='clinical/interpreter/zetta', data=data, **options) + def load(self, study, **options): + """ + Gzipped file containing clinical analyses. + PATH: /{apiVersion}/analysis/clinical/load + + :param inputstream file: Gzipped file. + :param str study: Study [[user@]project:]study where study and project + can be either the ID or UUID. + :param str job_id: Job ID. It must be a unique string within the + study. An ID will be autogenerated automatically if not provided. + :param str job_description: Job description. + :param str job_depends_on: Comma separated list of existing job IDs + the job will depend on. + :param str job_tags: Job tags. + """ + + options['study'] = study + return self._post(category='analysis', resource='load', subcategory='clinical', **options) + def aggregation_stats_rga(self, field, **options): """ RGA aggregation stats. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py index c91a5802fc8..96df4d09023 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py index 76994790b1e..576f2362904 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py index 39d1eee2ed7..b830f39af55 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py index 310bdb30e88..99d69b69f50 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py index 717cc8a3845..cdd7e7bc0b0 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py index af08c6a74a1..d544c581fc3 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py index 42b2faefe6f..89e3b11ce3c 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py index 8de5a377a23..f7664cd9614 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py index 2f7b4d85f89..673a85c414f 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py index 2eed2d27ca1..60cdc650de5 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py index 45140e09239..818808d501f 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py index 6f741db52db..39ff5057785 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py index 0ef98358760..1224ed2c5f8 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py index 3f422306930..3354962244d 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-03-29 + Autogenerated on: 2023-06-20 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java new file mode 100644 index 00000000000..229c263cce0 --- /dev/null +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java @@ -0,0 +1,37 @@ +package org.opencb.opencga.core.models.clinical; + +import org.opencb.commons.annotations.DataField; +import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.tools.ToolParams; + +import java.nio.file.Path; + +public class ClinicalAnalysisLoadParams extends ToolParams { + + @DataField(id = ParamConstants.FILE_PATH_PARAM, description = ParamConstants.FILE_PATH_DESCRIPTION) + private Path path; + + public ClinicalAnalysisLoadParams() { + } + + public ClinicalAnalysisLoadParams(Path path) { + this.path = path; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ClinicalAnalysisLoadParams{"); + sb.append("path=").append(path); + sb.append('}'); + return sb.toString(); + } + + public Path getPath() { + return path; + } + + public ClinicalAnalysisLoadParams setPath(Path path) { + this.path = path; + return this; + } +} 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..ef165ba37cc 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 @@ -16,10 +16,15 @@ package org.opencb.opencga.server.rest.analysis; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; +import org.glassfish.jersey.media.multipart.FormDataContentDisposition; +import org.glassfish.jersey.media.multipart.FormDataParam; import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; import org.opencb.commons.datastore.core.*; +import org.opencb.opencga.analysis.clinical.ClinicalAnalysisLoadTask; import org.opencb.opencga.analysis.clinical.ClinicalInterpretationManager; import org.opencb.opencga.analysis.clinical.exomiser.ExomiserInterpretationAnalysis; import org.opencb.opencga.analysis.clinical.rga.AuxiliarRgaAnalysis; @@ -34,8 +39,13 @@ import org.opencb.opencga.analysis.variant.manager.VariantStorageManager; import org.opencb.opencga.catalog.db.api.ClinicalAnalysisDBAdaptor; import org.opencb.opencga.catalog.db.api.InterpretationDBAdaptor; +import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.exceptions.CatalogIOException; +import org.opencb.opencga.catalog.io.IOManager; import org.opencb.opencga.catalog.managers.ClinicalAnalysisManager; +import org.opencb.opencga.catalog.managers.FileManager; import org.opencb.opencga.catalog.managers.InterpretationManager; +import org.opencb.opencga.catalog.managers.StudyManager; import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; @@ -44,6 +54,7 @@ import org.opencb.opencga.core.models.analysis.knockout.*; import org.opencb.opencga.core.models.clinical.*; import org.opencb.opencga.core.models.job.Job; +import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.study.configuration.ClinicalAnalysisStudyConfiguration; import org.opencb.opencga.core.tools.annotations.*; @@ -51,15 +62,17 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.*; import javax.ws.rs.core.*; +import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.opencb.opencga.analysis.variant.manager.VariantCatalogQueryUtils.SAVED_FILTER_DESCR; -import static org.opencb.opencga.core.api.ParamConstants.INCLUDE_INTERPRETATION; -import static org.opencb.opencga.core.api.ParamConstants.JOB_DEPENDS_ON; +import static org.opencb.opencga.core.api.ParamConstants.*; import static org.opencb.opencga.server.rest.analysis.VariantWebService.getVariantQuery; import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.*; @@ -148,7 +161,7 @@ public Response create( @QueryParam(ParamConstants.CLINICAL_ANALYSIS_SKIP_CREATE_DEFAULT_INTERPRETATION_PARAM) boolean skipCreateInterpretation, @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) - ClinicalAnalysisCreateParams params) { + ClinicalAnalysisCreateParams params) { try { return createOkResponse(clinicalManager.create(studyStr, params.toClinicalAnalysis(), skipCreateInterpretation, queryOptions, token)); @@ -157,6 +170,36 @@ public Response create( } } + @POST + @Path("/load") + @Consumes(MediaType.MULTIPART_FORM_DATA) + @ApiOperation(httpMethod = "POST", value = "Gzipped file containing clinical analyses", response = Job.class) + public Response load( + @ApiParam(value = "Gzipped file") @FormDataParam("file") InputStream fileInputStream, + @FormDataParam("file") FormDataContentDisposition fileMetaData, + @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @PathParam(ParamConstants.STUDY_PARAM) String studyStr, + @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @QueryParam(ParamConstants.STUDY_PARAM) String study, + @ApiParam(value = ParamConstants.JOB_ID_CREATION_DESCRIPTION) @QueryParam(ParamConstants.JOB_ID) String jobId, + @ApiParam(value = ParamConstants.JOB_DESCRIPTION_DESCRIPTION) @QueryParam(ParamConstants.JOB_DESCRIPTION) String jobDescription, + @ApiParam(value = ParamConstants.JOB_DEPENDS_ON_DESCRIPTION) @QueryParam(JOB_DEPENDS_ON) String dependsOn, + @ApiParam(value = ParamConstants.JOB_TAGS_DESCRIPTION) @QueryParam(ParamConstants.JOB_TAGS) String jobTags) { + try { + // Prepare input file + java.nio.file.Path scratchDir = Paths.get(catalogManager.getConfiguration().getAnalysis().getScratchDir()); + File inputFile = scratchDir.resolve(ClinicalAnalysisLoadTask.ID + "_" + RandomStringUtils.randomAlphanumeric(10)).toFile(); + logger.info("Uploaded clinical analyses file at {}", inputFile.getAbsolutePath()); + IOManager ioManager = catalogManager.getIoManagerFactory().getDefault(); + ioManager.copy(fileInputStream, inputFile.toURI()); + + // Execute load as a job + ClinicalAnalysisLoadParams params = new ClinicalAnalysisLoadParams(); + params.setPath(inputFile.toPath()); + return submitJob(ClinicalAnalysisLoadTask.ID, studyStr, params, jobId, jobDescription, dependsOn, jobTags); + } catch (Exception e) { + return createErrorResponse("Load clinical analyses from file", e.getMessage()); + } + } + @POST @Path("/update") @Consumes(MediaType.APPLICATION_JSON) @@ -182,7 +225,7 @@ public Response update( @ApiParam(value = "Text attributes (Format: sex=male,age>20 ...)") @QueryParam("attributes") String attributes, @ApiParam(name = "body", value = "JSON containing clinical analysis information", required = true) - ClinicalAnalysisUpdateParams params) { + ClinicalAnalysisUpdateParams params) { try { query.remove(ParamConstants.STUDY_PARAM); return createOkResponse(clinicalManager.update(studyStr, query, params, true, queryOptions, token)); @@ -443,7 +486,7 @@ public Response create( @QueryParam("setAs") ParamUtils.SaveInterpretationAs setAs, @ApiParam(value = ParamConstants.INCLUDE_RESULT_DESCRIPTION, defaultValue = "false") @QueryParam(ParamConstants.INCLUDE_RESULT_PARAM) boolean includeResult, @ApiParam(name = "body", value = "JSON containing clinical interpretation information", required = true) - InterpretationCreateParams params) { + InterpretationCreateParams params) { try { if (setAs == null) { setAs = ParamUtils.SaveInterpretationAs.SECONDARY; @@ -485,7 +528,7 @@ public Response updateInterpretation( @ApiParam(value = "Interpretation ID") @PathParam("interpretation") String interpretationId, @ApiParam(value = ParamConstants.INCLUDE_RESULT_DESCRIPTION, defaultValue = "false") @QueryParam(ParamConstants.INCLUDE_RESULT_PARAM) boolean includeResult, @ApiParam(name = "body", value = "JSON containing clinical interpretation information", required = true) - InterpretationUpdateParams params) { + InterpretationUpdateParams params) { try { if (primaryFindingsAction == null) { primaryFindingsAction = ParamUtils.UpdateAction.ADD; diff --git a/opencga-server/src/main/resources/cli-config.yaml b/opencga-server/src/main/resources/cli-config.yaml index b3a3c302120..afa6eba72fc 100644 --- a/opencga-server/src/main/resources/cli-config.yaml +++ b/opencga-server/src/main/resources/cli-config.yaml @@ -163,7 +163,12 @@ apiConfig: ignore: False key: ClinicalAnalysis analysis: True + executorExtended: True + optionExtended: True commands: + - name: load + executorExtended: True + optionExtended: True - name: update subcommands: - name: commentsAction From e91e58f3ef0955adb9742cd2e39e36fedc0f0830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Wed, 21 Jun 2023 15:50:32 +0200 Subject: [PATCH 04/18] client: upload file, #TASK-4610, #TASK-4158 --- .../opencb/opencga/client/rest/AbstractParentClient.java | 6 +++++- .../opencga/server/rest/analysis/ClinicalWebService.java | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java index 58d37b4caf6..68cf835335f 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java @@ -315,7 +315,11 @@ private RestResponse callRest(WebTarget path, ObjectMap params, Class batchRestResponse = new RestResponse<>(); break; default: - batchRestResponse = callRest(path, params, clazz, method); + if (action.equals("load") && path.toString().contains("analysis/clinical")) { + batchRestResponse = callUploadRest(path, params, clazz); + } else { + batchRestResponse = callRest(path, params, clazz, method); + } break; } return batchRestResponse; 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 ef165ba37cc..8e00434ab7b 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 @@ -185,8 +185,10 @@ public Response load( @ApiParam(value = ParamConstants.JOB_TAGS_DESCRIPTION) @QueryParam(ParamConstants.JOB_TAGS) String jobTags) { try { // Prepare input file - java.nio.file.Path scratchDir = Paths.get(catalogManager.getConfiguration().getAnalysis().getScratchDir()); - File inputFile = scratchDir.resolve(ClinicalAnalysisLoadTask.ID + "_" + RandomStringUtils.randomAlphanumeric(10)).toFile(); + java.nio.file.Path scratchDir = Paths.get(catalogManager.getConfiguration().getAnalysis().getScratchDir()) + .resolve(ClinicalAnalysisLoadTask.ID + "_" + RandomStringUtils.randomAlphanumeric(10)); + scratchDir.toFile().mkdirs(); + File inputFile = scratchDir.resolve(fileMetaData.getFileName()).toFile(); logger.info("Uploaded clinical analyses file at {}", inputFile.getAbsolutePath()); IOManager ioManager = catalogManager.getIoManagerFactory().getDefault(); ioManager.copy(fileInputStream, inputFile.toURI()); From fc73d9b03d6224288000900384f1f4ea8849ca35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Fri, 15 Sep 2023 11:09:11 +0200 Subject: [PATCH 05/18] analysis: add clinical analysis manager test for loading clinical analysis, and update clients, #TASK-4610, #TASK-TASK-4158 --- .../clinical/ClinicalAnalysisLoadTask.java | 21 ++++-- ...CustomAnalysisClinicalCommandExecutor.java | 59 ---------------- .../CustomAnalysisClinicalCommandOptions.java | 53 --------------- .../AnalysisClinicalCommandExecutor.java | 63 +++++++----------- .../AnalysisClinicalCommandOptions.java | 41 ++++++++++-- .../managers/ClinicalAnalysisManager.java | 1 - .../managers/ClinicalAnalysisManagerTest.java | 41 ++++++++++-- .../src/test/resources/biofiles/ca2.json.gz | Bin 0 -> 86718 bytes opencga-client/src/main/R/R/AllGenerics.R | 18 ++--- .../src/main/R/R/Clinical-methods.R | 10 +-- opencga-client/src/main/R/R/Cohort-methods.R | 2 +- opencga-client/src/main/R/R/Family-methods.R | 2 +- opencga-client/src/main/R/R/File-methods.R | 2 +- .../src/main/R/R/Individual-methods.R | 2 +- opencga-client/src/main/R/R/Job-methods.R | 2 +- opencga-client/src/main/R/R/Sample-methods.R | 2 +- opencga-client/src/main/R/R/Study-methods.R | 2 +- opencga-client/src/main/R/R/User-methods.R | 2 +- .../client/rest/AbstractParentClient.java | 6 +- .../rest/clients/ClinicalAnalysisClient.java | 10 +-- .../src/main/javascript/ClinicalAnalysis.js | 10 +-- .../rest_clients/clinical_analysis_client.py | 10 +-- .../clinical/ClinicalAnalysisLoadParams.java | 19 +++--- .../rest/analysis/ClinicalWebService.java | 38 ++--------- .../src/main/resources/cli-config.yaml | 5 -- 25 files changed, 165 insertions(+), 256 deletions(-) delete mode 100644 opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java delete mode 100644 opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java create mode 100644 opencga-catalog/src/test/resources/biofiles/ca2.json.gz diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java index e460463bd48..f4f72e67cab 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java @@ -1,20 +1,26 @@ package org.opencb.opencga.analysis.clinical; import org.apache.commons.lang3.StringUtils; +import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.analysis.tools.OpenCgaToolScopeStudy; +import org.opencb.opencga.catalog.managers.FileManager; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisLoadParams; import org.opencb.opencga.core.models.common.Enums; +import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.panel.PanelImportParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; +import java.nio.file.Path; +import java.nio.file.Paths; + @Tool(id = ClinicalAnalysisLoadTask.ID, resource = Enums.Resource.DISEASE_PANEL, description = ClinicalAnalysisLoadTask.DESCRIPTION) public class ClinicalAnalysisLoadTask extends OpenCgaToolScopeStudy { public final static String ID = "load"; public static final String DESCRIPTION = "Load clinical analyses from a file"; - private String studyFqn; + private Path filePath; @ToolParams protected ClinicalAnalysisLoadParams params = new ClinicalAnalysisLoadParams(); @@ -22,20 +28,21 @@ public class ClinicalAnalysisLoadTask extends OpenCgaToolScopeStudy { @Override protected void check() throws Exception { super.check(); - studyFqn = getStudy(); - if (StringUtils.isEmpty(params.getPath().toString())) { + String fileStr = params.getFile(); + if (StringUtils.isEmpty(fileStr)) { throw new ToolException("Missing input file when loading clinical analyses."); } - if (!params.getPath().toFile().exists()) { - throw new ToolException("Input file '" + params.getPath() + "' does not exist."); + File file = catalogManager.getFileManager().get(getStudy(), fileStr, FileManager.INCLUDE_FILE_URI_PATH, token).first(); + filePath = Paths.get(file.getUri()); + if (!filePath.toFile().exists()) { + throw new ToolException("Input file '" + filePath + "' does not exist."); } } @Override protected void run() throws Exception { - step(() -> catalogManager.getClinicalAnalysisManager().load(studyFqn, params.getPath(), token)); + step(() -> catalogManager.getClinicalAnalysisManager().load(getStudy(), filePath, token)); } - } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java deleted file mode 100644 index 839fae794c6..00000000000 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandExecutor.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2015-2017 OpenCB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this project 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.app.cli.main.custom; - -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.opencb.commons.datastore.core.ObjectMap; -import org.opencb.opencga.analysis.panel.PanelImportTask; -import org.opencb.opencga.app.cli.session.SessionManager; -import org.opencb.opencga.catalog.exceptions.CatalogException; -import org.opencb.opencga.catalog.io.IOManager; -import org.opencb.opencga.catalog.io.IOManagerFactory; -import org.opencb.opencga.client.config.ClientConfiguration; -import org.opencb.opencga.client.rest.OpenCGAClient; -import org.opencb.opencga.core.models.job.Job; -import org.opencb.opencga.core.models.study.TemplateParams; -import org.opencb.opencga.core.response.RestResponse; -import org.slf4j.Logger; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileVisitResult; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.LinkedList; -import java.util.List; - -public class CustomAnalysisClinicalCommandExecutor extends CustomCommandExecutor { - - public CustomAnalysisClinicalCommandExecutor(ObjectMap options, String token, ClientConfiguration clientConfiguration, - SessionManager session, String appHome, Logger logger) { - super(options, token, clientConfiguration, session, appHome, logger); - } - - public CustomAnalysisClinicalCommandExecutor(ObjectMap options, String token, ClientConfiguration clientConfiguration, - SessionManager session, String appHome, Logger logger, OpenCGAClient openCGAClient) { - super(options, token, clientConfiguration, session, appHome, logger, openCGAClient); - } - - public RestResponse load() throws Exception { - logger.debug("Load clinical analyses from file"); - return openCGAClient.getClinicalAnalysisClient().load(options.getString("study"), options); - } -} diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java deleted file mode 100644 index 72229cb1142..00000000000 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/custom/CustomAnalysisClinicalCommandOptions.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.opencb.opencga.app.cli.main.custom; - -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.beust.jcommander.ParametersDelegate; -import org.opencb.opencga.analysis.clinical.ClinicalAnalysisLoadTask; - -import static org.opencb.opencga.app.cli.GeneralCliOptions.CommonCommandOptions; - -/** - * This class contains methods for the Files command line. OpenCGA version: 2.2.0-SNAPSHOT PATH: /{apiVersion}/files - */ -@Parameters(commandNames = {"studies"}, commandDescription = "Studies commands") -public class CustomAnalysisClinicalCommandOptions { - - public JCommander jCommander; - public CommonCommandOptions commonCommandOptions; - public LoadCommandOptions loadCommandOptions; - - - public CustomAnalysisClinicalCommandOptions(CommonCommandOptions commonCommandOptions, JCommander jCommander) { - - this.jCommander = jCommander; - this.commonCommandOptions = commonCommandOptions; - this.loadCommandOptions = new LoadCommandOptions(); - } - - @Parameters(commandNames = ClinicalAnalysisLoadTask.ID, commandDescription = ClinicalAnalysisLoadTask.DESCRIPTION) - public class LoadCommandOptions { - @ParametersDelegate - public CommonCommandOptions commonOptions = commonCommandOptions; - - @Parameter(names = {"--study", "-s"}, description = "Study [[user@]project:]study where study and project can be either the ID or" + - " UUID", required = false, arity = 1) - public String study; - - @Parameter(names = {"-i", "--input"}, description = "GZIP file containing the clinical analyses", required = true, arity = 1) - public String file; - - @Parameter(names = {"--jobId"}, description = "Job id.", arity = 1) - public String jobId; - - @Parameter(names = {"--jobDependsOn"}, description = "Job depends on.", arity = 1) - public String jobDependsOn; - - @Parameter(names = {"--jobDescription"}, description = "Job description.", arity = 1) - public String jobDescription; - - @Parameter(names = {"--jobTags"}, description = "Job tags.", arity = 1) - public String jobTags; - } -} \ No newline at end of file diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java index 828b035a676..c8830ea76f3 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/AnalysisClinicalCommandExecutor.java @@ -18,40 +18,6 @@ import org.opencb.opencga.app.cli.main.options.AnalysisClinicalCommandOptions; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandOptions; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandExecutor; -import java.io.InputStream; import org.opencb.biodata.models.clinical.ClinicalDiscussion; import org.opencb.biodata.models.clinical.ClinicalProperty; import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; @@ -74,6 +40,7 @@ import org.opencb.opencga.core.models.clinical.ClinicalAnalysisAclEntryList; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisAclUpdateParams; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisCreateParams; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysisLoadParams; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisQualityControl; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisQualityControlUpdateParam; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisUpdateParams; @@ -729,10 +696,9 @@ private RestResponse runInterpreterZetta() throws Exception { private RestResponse load() throws Exception { logger.debug("Executing load in Analysis - Clinical command line"); - CustomAnalysisClinicalCommandOptions.LoadCommandOptions commandOptions = analysisClinicalCommandOptions.loadCommandOptions; + AnalysisClinicalCommandOptions.LoadCommandOptions commandOptions = analysisClinicalCommandOptions.loadCommandOptions; + ObjectMap queryParams = new ObjectMap(); - queryParams.putIfNotNull("file", commandOptions.file); - queryParams.putIfNotEmpty("study", commandOptions.study); queryParams.putIfNotEmpty("study", commandOptions.study); queryParams.putIfNotEmpty("jobId", commandOptions.jobId); queryParams.putIfNotEmpty("jobDescription", commandOptions.jobDescription); @@ -741,8 +707,27 @@ private RestResponse load() throws Exception { if (queryParams.get("study") == null && OpencgaMain.isShellMode()) { queryParams.putIfNotEmpty("study", sessionManager.getSession().getCurrentStudy()); } - CustomAnalysisClinicalCommandExecutor customAnalysisClinicalCommandExecutor = new CustomAnalysisClinicalCommandExecutor(queryParams, token, clientConfiguration, getSessionManager(), appHome, getLogger()); - return customAnalysisClinicalCommandExecutor.load(); + + + ClinicalAnalysisLoadParams clinicalAnalysisLoadParams = null; + if (commandOptions.jsonDataModel) { + clinicalAnalysisLoadParams = new ClinicalAnalysisLoadParams(); + RestResponse res = new RestResponse<>(); + res.setType(QueryType.VOID); + PrintUtils.println(getObjectAsJSON(clinicalAnalysisLoadParams)); + return res; + } else if (commandOptions.jsonFile != null) { + clinicalAnalysisLoadParams = JacksonUtils.getDefaultObjectMapper() + .readValue(new java.io.File(commandOptions.jsonFile), ClinicalAnalysisLoadParams.class); + } else { + ObjectMap beanParams = new ObjectMap(); + putNestedIfNotEmpty(beanParams, "file",commandOptions.file, true); + + clinicalAnalysisLoadParams = JacksonUtils.getDefaultObjectMapper().copy() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .readValue(beanParams.toJson(), ClinicalAnalysisLoadParams.class); + } + return openCGAClient.getClinicalAnalysisClient().load(clinicalAnalysisLoadParams, queryParams); } private RestResponse aggregationStatsRga() throws Exception { diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java index f7e89f52666..85768cc92f4 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AnalysisClinicalCommandOptions.java @@ -10,8 +10,6 @@ import java.util.Map; import java.util.List; -import org.opencb.opencga.app.cli.main.custom.CustomAnalysisClinicalCommandOptions; - import static org.opencb.opencga.app.cli.GeneralCliOptions.*; @@ -30,8 +28,10 @@ * PATH: /{apiVersion}/analysis/clinical */ @Parameters(commandNames = {"clinical"}, commandDescription = "Analysis - Clinical commands") -public class AnalysisClinicalCommandOptions extends CustomAnalysisClinicalCommandOptions { +public class AnalysisClinicalCommandOptions { + public JCommander jCommander; + public CommonCommandOptions commonCommandOptions; public UpdateAclCommandOptions updateAclCommandOptions; public UpdateClinicalConfigurationCommandOptions updateClinicalConfigurationCommandOptions; @@ -69,7 +69,8 @@ public class AnalysisClinicalCommandOptions extends CustomAnalysisClinicalComman public AnalysisClinicalCommandOptions(CommonCommandOptions commonCommandOptions, JCommander jCommander) { - super(commonCommandOptions,jCommander); + this.jCommander = jCommander; + this.commonCommandOptions = commonCommandOptions; this.updateAclCommandOptions = new UpdateAclCommandOptions(); this.updateClinicalConfigurationCommandOptions = new UpdateClinicalConfigurationCommandOptions(); this.createCommandOptions = new CreateCommandOptions(); @@ -834,6 +835,38 @@ public class RunInterpreterZettaCommandOptions { } + @Parameters(commandNames = {"load"}, commandDescription ="Load clinical analyses from a file") + public class LoadCommandOptions { + + @ParametersDelegate + public CommonCommandOptions commonOptions = commonCommandOptions; + + @Parameter(names = {"--json-file"}, description = "File with the body data in JSON format. Note, that using this parameter will ignore all the other parameters.", required = false, arity = 1) + public String jsonFile; + + @Parameter(names = {"--json-data-model"}, description = "Show example of file structure for body data.", help = true, arity = 0) + public Boolean jsonDataModel = false; + + @Parameter(names = {"--study", "-s"}, description = "Study [[user@]project:]study where study and project can be either the ID or UUID", required = false, arity = 1) + public String study; + + @Parameter(names = {"--job-id"}, description = "Job ID. It must be a unique string within the study. An ID will be autogenerated automatically if not provided.", required = false, arity = 1) + public String jobId; + + @Parameter(names = {"--job-description"}, description = "Job description", required = false, arity = 1) + public String jobDescription; + + @Parameter(names = {"--job-depends-on"}, description = "Comma separated list of existing job IDs the job will depend on.", required = false, arity = 1) + public String jobDependsOn; + + @Parameter(names = {"--job-tags"}, description = "Job tags", required = false, arity = 1) + public String jobTags; + + @Parameter(names = {"--file"}, description = "The body web service file parameter", required = false, arity = 1) + public String file; + + } + @Parameters(commandNames = {"rga-aggregation-stats"}, commandDescription ="RGA aggregation stats") public class AggregationStatsRgaCommandOptions { 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 6d6ba53bb95..a660447cc6e 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 @@ -597,7 +597,6 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis public int load(String studyStr, Path filePath, String token) throws CatalogException, IOException { String userId = catalogManager.getUserManager().getUserId(token); - Study study = catalogManager.getStudyManager().resolveId(studyStr, userId, StudyManager.INCLUDE_VARIABLE_SET); // Check gzip format int counter = 0; 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..0195054e743 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 @@ -18,10 +18,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.junit.*; import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.opencb.biodata.models.clinical.ClinicalAudit; @@ -77,6 +74,8 @@ import org.opencb.opencga.core.testclassification.duration.MediumTests; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; @@ -134,7 +133,7 @@ private Family getDummyFamily() { Individual mother = new Individual().setId("mother") .setSex(SexOntologyTermAnnotation.initFemale()) .setDisorders(Arrays.asList(new Disorder("dis2", "dis2", "OT", null, "", - null))); + null))); // We create a new father and mother with the same information to mimic the behaviour of the webservices. Otherwise, we would be // ingesting references to exactly the same object and this test would not work exactly the same way. @@ -3494,4 +3493,36 @@ 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()); } + + @Test + public void loadClinicalAnalysesTest() throws CatalogException, IOException { + String gzFile = getClass().getResource("/biofiles/ca2.json.gz").getFile(); + File file = catalogManager.getFileManager().link(STUDY, new FileLinkParams(gzFile, "", "", "", null, null, null, null, + null), false, sessionIdUser).first(); + + Path filePath = Paths.get(file.getUri()); + + System.out.println("Loading clinical analyses file: " + filePath + " ...."); + int numLoaded = catalogManager.getClinicalAnalysisManager().load(STUDY, filePath, sessionIdUser); + System.out.println("\t\t.... " + numLoaded + " clinical analyses loaded from file " + filePath); + + String ca1Id = "SAP-45016-1"; + String ca2Id = "OPA-6607-1"; + + Query query = new Query(); + OpenCGAResult result = catalogManager.getClinicalAnalysisManager().search(STUDY, query, QueryOptions.empty(), + sessionIdUser); + Assert.assertTrue(result.getResults().stream().map(ca -> ca.getId()).collect(Collectors.toList()).contains(ca1Id)); + Assert.assertTrue(result.getResults().stream().map(ca -> ca.getId()).collect(Collectors.toList()).contains(ca2Id)); + + query.put("id", ca1Id); + ClinicalAnalysis clinicalAnalysis = catalogManager.getClinicalAnalysisManager().search(STUDY, query, QueryOptions.empty(), + sessionIdUser).first(); + Assert.assertEquals(ca1Id, clinicalAnalysis.getId()); + + query.put("id", ca2Id); + clinicalAnalysis = catalogManager.getClinicalAnalysisManager().search(STUDY, query, QueryOptions.empty(), + sessionIdUser).first(); + Assert.assertEquals(ca2Id, clinicalAnalysis.getId()); + } } diff --git a/opencga-catalog/src/test/resources/biofiles/ca2.json.gz b/opencga-catalog/src/test/resources/biofiles/ca2.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..84358f04780428afe997ca2062ae2a30660ecc59 GIT binary patch literal 86718 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≈c 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 AsQ>@~ literal 0 HcmV?d00001 diff --git a/opencga-client/src/main/R/R/AllGenerics.R b/opencga-client/src/main/R/R/AllGenerics.R index b56d877bb7f..622c2b6cfd7 100644 --- a/opencga-client/src/main/R/R/AllGenerics.R +++ b/opencga-client/src/main/R/R/AllGenerics.R @@ -1,6 +1,6 @@ # ############################################################################## ## UserClient -setGeneric("userClient", function(OpencgaR, filterId, users, user, endpointName, params=NULL, ...) +setGeneric("userClient", function(OpencgaR, user, users, filterId, endpointName, params=NULL, ...) standardGeneric("userClient")) # ############################################################################## @@ -10,37 +10,37 @@ setGeneric("projectClient", function(OpencgaR, projects, project, endpointName, # ############################################################################## ## StudyClient -setGeneric("studyClient", function(OpencgaR, group, templateId, studies, study, members, variableSet, endpointName, params=NULL, ...) +setGeneric("studyClient", function(OpencgaR, study, group, variableSet, templateId, studies, members, endpointName, params=NULL, ...) standardGeneric("studyClient")) # ############################################################################## ## FileClient -setGeneric("fileClient", function(OpencgaR, folder, files, annotationSet, file, members, endpointName, params=NULL, ...) +setGeneric("fileClient", function(OpencgaR, file, members, annotationSet, folder, files, endpointName, params=NULL, ...) standardGeneric("fileClient")) # ############################################################################## ## JobClient -setGeneric("jobClient", function(OpencgaR, job, members, jobs, endpointName, params=NULL, ...) +setGeneric("jobClient", function(OpencgaR, members, jobs, job, endpointName, params=NULL, ...) standardGeneric("jobClient")) # ############################################################################## ## SampleClient -setGeneric("sampleClient", function(OpencgaR, sample, members, annotationSet, samples, endpointName, params=NULL, ...) +setGeneric("sampleClient", function(OpencgaR, members, sample, samples, annotationSet, endpointName, params=NULL, ...) standardGeneric("sampleClient")) # ############################################################################## ## IndividualClient -setGeneric("individualClient", function(OpencgaR, members, individual, individuals, annotationSet, endpointName, params=NULL, ...) +setGeneric("individualClient", function(OpencgaR, members, individual, annotationSet, individuals, endpointName, params=NULL, ...) standardGeneric("individualClient")) # ############################################################################## ## FamilyClient -setGeneric("familyClient", function(OpencgaR, families, members, annotationSet, family, endpointName, params=NULL, ...) +setGeneric("familyClient", function(OpencgaR, family, members, annotationSet, families, endpointName, params=NULL, ...) standardGeneric("familyClient")) # ############################################################################## ## CohortClient -setGeneric("cohortClient", function(OpencgaR, members, cohort, annotationSet, cohorts, endpointName, params=NULL, ...) +setGeneric("cohortClient", function(OpencgaR, members, cohorts, cohort, annotationSet, endpointName, params=NULL, ...) standardGeneric("cohortClient")) # ############################################################################## @@ -60,7 +60,7 @@ setGeneric("variantClient", function(OpencgaR, endpointName, params=NULL, ...) # ############################################################################## ## ClinicalClient -setGeneric("clinicalClient", function(OpencgaR, clinicalAnalysis, interpretation, interpretations, study, members, clinicalAnalyses, endpointName, params=NULL, ...) +setGeneric("clinicalClient", function(OpencgaR, clinicalAnalysis, interpretation, members, clinicalAnalyses, interpretations, endpointName, params=NULL, ...) standardGeneric("clinicalClient")) # ############################################################################## diff --git a/opencga-client/src/main/R/R/Clinical-methods.R b/opencga-client/src/main/R/R/Clinical-methods.R index 7ea95271d07..3c6f7a5cf7e 100644 --- a/opencga-client/src/main/R/R/Clinical-methods.R +++ b/opencga-client/src/main/R/R/Clinical-methods.R @@ -32,7 +32,7 @@ #' | runInterpreterTeam | /{apiVersion}/analysis/clinical/interpreter/team/run | study, jobId, jobDescription, jobDependsOn, jobTags, body[*] | #' | runInterpreterTiering | /{apiVersion}/analysis/clinical/interpreter/tiering/run | study, jobId, jobDescription, jobDependsOn, jobTags, body[*] | #' | runInterpreterZetta | /{apiVersion}/analysis/clinical/interpreter/zetta/run | study, jobId, jobDescription, jobDependsOn, jobTags, body[*] | -#' | load | /{apiVersion}/analysis/clinical/load | file, study[*], study, jobId, jobDescription, jobDependsOn, jobTags | +#' | load | /{apiVersion}/analysis/clinical/load | study, jobId, jobDescription, jobDependsOn, jobTags, body[*] | #' | aggregationStatsRga | /{apiVersion}/analysis/clinical/rga/aggregationStats | limit, skip, sampleId, individualId, sex, phenotypes, disorders, numParents, geneId, geneName, chromosome, start, end, transcriptId, variants, dbSnps, knockoutType, filter, type, clinicalSignificance, populationFrequency, consequenceType, study, field[*] | #' | queryRgaGene | /{apiVersion}/analysis/clinical/rga/gene/query | include, exclude, limit, skip, count, includeIndividual, skipIndividual, limitIndividual, sampleId, individualId, sex, phenotypes, disorders, numParents, geneId, geneName, chromosome, start, end, transcriptId, variants, dbSnps, knockoutType, filter, type, clinicalSignificance, populationFrequency, consequenceType, study | #' | summaryRgaGene | /{apiVersion}/analysis/clinical/rga/gene/summary | limit, skip, count, sampleId, individualId, sex, phenotypes, disorders, numParents, geneId, geneName, chromosome, start, end, transcriptId, variants, dbSnps, knockoutType, filter, type, clinicalSignificance, populationFrequency, consequenceType, study | @@ -59,7 +59,7 @@ #' [*]: Required parameter #' @export -setMethod("clinicalClient", "OpencgaR", function(OpencgaR, clinicalAnalysis, interpretation, interpretations, study, members, clinicalAnalyses, endpointName, params=NULL, ...) { +setMethod("clinicalClient", "OpencgaR", function(OpencgaR, clinicalAnalysis, interpretation, members, clinicalAnalyses, interpretations, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/analysis/clinical/acl/{members}/update: @@ -243,15 +243,15 @@ setMethod("clinicalClient", "OpencgaR", function(OpencgaR, clinicalAnalysis, int httpMethod="POST", as.queryParam=NULL, ...), #' @section Endpoint /{apiVersion}/analysis/clinical/load: - #' Gzipped file containing clinical analyses. - #' @param file Gzipped file. + #' Load clinical analyses from a file. #' @param study Study [[user@]project:]study where study and project can be either the ID or UUID. #' @param jobId Job ID. It must be a unique string within the study. An ID will be autogenerated automatically if not provided. #' @param jobDescription Job description. #' @param jobDependsOn Comma separated list of existing job IDs the job will depend on. #' @param jobTags Job tags. + #' @param data Parameters to load clinical analysis in OpenCGA catalog from a file. load=fetchOpenCGA(object=OpencgaR, category="analysis", categoryId=NULL, subcategory="clinical", - subcategoryId=NULL, action="load", params=params, httpMethod="POST", as.queryParam=c("study"), ...), + subcategoryId=NULL, action="load", params=params, httpMethod="POST", as.queryParam=NULL, ...), #' @section Endpoint /{apiVersion}/analysis/clinical/rga/aggregationStats: #' RGA aggregation stats. diff --git a/opencga-client/src/main/R/R/Cohort-methods.R b/opencga-client/src/main/R/R/Cohort-methods.R index 120b0518ec0..b35ee7a1a63 100644 --- a/opencga-client/src/main/R/R/Cohort-methods.R +++ b/opencga-client/src/main/R/R/Cohort-methods.R @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("cohortClient", "OpencgaR", function(OpencgaR, members, cohort, annotationSet, cohorts, endpointName, params=NULL, ...) { +setMethod("cohortClient", "OpencgaR", function(OpencgaR, members, cohorts, cohort, annotationSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/cohorts/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Family-methods.R b/opencga-client/src/main/R/R/Family-methods.R index 37371c7ecd9..d65abc404f3 100644 --- a/opencga-client/src/main/R/R/Family-methods.R +++ b/opencga-client/src/main/R/R/Family-methods.R @@ -38,7 +38,7 @@ #' [*]: Required parameter #' @export -setMethod("familyClient", "OpencgaR", function(OpencgaR, families, members, annotationSet, family, endpointName, params=NULL, ...) { +setMethod("familyClient", "OpencgaR", function(OpencgaR, family, members, annotationSet, families, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/families/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/File-methods.R b/opencga-client/src/main/R/R/File-methods.R index 5b0223b12f5..23df515b2b5 100644 --- a/opencga-client/src/main/R/R/File-methods.R +++ b/opencga-client/src/main/R/R/File-methods.R @@ -54,7 +54,7 @@ #' [*]: Required parameter #' @export -setMethod("fileClient", "OpencgaR", function(OpencgaR, folder, files, annotationSet, file, members, endpointName, params=NULL, ...) { +setMethod("fileClient", "OpencgaR", function(OpencgaR, file, members, annotationSet, folder, files, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/files/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Individual-methods.R b/opencga-client/src/main/R/R/Individual-methods.R index bfb40de5730..4133f71de23 100644 --- a/opencga-client/src/main/R/R/Individual-methods.R +++ b/opencga-client/src/main/R/R/Individual-methods.R @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("individualClient", "OpencgaR", function(OpencgaR, members, individual, individuals, annotationSet, endpointName, params=NULL, ...) { +setMethod("individualClient", "OpencgaR", function(OpencgaR, members, individual, annotationSet, individuals, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/individuals/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Job-methods.R b/opencga-client/src/main/R/R/Job-methods.R index 6c5ee6ffd7e..94abcdf48cd 100644 --- a/opencga-client/src/main/R/R/Job-methods.R +++ b/opencga-client/src/main/R/R/Job-methods.R @@ -40,7 +40,7 @@ #' [*]: Required parameter #' @export -setMethod("jobClient", "OpencgaR", function(OpencgaR, job, members, jobs, endpointName, params=NULL, ...) { +setMethod("jobClient", "OpencgaR", function(OpencgaR, members, jobs, job, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/jobs/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Sample-methods.R b/opencga-client/src/main/R/R/Sample-methods.R index 2d271cfd9b1..3ceadc72b5c 100644 --- a/opencga-client/src/main/R/R/Sample-methods.R +++ b/opencga-client/src/main/R/R/Sample-methods.R @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("sampleClient", "OpencgaR", function(OpencgaR, sample, members, annotationSet, samples, endpointName, params=NULL, ...) { +setMethod("sampleClient", "OpencgaR", function(OpencgaR, members, sample, samples, annotationSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/samples/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Study-methods.R b/opencga-client/src/main/R/R/Study-methods.R index b1100eac7dc..7869db7f30a 100644 --- a/opencga-client/src/main/R/R/Study-methods.R +++ b/opencga-client/src/main/R/R/Study-methods.R @@ -46,7 +46,7 @@ #' [*]: Required parameter #' @export -setMethod("studyClient", "OpencgaR", function(OpencgaR, group, templateId, studies, study, members, variableSet, endpointName, params=NULL, ...) { +setMethod("studyClient", "OpencgaR", function(OpencgaR, study, group, variableSet, templateId, studies, members, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/studies/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/User-methods.R b/opencga-client/src/main/R/R/User-methods.R index 6dd8c1b162a..4cefb8e2c86 100644 --- a/opencga-client/src/main/R/R/User-methods.R +++ b/opencga-client/src/main/R/R/User-methods.R @@ -38,7 +38,7 @@ #' [*]: Required parameter #' @export -setMethod("userClient", "OpencgaR", function(OpencgaR, filterId, users, user, endpointName, params=NULL, ...) { +setMethod("userClient", "OpencgaR", function(OpencgaR, user, users, filterId, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/users/login: diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java index 68cf835335f..58d37b4caf6 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/AbstractParentClient.java @@ -315,11 +315,7 @@ private RestResponse callRest(WebTarget path, ObjectMap params, Class batchRestResponse = new RestResponse<>(); break; default: - if (action.equals("load") && path.toString().contains("analysis/clinical")) { - batchRestResponse = callUploadRest(path, params, clazz); - } else { - batchRestResponse = callRest(path, params, clazz, method); - } + batchRestResponse = callRest(path, params, clazz, method); break; } return batchRestResponse; diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java index e64ddb3ac0a..886b7a6c547 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java @@ -33,6 +33,7 @@ import org.opencb.opencga.core.models.clinical.ClinicalAnalysisAclEntryList; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisAclUpdateParams; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisCreateParams; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysisLoadParams; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisUpdateParams; import org.opencb.opencga.core.models.clinical.ExomiserInterpretationAnalysisParams; import org.opencb.opencga.core.models.clinical.Interpretation; @@ -334,10 +335,9 @@ public RestResponse runInterpreterZetta(ZettaInterpretationAnalysisParams d } /** - * Gzipped file containing clinical analyses. - * @param study Study [[user@]project:]study where study and project can be either the ID or UUID. + * Load clinical analyses from a file. + * @param data Parameters to load clinical analysis in OpenCGA catalog from a file. * @param params Map containing any of the following optional parameters. - * file: Gzipped file. * study: Study [[user@]project:]study where study and project can be either the ID or UUID. * jobId: Job ID. It must be a unique string within the study. An ID will be autogenerated automatically if not provided. * jobDescription: Job description. @@ -346,9 +346,9 @@ public RestResponse runInterpreterZetta(ZettaInterpretationAnalysisParams d * @return a RestResponse object. * @throws ClientException ClientException if there is any server error. */ - public RestResponse load(String study, ObjectMap params) throws ClientException { + public RestResponse load(ClinicalAnalysisLoadParams data, ObjectMap params) throws ClientException { params = params != null ? params : new ObjectMap(); - params.putIfNotNull("study", study); + params.put("body", data); return execute("analysis", null, "clinical", null, "load", params, POST, Job.class); } diff --git a/opencga-client/src/main/javascript/ClinicalAnalysis.js b/opencga-client/src/main/javascript/ClinicalAnalysis.js index 8695a7d4daa..3ebee290063 100644 --- a/opencga-client/src/main/javascript/ClinicalAnalysis.js +++ b/opencga-client/src/main/javascript/ClinicalAnalysis.js @@ -256,10 +256,10 @@ export default class ClinicalAnalysis extends OpenCGAParentClass { return this._post("analysis", null, "clinical/interpreter/zetta", null, "run", data, params); } - /** Gzipped file containing clinical analyses - * @param {String} study - Study [[user@]project:]study where study and project can be either the ID or UUID. + /** Load clinical analyses from a file + * @param {Object} data - Parameters to load clinical analysis in OpenCGA catalog from a file. * @param {Object} [params] - The Object containing the following optional parameters: - * @param {InputStream} [params.file] - Gzipped file. + * @param {String} [params.study] - Study [[user@]project:]study where study and project can be either the ID or UUID. * @param {String} [params.jobId] - Job ID. It must be a unique string within the study. An ID will be autogenerated automatically if not * provided. * @param {String} [params.jobDescription] - Job description. @@ -267,8 +267,8 @@ export default class ClinicalAnalysis extends OpenCGAParentClass { * @param {String} [params.jobTags] - Job tags. * @returns {Promise} Promise object in the form of RestResponse instance. */ - load(study, params) { - return this._post("analysis", null, "clinical", null, "load", {study, ...params}); + load(data, params) { + return this._post("analysis", null, "clinical", null, "load", data, params); } /** RGA aggregation stats diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py index 457ece8ef57..5af123acb9f 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py @@ -316,12 +316,13 @@ def run_interpreter_zetta(self, data=None, **options): return self._post(category='analysis', resource='run', subcategory='clinical/interpreter/zetta', data=data, **options) - def load(self, study, **options): + def load(self, data=None, **options): """ - Gzipped file containing clinical analyses. + Load clinical analyses from a file. PATH: /{apiVersion}/analysis/clinical/load - :param inputstream file: Gzipped file. + :param dict data: Parameters to load clinical analysis in OpenCGA + catalog from a file. (REQUIRED) :param str study: Study [[user@]project:]study where study and project can be either the ID or UUID. :param str job_id: Job ID. It must be a unique string within the @@ -332,8 +333,7 @@ def load(self, study, **options): :param str job_tags: Job tags. """ - options['study'] = study - return self._post(category='analysis', resource='load', subcategory='clinical', **options) + return self._post(category='analysis', resource='load', subcategory='clinical', data=data, **options) def aggregation_stats_rga(self, field, **options): """ diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java index 229c263cce0..9d3f75e7359 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/clinical/ClinicalAnalysisLoadParams.java @@ -7,31 +7,32 @@ import java.nio.file.Path; public class ClinicalAnalysisLoadParams extends ToolParams { + public static final String DESCRIPTION = "Parameters to load clinical analysis in OpenCGA catalog from a file"; + public static final String FILE = "file"; - @DataField(id = ParamConstants.FILE_PATH_PARAM, description = ParamConstants.FILE_PATH_DESCRIPTION) - private Path path; + private String file; public ClinicalAnalysisLoadParams() { } - public ClinicalAnalysisLoadParams(Path path) { - this.path = path; + public ClinicalAnalysisLoadParams(String file) { + this.file = file; } @Override public String toString() { final StringBuilder sb = new StringBuilder("ClinicalAnalysisLoadParams{"); - sb.append("path=").append(path); + sb.append("file=").append(file); sb.append('}'); return sb.toString(); } - public Path getPath() { - return path; + public String getFile() { + return file; } - public ClinicalAnalysisLoadParams setPath(Path path) { - this.path = path; + public ClinicalAnalysisLoadParams setFile(String file) { + this.file = file; return this; } } 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 8e00434ab7b..ecf241e0cff 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 @@ -16,12 +16,8 @@ package org.opencb.opencga.server.rest.analysis; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; -import org.glassfish.jersey.media.multipart.FormDataContentDisposition; -import org.glassfish.jersey.media.multipart.FormDataParam; import org.opencb.biodata.models.clinical.interpretation.ClinicalVariant; import org.opencb.commons.datastore.core.*; import org.opencb.opencga.analysis.clinical.ClinicalAnalysisLoadTask; @@ -39,13 +35,8 @@ import org.opencb.opencga.analysis.variant.manager.VariantStorageManager; import org.opencb.opencga.catalog.db.api.ClinicalAnalysisDBAdaptor; import org.opencb.opencga.catalog.db.api.InterpretationDBAdaptor; -import org.opencb.opencga.catalog.exceptions.CatalogException; -import org.opencb.opencga.catalog.exceptions.CatalogIOException; -import org.opencb.opencga.catalog.io.IOManager; import org.opencb.opencga.catalog.managers.ClinicalAnalysisManager; -import org.opencb.opencga.catalog.managers.FileManager; import org.opencb.opencga.catalog.managers.InterpretationManager; -import org.opencb.opencga.catalog.managers.StudyManager; import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; @@ -54,7 +45,6 @@ import org.opencb.opencga.core.models.analysis.knockout.*; import org.opencb.opencga.core.models.clinical.*; import org.opencb.opencga.core.models.job.Job; -import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.study.configuration.ClinicalAnalysisStudyConfiguration; import org.opencb.opencga.core.tools.annotations.*; @@ -62,17 +52,15 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.*; import javax.ws.rs.core.*; -import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.opencb.opencga.analysis.variant.manager.VariantCatalogQueryUtils.SAVED_FILTER_DESCR; -import static org.opencb.opencga.core.api.ParamConstants.*; +import static org.opencb.opencga.core.api.ParamConstants.INCLUDE_INTERPRETATION; +import static org.opencb.opencga.core.api.ParamConstants.JOB_DEPENDS_ON; import static org.opencb.opencga.server.rest.analysis.VariantWebService.getVariantQuery; import static org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam.*; @@ -172,31 +160,17 @@ public Response create( @POST @Path("/load") - @Consumes(MediaType.MULTIPART_FORM_DATA) - @ApiOperation(httpMethod = "POST", value = "Gzipped file containing clinical analyses", response = Job.class) + @ApiOperation(value = ClinicalAnalysisLoadTask.DESCRIPTION, response = Job.class) public Response load( - @ApiParam(value = "Gzipped file") @FormDataParam("file") InputStream fileInputStream, - @FormDataParam("file") FormDataContentDisposition fileMetaData, - @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @PathParam(ParamConstants.STUDY_PARAM) String studyStr, @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @QueryParam(ParamConstants.STUDY_PARAM) String study, @ApiParam(value = ParamConstants.JOB_ID_CREATION_DESCRIPTION) @QueryParam(ParamConstants.JOB_ID) String jobId, @ApiParam(value = ParamConstants.JOB_DESCRIPTION_DESCRIPTION) @QueryParam(ParamConstants.JOB_DESCRIPTION) String jobDescription, @ApiParam(value = ParamConstants.JOB_DEPENDS_ON_DESCRIPTION) @QueryParam(JOB_DEPENDS_ON) String dependsOn, - @ApiParam(value = ParamConstants.JOB_TAGS_DESCRIPTION) @QueryParam(ParamConstants.JOB_TAGS) String jobTags) { + @ApiParam(value = ParamConstants.JOB_TAGS_DESCRIPTION) @QueryParam(ParamConstants.JOB_TAGS) String jobTags, + @ApiParam(value = ClinicalAnalysisLoadParams.DESCRIPTION, required = true) ClinicalAnalysisLoadParams params) { try { - // Prepare input file - java.nio.file.Path scratchDir = Paths.get(catalogManager.getConfiguration().getAnalysis().getScratchDir()) - .resolve(ClinicalAnalysisLoadTask.ID + "_" + RandomStringUtils.randomAlphanumeric(10)); - scratchDir.toFile().mkdirs(); - File inputFile = scratchDir.resolve(fileMetaData.getFileName()).toFile(); - logger.info("Uploaded clinical analyses file at {}", inputFile.getAbsolutePath()); - IOManager ioManager = catalogManager.getIoManagerFactory().getDefault(); - ioManager.copy(fileInputStream, inputFile.toURI()); - // Execute load as a job - ClinicalAnalysisLoadParams params = new ClinicalAnalysisLoadParams(); - params.setPath(inputFile.toPath()); - return submitJob(ClinicalAnalysisLoadTask.ID, studyStr, params, jobId, jobDescription, dependsOn, jobTags); + return submitJob(ClinicalAnalysisLoadTask.ID, study, params, jobId, jobDescription, dependsOn, jobTags); } catch (Exception e) { return createErrorResponse("Load clinical analyses from file", e.getMessage()); } diff --git a/opencga-server/src/main/resources/cli-config.yaml b/opencga-server/src/main/resources/cli-config.yaml index afa6eba72fc..b3a3c302120 100644 --- a/opencga-server/src/main/resources/cli-config.yaml +++ b/opencga-server/src/main/resources/cli-config.yaml @@ -163,12 +163,7 @@ apiConfig: ignore: False key: ClinicalAnalysis analysis: True - executorExtended: True - optionExtended: True commands: - - name: load - executorExtended: True - optionExtended: True - name: update subcommands: - name: commentsAction From f9c2e308955d925e71fbc305f026ece9c0d77afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Fri, 15 Sep 2023 16:25:39 +0200 Subject: [PATCH 06/18] analysis: add clinical analysis load task test, #TASK-4610, #TASK-TASK-4158 --- .../clinical/ClinicalAnalysisLoadTask.java | 2 +- .../analysis/variant/VariantAnalysisTest.java | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java index f4f72e67cab..aeb4236a527 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java @@ -37,7 +37,7 @@ protected void check() throws Exception { File file = catalogManager.getFileManager().get(getStudy(), fileStr, FileManager.INCLUDE_FILE_URI_PATH, token).first(); filePath = Paths.get(file.getUri()); if (!filePath.toFile().exists()) { - throw new ToolException("Input file '" + filePath + "' does not exist."); + throw new ToolException("Input file '" + fileStr + "' does not exist: " + filePath); } } diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java index 31a59eebc2e..69d7b949a79 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java @@ -38,6 +38,7 @@ import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.TestParamConstants; +import org.opencb.opencga.analysis.clinical.ClinicalAnalysisLoadTask; import org.opencb.opencga.analysis.tools.ToolRunner; import org.opencb.opencga.analysis.variant.gwas.GwasAnalysis; import org.opencb.opencga.analysis.variant.hrdetect.HRDetectAnalysis; @@ -60,12 +61,15 @@ import org.opencb.opencga.core.common.JacksonUtils; import org.opencb.opencga.core.config.storage.StorageConfiguration; import org.opencb.opencga.core.exceptions.ToolException; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysis; +import org.opencb.opencga.core.models.clinical.ClinicalAnalysisLoadParams; import org.opencb.opencga.core.models.cohort.Cohort; import org.opencb.opencga.core.models.cohort.CohortCreateParams; import org.opencb.opencga.core.models.cohort.CohortUpdateParams; import org.opencb.opencga.core.models.common.AnnotationSet; import org.opencb.opencga.core.models.family.Family; import org.opencb.opencga.core.models.file.File; +import org.opencb.opencga.core.models.file.FileLinkParams; import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.individual.IndividualInternal; import org.opencb.opencga.core.models.individual.Location; @@ -1056,6 +1060,46 @@ public void testPedigreeGraph() throws CatalogException { assertEquals(base64, family.getPedigreeGraph().getBase64()); } + @Test + public void testClinicalAnalysisLoading() throws IOException, ToolException, CatalogException { + String fileStr = "ca2.json.gz"; + + String gzFile = getClass().getResource("/biofiles/" + fileStr).getFile(); + File file = catalogManager.getFileManager().link(CANCER_STUDY, new FileLinkParams(gzFile, "ca", "", "", null, null, null, null, + null), true, token).first(); + System.out.println("file ID = " + file.getId()); + System.out.println("file name = " + file.getName()); + + // Run clinical analysis load task + Path loadingOutDir = Paths.get(opencga.createTmpOutdir("_clinical_analysis_outdir")); + System.out.println("Clinical analysis load task out dir = " + loadingOutDir); + + ClinicalAnalysisLoadParams params = new ClinicalAnalysisLoadParams(); + params.setFile(file.getId()); + + toolRunner.execute(ClinicalAnalysisLoadTask.class, params, new ObjectMap(ParamConstants.STUDY_PARAM, + CANCER_STUDY), loadingOutDir, null, token); + + String ca1Id = "SAP-45016-1"; + String ca2Id = "OPA-6607-1"; + + Query query = new Query(); + OpenCGAResult result = catalogManager.getClinicalAnalysisManager().search(CANCER_STUDY, query, QueryOptions.empty(), + token); + Assert.assertTrue(result.getResults().stream().map(ca -> ca.getId()).collect(Collectors.toList()).contains(ca1Id)); + Assert.assertTrue(result.getResults().stream().map(ca -> ca.getId()).collect(Collectors.toList()).contains(ca2Id)); + + query.put("id", ca1Id); + ClinicalAnalysis clinicalAnalysis = catalogManager.getClinicalAnalysisManager().search(CANCER_STUDY, query, QueryOptions.empty(), + token).first(); + Assert.assertEquals(ca1Id, clinicalAnalysis.getId()); + + query.put("id", ca2Id); + clinicalAnalysis = catalogManager.getClinicalAnalysisManager().search(CANCER_STUDY, query, QueryOptions.empty(), + token).first(); + Assert.assertEquals(ca2Id, clinicalAnalysis.getId()); + } + public void checkExecutionResult(ExecutionResult er) { checkExecutionResult(er, true); } From 5ac1346d89d1dc89556410d230b066e75fadf46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Mon, 18 Sep 2023 16:13:04 +0200 Subject: [PATCH 07/18] analysis: add a data model for the clinical analysis load result, #TASK-4610, #TASK-4158 --- .../clinical/ClinicalAnalysisLoadTask.java | 21 ++++- .../analysis/variant/VariantAnalysisTest.java | 2 +- .../managers/ClinicalAnalysisManager.java | 18 +++- .../models/ClinicalAnalysisLoadResult.java | 86 ++++++++++++++++++ .../managers/ClinicalAnalysisManagerTest.java | 9 +- .../managers/ClinicalAnalysisTest.java | 58 ------------ ...{ca2.json.gz => clinical_analyses.json.gz} | Bin 86718 -> 122562 bytes 7 files changed, 125 insertions(+), 69 deletions(-) create mode 100644 opencga-catalog/src/main/java/org/opencb/opencga/catalog/models/ClinicalAnalysisLoadResult.java delete mode 100644 opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java rename opencga-catalog/src/test/resources/biofiles/{ca2.json.gz => clinical_analyses.json.gz} (60%) diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java index aeb4236a527..735d3f55e11 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java @@ -1,19 +1,19 @@ package org.opencb.opencga.analysis.clinical; import org.apache.commons.lang3.StringUtils; -import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.analysis.tools.OpenCgaToolScopeStudy; import org.opencb.opencga.catalog.managers.FileManager; +import org.opencb.opencga.catalog.models.ClinicalAnalysisLoadResult; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisLoadParams; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.file.File; -import org.opencb.opencga.core.models.panel.PanelImportParams; import org.opencb.opencga.core.tools.annotations.Tool; import org.opencb.opencga.core.tools.annotations.ToolParams; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map; @Tool(id = ClinicalAnalysisLoadTask.ID, resource = Enums.Resource.DISEASE_PANEL, description = ClinicalAnalysisLoadTask.DESCRIPTION) public class ClinicalAnalysisLoadTask extends OpenCgaToolScopeStudy { @@ -43,6 +43,21 @@ protected void check() throws Exception { @Override protected void run() throws Exception { - step(() -> catalogManager.getClinicalAnalysisManager().load(getStudy(), filePath, token)); + step(() -> { + ClinicalAnalysisLoadResult loadResult = catalogManager.getClinicalAnalysisManager().load(getStudy(), filePath, token); + + // Add results as attributes + addAttribute("Num. clinical analyses loaded", loadResult.getNumLoaded()); + addAttribute("Num. clinical analyses not loaded", loadResult.getFailures().size()); + addAttribute("Loading time (in sec.)", loadResult.getTime()); + addAttribute("Clinical analyses file name", loadResult.getFilename()); + + // Add warnings with the not loaded clinical analysis + if (loadResult.getFailures().size() > 0) { + for (Map.Entry entry : loadResult.getFailures().entrySet()) { + addWarning("Clinical analysis " + entry.getKey() + " could not be loaded due to error: " + entry.getValue()); + } + } + }); } } diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java index 69d7b949a79..b089cc79869 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/VariantAnalysisTest.java @@ -1062,7 +1062,7 @@ public void testPedigreeGraph() throws CatalogException { @Test public void testClinicalAnalysisLoading() throws IOException, ToolException, CatalogException { - String fileStr = "ca2.json.gz"; + String fileStr = "clinical_analyses.json.gz"; String gzFile = getClass().getResource("/biofiles/" + fileStr).getFile(); File file = catalogManager.getFileManager().link(CANCER_STUDY, new FileLinkParams(gzFile, "ca", "", "", null, null, null, null, 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 a660447cc6e..3ceb9b2e0f4 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 @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectReader; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.StopWatch; import org.opencb.biodata.models.clinical.ClinicalAnalyst; import org.opencb.biodata.models.clinical.ClinicalAudit; import org.opencb.biodata.models.clinical.ClinicalComment; @@ -38,6 +39,7 @@ import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.exceptions.CatalogParameterException; +import org.opencb.opencga.catalog.models.ClinicalAnalysisLoadResult; import org.opencb.opencga.catalog.models.InternalGetDataResult; import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; @@ -76,6 +78,7 @@ import java.io.IOException; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -595,13 +598,13 @@ public OpenCGAResult create(String studyStr, ClinicalAnalysis } } - public int load(String studyStr, Path filePath, String token) throws CatalogException, IOException { - String userId = catalogManager.getUserManager().getUserId(token); + public ClinicalAnalysisLoadResult load(String studyStr, Path filePath, String token) throws CatalogException, IOException { + ClinicalAnalysisLoadResult result = new ClinicalAnalysisLoadResult(); - // Check gzip format int counter = 0; ObjectReader objectReader = JacksonUtils.getDefaultObjectMapper().readerFor(ClinicalAnalysis.class); + StopWatch stopWatch = StopWatch.createStarted(); try (BufferedReader br = FileUtils.newBufferedReader(filePath)) { while (true) { String line = br.readLine(); @@ -618,10 +621,17 @@ public int load(String studyStr, Path filePath, String token) throws CatalogExce } catch (Exception e) { logger.error("Error loading clinical analysis" + (clinicalAnalysis != null ? (": " + clinicalAnalysis.getId()) : "") + ": " + e.getMessage()); + result.getFailures().put(clinicalAnalysis.getId(), e.getMessage()); } } } - return counter; + stopWatch.stop(); + + result.setNumLoaded(counter) + .setFilename(filePath.getFileName().toString()) + .setTime((int) stopWatch.getTime(TimeUnit.SECONDS)); + + return result; } private void load(ClinicalAnalysis clinicalAnalysis, String study, String token) throws CatalogException { diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/models/ClinicalAnalysisLoadResult.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/models/ClinicalAnalysisLoadResult.java new file mode 100644 index 00000000000..d154b56871e --- /dev/null +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/models/ClinicalAnalysisLoadResult.java @@ -0,0 +1,86 @@ +/* + * 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.catalog.models; + +import java.util.HashMap; +import java.util.Map; + +public class ClinicalAnalysisLoadResult { + private int numLoaded; + private Map failures; + private String filename; + private int time; + + public ClinicalAnalysisLoadResult() { + this.numLoaded = 0; + failures = new HashMap<>(); + } + + public ClinicalAnalysisLoadResult(int numLoaded, Map failures, String filename, int time) { + this.numLoaded = numLoaded; + this.failures = failures; + this.filename = filename; + this.time = time; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ClinicalAnalysisLoadResult{"); + sb.append("numLoaded=").append(numLoaded); + sb.append(", failures=").append(failures); + sb.append(", filename='").append(filename).append('\''); + sb.append(", time='").append(time).append('\''); + sb.append('}'); + return sb.toString(); + } + + public int getNumLoaded() { + return numLoaded; + } + + public ClinicalAnalysisLoadResult setNumLoaded(int numLoaded) { + this.numLoaded = numLoaded; + return this; + } + + public Map getFailures() { + return failures; + } + + public ClinicalAnalysisLoadResult setFailures(Map failures) { + this.failures = failures; + return this; + } + + public String getFilename() { + return filename; + } + + public ClinicalAnalysisLoadResult setFilename(String filename) { + this.filename = filename; + return this; + } + + public int getTime() { + return time; + } + + public ClinicalAnalysisLoadResult setTime(int time) { + this.time = time; + return this; + } +} 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 0195054e743..dc2cdc95926 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 @@ -41,6 +41,7 @@ import org.opencb.opencga.catalog.db.api.ClinicalAnalysisDBAdaptor; import org.opencb.opencga.catalog.db.api.InterpretationDBAdaptor; import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.models.ClinicalAnalysisLoadResult; import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; @@ -3496,15 +3497,17 @@ public void fetchCasesWithSameProbandAndDifferentSample() throws CatalogExceptio @Test public void loadClinicalAnalysesTest() throws CatalogException, IOException { - String gzFile = getClass().getResource("/biofiles/ca2.json.gz").getFile(); + String gzFile = getClass().getResource("/biofiles/clinical_analyses.json.gz").getFile(); File file = catalogManager.getFileManager().link(STUDY, new FileLinkParams(gzFile, "", "", "", null, null, null, null, null), false, sessionIdUser).first(); Path filePath = Paths.get(file.getUri()); System.out.println("Loading clinical analyses file: " + filePath + " ...."); - int numLoaded = catalogManager.getClinicalAnalysisManager().load(STUDY, filePath, sessionIdUser); - System.out.println("\t\t.... " + numLoaded + " clinical analyses loaded from file " + filePath); + ClinicalAnalysisLoadResult loadResult = catalogManager.getClinicalAnalysisManager().load(STUDY, filePath, sessionIdUser); + System.out.println(loadResult); + + Assert.assertEquals(1, loadResult.getFailures().size()); String ca1Id = "SAP-45016-1"; String ca2Id = "OPA-6607-1"; diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java deleted file mode 100644 index 5255570707f..00000000000 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/ClinicalAnalysisTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.opencb.opencga.catalog.managers; - -import org.apache.commons.io.FileUtils; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; -import org.opencb.opencga.catalog.exceptions.CatalogException; -import org.opencb.opencga.core.config.Configuration; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; - -public class ClinicalAnalysisTest { - - private String sessionIdUser; - private CatalogManager catalogManager; - - private File testFile; - - private String study; - private Path clinicalAnalysesDir; - - @Before - public void init() throws CatalogException, IOException { - // line #1: username - // line #2: password - // line #3: study - // line #4: path to the OpenCGA configuration file - // line #5: path to the folder where the clinical analyses to load are located at - testFile = Paths.get("/tmp/load-clinical-analyses.conf").toFile(); - Assume.assumeTrue(testFile.exists()); - - List lines = FileUtils.readLines(testFile); - String user = lines.get(0); - String pass = lines.get(1); - study = lines.get(2); - String confPath = lines.get(3); - clinicalAnalysesDir = Paths.get(lines.get(4)); - - Configuration configuration = Configuration.load(new FileInputStream(confPath)); - catalogManager = new CatalogManager(configuration); - sessionIdUser = catalogManager.getUserManager().login(user, pass).getToken(); - } - - @Test - public void loadClinicalAnalysesTest() throws CatalogException, IOException { - Assume.assumeTrue(clinicalAnalysesDir.toFile().exists()); - for (File file : clinicalAnalysesDir.toFile().listFiles()) { - System.out.println("Loading clinical analyses file: " + file.getAbsolutePath() + " ...."); - int numLoaded = catalogManager.getClinicalAnalysisManager().load(study, file.toPath(), sessionIdUser); - System.out.println("\t\t.... " + numLoaded + " clinical analyses loaded from file " + file.getAbsolutePath()); - } - } -} \ No newline at end of file diff --git a/opencga-catalog/src/test/resources/biofiles/ca2.json.gz b/opencga-catalog/src/test/resources/biofiles/clinical_analyses.json.gz similarity index 60% rename from opencga-catalog/src/test/resources/biofiles/ca2.json.gz rename to opencga-catalog/src/test/resources/biofiles/clinical_analyses.json.gz index 84358f04780428afe997ca2062ae2a30660ecc59..59ce26c8ad80355ac41d2096b3122e76f1c26c57 100644 GIT binary patch delta 48741 zcmYg%1yEdF({zGsNN^_v3+`^g-QC^Yol9_n1$PMU?rs4Bi@UqKyZl7H_xY=++Mb#2 zp4r;E>^U)X#jIM3p+K~6N(QXfd z?Oza$EU0OI26)zJo90q~j4P^R-~``Iix6t{Pg&6=ip(bwo$haUZ#}?IRS@`jA1X@} zdmKVp=`^ndRv+EX4yDyj9ACCtpn=8Q;5!B~)JzfX$WGAKT|TlB^R>VzpVoKkT&LFEI-8D5IK?E zBtD8_@V+!BAU4xHnbCa`qAzBhfZl*iyzP_8?zDQBF-9-t7Qm=LFzz?9UrnYUJu*8` zoSu4t36!s!1`u;2U+SEhQ1fBTn76m-RqgL7wO(oGt9@z>ne3Eywx!u)OP3c7%b1Ho zKc9oM6j3?t*G*CCz{RiiIqN~g6v6h}$0#gfKIYmh`sIlE2%X90ygbt-jjC8i;4H56 zxXx=fv@coQ>VIsfE=DzdUoA6AoPAoD?s!C}2Jqr~O9a_S2~iRf<(fG6|56g^+gD&& z+Z+k_j#bwBHNxOt&3h#u6Snnx{ua&H=<8U z4N$Tb*hiV^DAHA`pio6CX+qSBTVP@RFD6kYF`xL*@UIM~yaZLOK1lzg*jK6Zw`RZY zu?VUj7gibaZ)S>muZZ8?%Y*Gn2Y^SXdiN8Ha+6uS~Nv zFd={~tkQ{Ddqk&JH(!wGrrln@vFLCIbkk@cz@~k!@H;u?SRquwrysx%Cehx#0uP7- z;$D;rsFE9)>5x#oR{*>su{@Lx?22U~RkD<K z)1`G;-%6Y)iJev0pCee<*}L?|lTuX=5WAhE`nc7|P)$*`H-+22>kkjFdH}V!ner7S zrs56z)bHi1zq^9)<+}~T=_iPtWR6wLZv~JP7gMt|hlxJ}aHx>qz=Yyrvv6r7Wl=x> z4k<{4#PrEg0`Xs?fL0WX<22o>o>l@n!FWqu?U}^L#~fVBU)=vZ zu8K(yH)pD1ZhC2W97h)NVm~$7CP9k7&GnJLUYatlO%xNlDNU!K-KK$b!bye~5eW(SrN6EL?N&wqYC@r+%I{K~t{{6sH|^+aDR z)o0sZqR*SuD@t0O^P!6gZ?7Pg(*w2aZT+Ni4Mn3A@Ku?w_=nOcg{hOqJO#km zxaIr5goX4Q5lNeA=+b|weMZwM=6sic|4FW}q~GX?-u_RZQDd%-Zo*=URs*LF`KqcH zEY?()?A!jIz=^X>3H0WFek=(kmS08Ryf*)>Si&y_>_NzXhWQg?mnM%pefc*XEy_pc zNK)jFV_($jY>kSOKtSx`18Bi02~ zMC5S?zjG!M?reL^vsc6n#k^thX_e`a7NB}#`Nku|C+!#nQStWB3(OhY0s+tkpM8Kh zWJ<&LLDW6LsMzRu{|2XHnO3*&YtC<-N{4iv82Id)Yo529L#1&4*r|{mP!%`uvv!}T zezPGffW9@3EpAN_NW`PNHI7`aL)SQ6THMlVE{-`TOOYg;17R}wekyk6N^O>6m3$4Z zWXB%Y1<=Lz4$zxFqLQyOe1H*L6K=h3I%cZ)j;ZSU@H2**$DCypw&g-;HB;GjmD)tT z`Ripd)>SYMGVZNu61DeNyL7wHylbQ{x?=`6M{%b7RA{yLLXN`!q7yQiITEjyQF@}B zVdOA1hITb~ht_h`{V8FTN^vf?s6?+}oS9YDNNQNh0^nE}4l0(oUQa&M(ZJYIY4>Ea zO7m{xYA@1Z&K%-T>A7A|(Sy`uB+VOPeM9MkcI}IHJ>2_b5o$MqUPqXkn)EQE?(Wi< z`!JIV6(1{JQ*qcd31u_j44G5Q?7>PXJlkA6*Bk&JLrX7Ts2>{TYXX)YVnk^*aw%!4 zms=W}4unkQ39)^SFR!%|uxSv!77A)=Z|OQrI@D_z#?TLNer)0{qp^EWU($K$v)ZML`AY=OnQ zU^tg7nb`H4+M5MDdsXl^&^biL3*r%JY~d*HMQTedsY77=$nfy|Z@@zncbq6wp0DwJ z1%O89US+ZIA>-({1u<}?I3X-W(Ncv2x5>a81@xNKn@ar zgZP_q)L{34)s2Ts?00|r7ld@9|K%8(;QY;q6@2~&O7LF+`WuwOj}obH=Z7$R!#YH0 zn}B&}y(nbk<0c8HMw8jt!bZ#yp)Y;j{o?FG2j{>ap)qidJ^zb`2&@HOo198W)LdP9h}OiaS}crO#0(maDt1a%5! zu7tD2UZ!w{s)e<*3V_{W_Y29V39f9LD`3Pa%DqgOBFh_hX3mr;Z{J2ZH+dgxpB_=m zO~5OUBW3?(|80?Gb!M_H`b(zoedN8D79fI#x0Mxa80tvxL}P1FzQP6@8C`qpWbyFW z1X^!1ly(YkB9g<6E(S*qkPoQNZ4&(|URle0Droh!wp9WRLHU4BqqN-1r?Y>A3^1xu zPmtlRP))G0*Xv?pncM8j?U}uVUB9AbwZJSncpy!{yJ8rrPt2Z4Wpp5E&(Yor27Q}0 zz?&S8(irPb8lR%A`P!fb*R>Eu%{xmmbzuWx8Lc*_4-~Ig~|9isV z_kyk+f~NF(qJJ>VCoaoROpU;nFASgmYklo4O6Df<0n{rE<`cvRe7X^7 zub=Lx8U(XMn2fi7*g;bM5#Q;6@ZY+pk6kM(n)>B)D3_%6I%?F|pC_UsERC?TE_e4* zYK5f;Tv&M{qn%<&t3}~@=BTPbd-*#t-vy`I^_wzd7fsmRHRb}xb3Ym4bfwmP9=U}7 zLO-_-`5Ek6IeZV(2c#b`pW7HHNf&V62z@!L&BSa*_zj#g6U$7%fNwtQ7S;MKPf)TD zs5&^`>>6->(^*hATG@O){kWa??qLhU|DB>io!I35ladWnpaTD`JNRZNN%~q7mVLwv zx`@58#Ye;NBQnx?woO-BPqO72IsdNK&3wr)J>Pfn{hEgxP5b%UEbYQD;gktXd`0&Q zd9?)~$&Q7qN0e1F`5|}EntqG8UXm3<9y>gvO~Ya`Sp?od{3^@9o?l&)g8dV9t0t-< z_S8!;5r(riHeQ9)qfvtiixeW-}~OUwiu2ptypP(nKX=oYrRT0>(J zs0k7J9EUu&jnpKpr?G3RvjgPL&p+2IkW<7SD~i)#K=zBge)Qix2TppJT5Fk%`z?LG zxJgB10enu`?{_yFz)}?{*FK^0=TvP=R>95?o55y z4)u?kbt}?s``>UCJVew#Z~jfNfxrug?1}PF{(tj)0hl2Fy(wP;lRG)MZpZ(cX5Hd< z$N!nzF5~X(hMUd286Tv>CHs1nch>-d#p(F9c;V?yYQ?mi<|$hdWx?=0WGXo-6G*gE zVhiM37<4yv?}Wr#%h^z28JB#M%5FEy626#XXCU~4Ws(S);=YCa3p|D4-oyFFqh{Kn zOMOIzp(f#Y4OSJ^u1u}k?Up#*AavTfxFUdnzb!O8bwo8QpQRbIH1d)2O(;@F#-M9bCk{SE0q%MUF|V7xL+2lBri-xa zPaNi<3RMe0{{gpi%))&&sZXc{9~+&dGIwiH{W8vgnmtBAt1;e-$ULUaX5o@_@5rI3 z!=xo-T{h)y&IS={fe$;E`P+?9O1cOps43cUFU^hNn6jdc8>kn))%*x|Y^ZMa*)bf1 z?4UGxwDwyW_l0RS1)FK?cYS6g^ptxeJbcv~0Ww!Wj>Q$mer31K-f3xLDCL@bZAj_C z&4~z-2y&BL2fydEY9oTY}Yn?tk-{35@hi$LbN`}IO>+rUdiG;6Xre32!sdzl$g`Y(E|g-V$Ila(qYnbC{*{wpoyF@(aG=>7?-Sf!2lu_KmO-co?a{npw5 z2?LVbFf{*P$04hC^}K(F*-#{<>iqGCXp~vC5_xl2qqMBQSfRC0qjub&q%Cnm!)p^q zkc)FQA%CX)wCGeAsVPP79&c73he*2A6ktjBz{!Y4hT{(FNTQ513h8J?pcUfreiy8% zF0zc67yLB?yr#r3>HgNl3`u_jJgq%^w;%Ak^F4!~SEJrEIp@_p`AR`$Q?IXy-cS$F_Y|EH0+L+<|vNXQ$8-@VNeb z-7{G$=&rN~PtFT}3MS(^9o;o~AKo1RoAq|g$MhYkZKGW*yr%K%_J|pF8MQ_@ldW2$ zj-yKCN>|6$;|Bw=LO~r#+I*bvDwo;!I`r1;TO)2v4Yk!&C~W~MdNUKfsQ|cyj&+U! z8a`3$MN}Y0{!i`H?Xp%jw%h8_(CYCL1hQfKg#qV=W#p2I8?DcZ?}jW59oYun@+2C-MF{$s|(c-H@&Bvjz|;RRJ)0bZ{d9*JAHLK z1&sInGCAFPr9ES^=REb!DEc9`gm24Mf`fBEM`#0j)L`R zoA->_C5D9Y%ZDf%D?koeE8{SgNOq$Ux7pbh%FuVH}D5BbwT_A0LXEO;1!wJhJ;Cz4DT|zOyv%mrO#eW za8)woxv97L1meno3|J6mzFYbwA`X+V=6#x>0Um041{-1YT|M{^L3rp4Q=MAIUpwlPV9_z|Y-IwDI9o-s z#~#DjGn7D^Z~#Hg3qq^xZ!~1j@4r10dctQT0}t%lWXD>J4>Su=Z*{KYRqo6@c{`UX zMg979ny0H!K|YIWdC$cX*F~rGiUCwhI2Onh9t~EbF6WN_)iqg|HB-Jy{S#cy70bSJ z{40fH@#0Cbci~`Vef9Y*^_61PRY>U`hkiw`{E3|hsa&J~q<7v57-J9r*Y|PPtB>Ws zHkM0(;{51;Z7eAy;frPeM{bo3g#FXux3AS9l~Bv?;s4B=A|(Yv`$v2o$@!9}Z&?1% zdxK+H0!!ieZ``G>qB+pk*is0Ym83aJ9%`Ec2s55WGNTHkM`xV6hmHYF$ zbn8OA?*BAmGMZSq0^K}%+uPn~{gBR#z_Pg7j}#?F4PO*8ktogpE}AxKGR5W^=9(P2 zyotO%dO6lO3kKUok>7{Go^0=rh!i26JdQ^8am6>f{!nLKY+a3-*_t zYK8fa9mxNijl{Kpd*Ih`vHaR?9P;0o!~pw0rzG>gPE>d$NMIuxz18A646e8?S6@64 zvOmC7t(`zV@v$W_61qsB{R&K0NApg>l$+uu@a75>+o*ayB@z>vbje@4eb(GU`G)MY z*)H#fsn~{`hC|fsh2`&6z87%wy!rI`K;)R-P4&-7k)CG4GZQ9&NC2(Ys-_;B@;OE9 zaU*|hm4z%U^JUk=Y#24pg#?r}wg(}Hc%{jMc(p`O9YPtHZATW&C{bXDE2jOf{AO0l z`d}oPhNcYiZ6Ipn-*Y1wAD6EK-^R~?obWT@^Z}6Gqb07Ha7t;1vm;~%LR|wGOFr24 zX#r#yZTUxWz-~PsOQbiAOiQWn1o7GLjRf8m6GC8ZMYh74AQERTh4)#8qi6tAnSI(xgP@i1H4VlU(XJMXCGnY)l}P*ub4jOw7i#f(WRn_> z)f&}QcoB*>Whv+&#q^OvV!sTU{i%tA8@LEclV0@{U`vH)$PMKu<2KRn1)x~N?*$;- z#5i^ge6(h3E3t}4%;Y|%W&;+6c!C}H>$(pD!xtVY6+USL4(bo?41gXEs1vaKTj>l4 z8V(3!C;e}+#}Q#XF1-yOr>kN443V5)UT~wnoSC=%D zfa#{!im4wS_ggolZdBteo{B`rM@hLfCT>qb9$dD__5 zkpwRVmaY^{%A)5%QyVT#Y!GJ@)-fI_t=}BwF9>-d*oEK#1r7g%f9nXAwk2Rvb%L-9 z{7D%y!~d-j<72%7_^}X46p~>kIo^4@yb!mLPs)Q#o!3kgKPlkRdkRUAPgr#3X&{tz%OG1cVmxLyG8YHDl|(xzph1=qO$6x@_F)wAx~~m%W4j@fGwl z^T5MAYW$XHMra3x54;i6C*|I0swQ}_sANiBk`15Ci+&FLtjl77b0Gd|pI&!3-+UY- zx?ikF<-sAz*QzzyJ+@SXgsgvX`_Cs$VIt^AkCtFOUvPFwa{lB(o^&YlJ&_vOXXD(4=AQ%Hl{N=MxJ^y?h*`_rkxD7<3mK)@_>^ycRBkIz0k zzP%0SyG{g83!?|hz%eR)(6ily$E85KzzoFK)7xYi*4>FOO2@&)1fJy%o)>g5B4`$u zvY9iN^V*_uxPbHqdBj0|j@xCyfZnN0J3~Wp82;lNG-70XfvDd=+RX6lZB(%w39P1m zpoM5a)f89)Lk|U1*OpDj_D^yvatN~Ci&WDj)iig7OonksAjgUg-6W!43*_y+IryR4 zBCJ7+t*h*H2Q9lEL)^jf?rtUp>BZa4S=)fWfyIEDklGAS=W|-yx`&>N?SYE-y<@2) z?hH$s%hZqZ#8l&;fsZAw_216LE=9-jp_U?$SH6iPUzonKf`C*(Xj!E?6d?L5# z8@$@9fTIgoHwJbiU_*ABeyxtGwZ%m$7zkeIL+N~KFX5PPVQG&{$(4>f!rZr;$3 z7Yh~FYcFm)xsOEBW^?s~5#2xt+^di8UY7bGf1V~^u%dL3HrycPfYG|s;F9tXF<=xH zyIilxss(jqJE|;m$dfCxjt;1ZTJ|q|p6OiQ8DcP!2jcClTZ(D!yp`s|;OwS$jkH{9 z@;g<-v#BsCVcW+)*)%OW9;#>Of#+@a>Ckgln?%c3a>r;%^V)H2tlhs=1tum0sGWrE zXd3l0RBNw8j8cy0GO%m=Vlwu%A{z%u8(I{8*2cM(I;y9n6{YW}g&yNIFKJ7Zx#hibGk|0f;f;Y$js_D>Xco;5h!IJkpAN$TxSfXCc3NFJNOeQ8q{`kL)m7 zAvONM!U{b?Gkh06*r_o7vwUA=h`SytKsGP%Adw}8l>9KLVeLsYMJH;HBx%jIzQ>9X zL!CcoC#;3wZO9UMzwnmbsMr4J1D6rYJA-H&eKhI{Hd<~9Cb*UwA2ZD2L^(>9YhHkn zXjTc@x8A>}vAO~$@P1B!`6$ezQpzIHm$m}h1I5N0t3CKV{0bL+d4@Z2C|`5dcnNU; zdGUsGX)IM^xOFqVIyCu?_&nGEK*yUNpL z4##!SJ;g`D0)%#CF|w__)`Z#h9BzQ;61p0keCOtA&*$@XY>MN77HPW- zA+Au2C+Eoq9iOEH@e^NMEW8sGJyNG&)?D1$R&rhId#i?UPdj4g!5!YfZ{=Qgln4(% zZ~KxLi{x^>L+9yD z!Bp7w&3FE&=V6KIgb+mhpP5y&R{nJ+Q^P;xFWQv?-p$yQ$Y#wAQ zuTeZh!a^O1dKUwA-A_SD?V67DY;Pu&J!)FF&_yI`Xs>qu^M~6XapS<`lEJLLO?Yco zqNx;m9uc0J1(k%DnUT4W{O6^$Bu$!c!OK)6TPv3>(Hf=+=$RKkNd+P~jW=Yj*gnqJ z4mWL3msOL+tQ|r?dmtSgR-7*n20nPzo-1b>%(@zxVOVm_x@y#&G1MZm;Qz)IXWzOD z^KtntW?h?P^Avw30L?IY=@uJ}bxUC^t?4daokkag{!!3GNW#k?Y&TA%JnyI67Wrd0 zt$;`u>rHMC95d3}g@Pigyjbc0N*31KHcc&{A&|N@omz;4)_|1kP-D>9a?R2p$V`gF zBl`tQU#En8PkkVnLDP(8veiwA_-2aGO5qw(!Q>pU}3 z0+Z0cwLREpJ4#@-mF_OwvnL$T$RJ@X_1-vzVtL;AXP5OZh2O>SNq9Gvqe?5N#bv~K zXZan6Ox%gL?F*eaI8>u1xvl%Kz|9l``QWc?k`clR3SMD{O0%x2LON=+k$}bnA$Zv# z#Bh|MN%n)EKy8j$Dw}yR*+w`obDY!piAIfhbV#mlm5*9gfND3{I7+%Z6A~%}_OLhf zM9yNK7|MZ6dG&|`2~VIM*ak#zze9M8q>%4l48kHy($K}*5KI`53Ou`_$=cY)Ase)H z!zF+|*gR4tn}w|fSO$g-HY+@Zl4z=sI_{nPYoC(KjA(q-YRl zSeK2|#Q|HkRLI@*xn>m2g2{D?P!5N1mSpeg=c50Px6$(}c5=-fhhkHn>)FFS!ViSL=N zlzvaK;PfV!cpx>b26Ek|d!1p?`_6d(KM}EIzu3aDWf^r+)7o?;iBe|jI`Zq+k`0bU z47{Zn=@FiwbXTC@Ch?(YiFjL@Qooxc0_V9kKd6(cdwtMx@+lQEGl0{#Rx;X7!tG)xv zHwTSzsR#3V)HHG2{xDkv1uEXS*&(TDvBJwYgO~0Y`N$R#LqaWBi}pDYu8aW}Hqx;J zo*wzg`3n4Rhq`t77=U+)G?00q;YgCeJ$cinlHLJ2E#0OLNAmi;=t8}}SPeIl#p<}+ zplMgWQH-H&S!g|oe4+<68bpjg6r7+r++?uO;yS+`4Nrwh)!1Q8E;0}OGpAI)elT-0 zLE4eWjC51KpXa_Ui#~bsL{tphMJx$h*_Y?3kqCkp4G=Cq$~mr@#V0k1UtGL4^=$j@ zgh_CKO+eu~#KQh0g&ojcgFt~21<4~W*;fXS$hpS$M^i4TYX1HB;QJMx%Gs|Rk+b`{oH!{7w z6x>&|Ew{wR{;{UAA0c8h>3!-jhfP)K>a$XO2wQFPq8=K}3=zi z)2bk>gUX5(nVKtX-KbWmliWacifllC$-x(21bl6dXv|b6DUM$>AV600)N zF~=_yHo12eD~1qq(VEu259W&M!v5^F2Q0)4W9;QWr^i-X5Mp zA0+v}vD(IFF=-R(-V!sh4}?A_kw=c5bHI{8eQy(xcuxkwkq%-==oH>q{XF zUYGIq`4DTWiC}Wh9e21Sj6x`&eaya9N{1py_iP4lm9?HWlM89k^&NK2(e4_*1~^r07(1z>kSVQZoLN-#0h>0@#?4W^nNhtV zlzn@%hB8pVnkwl^#5<5-_=0=`x<|3y$DkYq;cTI%OE@*b;o!4BC<3YlBv0vbhj5wnb;HJ(*`deIV6j4C2^;6u(A6Q)REK6D8iIQ`jDZygvD;6Tw(0bXpqV%y_CqQWS?PlWwkW{2dSgvZlXt_!KXTVNIZvk+yq4t zX2B*@K{`)csPkJK^SNA=S_{rvB2DAyz6%6J8f@CYS#TpWsg;;IB~aX_jvH%V7zASQ zr&E@txd@u=%|*Tzr*+BHq*xp7QJF6@U>n=E*&}ufPe#flfgD>^t{n)DF?NHxC%zQh zjTt4=(a6vYh{}}V&+y#1H_ZF-C4(xQM=kcnOZ`blBx5p{;rbh`OVnHMHl95W=fk|m z>X|rTl+F<{AHi&s>jSR6lai`_-V*gP-!IUTRer@o=pRtqyXK*I)@)Jy*6=i?<8(w-M-CS zU+|Nuu{k+fp2U9WxM|!sO-}5Gq^1%aVB2NClI#7+&yB}ocRu%Vwr7=tfPTsOneM3} z;-X@CtnrgitzUQZGT>ApqUEITq1HYVP5VqXjVa7$V`RKAEXCQem%k{n8uE)Yr08Dz zN|W}4+1=Y%lQAaPktP|5Nj&8-Udv2CZ>}8P?h+W3OGuGq;Y6_{L>&;jUY-f>0mQsh za-g6i&L{%H>Z@IDCunUloEnez6hsw?vjl6;sfwaNQwD8D!EhY1K&uAL#}Ojx2eI6k zcm;_wn}Q;>W@o}AgI|U7arN%nid?lu6HR6hAGR@8nfPQH7%fRd1LO zkxsGVHzr{8ejk7~Xey_ggd36pcUN2~Ulcm_(F-6g82C<=At%%>o+#1KGGQ`!rbtW>MWRKYS1S7SDerru8eu_ zmSdc=@0?R1g-Dfs7mZ-0Cy8WX+`Y7UIrymC=ao{vHXOPL?SS9K>Lpqd=APuhFU5UV z%vp}%m{9{xsKh|(xU^nqoLFe4BNie@sxaEtZ+)K)jV#NZ0`xv5&Lg>jKU^$5d{pEZ zJ48)Tp(iz&*ksN#8Ets#OamD8a(0$%3k5zweL3d{$-+%I{q+ssP0L8s_36QGbjAjm z^9!lgo6zm4$-P=zN0Y?2*$?r;tTTP$gj(4_2A|qXbUanZsJT|q9{UDmb7@vX&eTT` zTTk8Y_+Dlph=HJu1u#L$5j#LKQ&6hbH|)K04TZ8IY*5ZG{c$EKgEDRIyA-a#h<%KK zInBf3$dNHeE!Kmw5RVJa^^*GJNJQ*}hQ=?D3Djtv1GjdV>k)e@uHuIxLpQCgnpkMYmCBr9aBc{ zjkYSl>jZbHu(NeF0p=Cf8pvbZQ2q4bwV`vOugnHf<4x65XK9l2>sU>A)$R;{na z5Z%UdK7wh~PUn<;I?KE%Eo<%el}c{VYCxyEj6Ct{a&n0(kq&paBkj4h=zAH50a^q` zx1V}qxPfD3AusJEK`R}`SP{oXO`-WE5Vp>ivY9fAcd)c#^!H0u0q=V?-(S&?M_>NteSaYf#(%7W zh$8t-?Z4#y|Dso+vSz}9YrkbyAp3zmvWtdHOId!%7XXT3&~YJSX?mdx6Lf5SsAOD|8qtMu=+hVBaB%cA*VI zE5coOb)he{v|M}5PYh_EBjv@l;woFd@BCRMByzdEfOx@NavWT@mf;CHtZpL0UJzcs z1ily~&lOQ7x5}y17?`U#cB@hh+X~4vWMOHJ*$U-*z08;GhS!$4#dVrgBvkWwLV2*4 z*a~$UR6Lmp@enotOda@cs-Iy76GD3J=1AYK8?ELMedk58I?P^Z3-7h|;!0e-aIy&T z62{TIo72l~Pz;aCkZ!T?@S;?3Do!HqMg{8J!#a=kEc$H-_+Jbdr?!DubAz5 zQJ1>vNL;Ba=>VQwCyGhGtRS@N;$7boHv=6{{tILL@hmE86TCe&A)(J!fIP#Vaygks6R=4hQ+jzpI57V2t@7(va z>+XBD5IM_gyhz6OsWg}5>X%;+GpwQ0^1opaBp$j@oas&71*MbhxQ+0zy-ZDZ12d_s zNW3d)H)ajKa*l0}D-rwcEW9h8K;B*c)t10U_fUA2;8d|<9ET#~Ji_(e`Te?pf}PQ* z2SQwf3n^*BW9F$y#_$Lap48#~{lXaZI(AnlM$;Zpo*K|rzPu-3^LoUfSS-Tlq1t$A z4V}rF<|Y}JS}xZXXK*x>uTHsF{;q+Jae zlDhG$h#FU~nIn;%lZuBUsqK7dO+7fz!ok=ACBjHUT1m-bw(N7s5Oa;9U|*z6lBv2N zW5|%4pKUHx7E~X7DYa}ju;&y&3hF#1A5Woh(N5XS97k-skE`Bhmg+%+Wk?i(%E&i* zx+wm3?h$ExB6sDr>e7U@wCOBA^1{~$sXVE<;OPHdd8(FryGgk+xRkV_h(#kA-9#}Z zn`Qpu3i28hKozaBkmEc&K*_49LVLiipdAYRosWN|#=pu4c|e>N$PAmx*(8>#(~6U~ zf|{6!5%4;iku-=%|U5GEpI^Q_{qQ!TgKp4(y&Uz6KR~ zFJ;JCSi}nl#Uqry_&^bHT7rgg98Y5E#d@QS5WUhA>;#jQQW_negI}RyNTD2Y{yRp5 zYFX*rbORTnd|dk`ATRYi*M5L7|DeB6%^tpsf@5cR>VND^v3*fFUlohY}6M>xwmSy;)d3UOlT7svu*K4*~u&X2-U<#-Tu zz%Ra16WavJnWS!woo6P*l*)-Ofg_YkR>(kE{9&;{16x=mRoaKzoEa}#pwXgCu>=Nd zG;U}Qe_=Y4?-7L2@962cd=%jUktoiHJ3)(TiL>+rASh@^!+9YvDB{>%ndm#!7@g+x z(P2SX3tjA2(_<5yS`MJnahDya%rB1;D0_3@ zMb0Cgz#v5W(S`T5AhuY~s9Z^MU3Xt43@qs`&WyBPQ6llQ-mYfI-1t>K^L?tg6b02` zK1Jde!r$u2D_j#((BZB;FWE${M_}Gh)_Tjmxul@R-%qn6`R6tkS9~<{Vid zdn0rqLDOK>{}>JPcqquYX3!^iL1khg1*k_5HSQ_q!Ud#ZsMlwe$C)!W@+liDspp_Y zZ#DK4U*wKXQRK?4n&+CRZllLjdGNq4{*am_-A$sUUm#ZKB^;)^z~o1)R_b`a?3%;E zFT0iY)!YOSRQ&#ksaSzWVQU_508=F}MBzmjH_9CZpNb7zQhHskJOypNaNyxc3*ZiWy!R#naTF+?HJn-$uFLl zVuJ}1sCq8Kcm{uB)Pp320|qGCsq06dYkU1G5K=g^ig?Itx7g7GiXtyjJE@g2%Tlf0r$H_Sy3ekNgfZfY0N!U^^_LJbus1CPx{h z*z(5z+6un3TGBY}JhVj3LG!pM_^fCnTI)+*uLE&>k%OWj&QRTDr(R&IH*kY!(!9*lmW z&C2x)^mma|d^mRJ%TG3bJE-2KooPQ2pu3*cbh3T^M*@)RFb) zc(LgFxM8|#92rAzP+X^0PqT_mKQ&E*L6OgZ!+uAQuIgC-Y@uS$%eGS=2YZ1^NW`k# z7Mn~uL&@XMl7Z8x;4+k&>gEBhOmIyl^V1H3w$S7aK=HyKI9Ln*jM$+8drr>gcP84W+)i1 z9L-R~29KLeE(-o87uRMEs-(bkADFb+u`Z$;Ix`qwi95k}TBc5lTgm#RRz+#zvHJSx z#vVjJz*)2`+DvwgFmCkVXX6Nb87aN&l^gI-VlY>!wfA!KRxi%U#&JO4=EeUUNjk%F z)d9TByy%=1`Tx8@itA+M4TIK%2HVNNntE8}c7{fINBSfmNl0i-hi}fPM);x}-i$pM z@@18+`>e7{M8jl1|Eoe}xBC}-=x)9JS+|crfe&7rbYfm;3fAJClfTNahhtqMrv#vn zuGuCt2jv2{Ho2uKG9K>v#*Uz|XEp|YJz&$bIn~TqBlSKN&;wZhOVV30Cvq!_JV7_w zytgKkee+1~5#ME!%^0z#7L@#)1iy^Ya91U{0Z%Jv5OEZbJiO&Kttc#4dbAyZZ+EZP z0(iZzIG4UPYhn-^tW1rU;GZll6<)L#+&xD+yKo^CxTLRty?>_r(HH1#&1IcIk?*|) z`thWc`~P_Q>bNSN?{7jRrIhaOM!KZCq@)`}LAqfn0qO4U?nXKViA#6KrQ=?@9{hY? zzvu6}cV}mI&Uwe|oXITOb5>y#{hC7U_!xz`T+A4!Y&GZr*HY@L{j7mC_-~M&Jzt!n z$;wr4bA)_~l5&@+Ta&;sk;(p`rCNvQITp)Tk2b?E#homv!;FS!!XG>Nn zWBBA%sbE$Zx#dX&$RfFX^Vv=7)V4jH8oE#gK&$7VAJOet3SP17%C5K{H>@bvV6e99h|bBOA{^$3=+TF7g1X!42H#-!28+)Fk!JUCBN1!30&0#n+9ziP_2g3w|| zm$$(PvJP}{Ma@OolR-;e(N?Taulr-whA@G`!0Qbc?qO`|sUt<~U!I?;IUk2o5;_lo z7@g$9u=pOUyKT*xP<0Ksk0>eQN%v9%c}$Vm^}so2&kut$-fbUxmT zbWDkYsEQ4#Y^&C0a^B{pmtmJdu%k=vgWvF}-ir%maB#VH$e8BkJmtJmKLJe^;O7Pi zT=t9dM-5(>ubqa*6^JM}5AX|!&;7mwMCqHQa@4h3RvaoAwU4qUl=MslS}r3$(_ zS#mhk@Dq30i|s#WZilhllG}+~+)&SDtr^GPQW(-c+E^XF6b$9AQ9XU}AkJ$yNTUv~ zgmalIbf!uw$X<)(&vCiam@nXk{z)iiqrFaM;8a~5o+0Iv66TXqhiG!}OvR45xq3t$a7XEEoou&h2Sh#%I@Dd{^GH?4!V z#pCd&`0rO$}FGP)*oW; zV--!I9lkB1q@^}wh^xh+H6g-)=TdQx+m+DA%}4SLgmqcFXu334p;f!p8SCCDrSM|+ z${S_Zx~T(YMl!0;zeougHfB--A0LaeR#0W@jb_J(Bi$vmskHPgp>?Ayx-k8S_dZu*5OS0HmqccOaUkzmbWk$C z*@XR6ibS5!$W0Lrr&KBy`#e6Fq*QA@dmW(2Je&`wE$0#z0a+EAJl$VhW=?IWe|EVQ zGg$wHVo_aLR1SucSg1h#75I`3GBLQ}}!(x~l6Y+dS(xRL{)5KfJNt#fomMgJP|}mwjCs zS!LPCJo9McIQkg+BaC2a8_B(xGrBvYAn;i-l~f+b$K6E`|?>sKGkk_K)UVh!2hf}zsaZA1>@_%4LM zqD3R*_r#xIGjT|DwmrgUUu}fA!_?v92xA@_PJWtfgh)KWbi>sEDFFj7>hc13iiCf6k>~e4 zRqzKBT19|pZ%doP@K4ZT>cdCr2_4Z=JKtm|L?xnQ^iF)H6Ry#YM>S1!jPGV7CZEyL z>djaqw1E7t5Dd4@FEzJ^g!4ny(zvCA@+clJeEP(4vXzj-0xrU;1%~2d9Ho}BbiR)R z?KCGADTG@%%T&;xGJed3 z`zG_FB7IU6;dJfQOr{}#^NqQ7tWJlnzg)ezJj%=hriWyNBx&DfT0$4 zH|29-e}4MtsAZmbAP_KYFQ-myej1idB@i0+oGOr$C{%cSxN=NeBghx@?I$?0xI1m3a`FA-NQ#bd zyyT>o=a_^&Q9qr(7A@V~u;gbe5MxM7U5i}n11fDdZH7pf25^0Po#cGe~2al5$lDou_ZN(p%ke#pdewMebE*S8=4R~d4 zz>{)A?#H*4pB9_?f*kovGX!RxwbbP&9H7>P4HmcQwU&uh7iK&AG<2?V=2uK0i>X3T zePk7x%_!My) zbo!!@4{-iWzfSoerFIU@u|3wfZp-|11(d5ymAUYAsPN;mY8~8#)iFgHkeb$IMu@$x zF4nnU-sL$3otTe~@Au+p4V3Y3Gw8ltTTv1-}nno5sxwj9Z~+Yn4`Jn~x^saq5& zq>+4er`C+3m|IuSe%VF+_JNcmuZ$&vx1QMw^{ev~d_^J=_ZKryJ%VH-$&`lRT|jm; zE1Q3!E>hnxLLrm=hxjO=dcJ1?u{6(1CZ4m)qnhqz;r`jxfb>kt(mqSdfyNbG@}HN{ zwMN#)%I^o-4j17wm0=9H)xo(3C6J;o zhnZB6$H}OtFyH!^52UwO;N8Ax_a@a;@Xp!!*a$&*HEJct&y@qeWR0nS`v>j8$;NX0xxV7rvGyq8He)+i80geWF=hvLOibDg?Ye^dE0%{USNv6Bgx4^FYpRGEStiuG)(MZ+ zE0(*|t`Q>S6Si+n|D-p`$SuhSB@b9naec#hVTA6eNT4zJvT2TDtkT-$UbRc;!w!4$ zTJ$*K@4K>oyzP27*vyAxiN5dk(MRbnnL$m_38op|a+3|6+b+`rWo{p6R|eM)_ZkVI zSmC4XMP9^1!cnWODa~M=?vxxWN z9w>Jcyp+SDa7LBDqLPsp5HTxMur{`%IZa zlA9XU?nvZa)i2)SK~oB}e2^`vz8QvK$PsPIRVEkM4;hKT=85KQ!*To3JOV+z=U}KC zcCOGLANR|!!{XJwjwwax+rHe7E%h_X)BRCzKYj@QRUio;TCtt$NeolxMbY$`v(+wq z#$oCO;qWXsXMivdvmh`o<~Iz@4+erw_`GocOEJpi#3*aZ!g*`prptBulhG2%0PaqV zB-bQW;7klCQ`cWZrom^C6%Ffm($J`(5NFxzvEM6|<2|%CuNn z+Q7T)1A=qc56OD|ykrR8hNSYF$W_aR1_(Yaj<7jmP3|325mz`FsqzL~|wHNCU` zszP_0%SXw1JdNfR*UyTyaD{4&;VAalC*XIAzehv1G0>@A`0;t3s$wu0eucP_EcX$r zgA)$Tt5?q=PF+G7JzTPX%gK7L=2fcUPa|4|oCVdr=_HH-2>GVnwcC%1`eIiSNoIAo zKg^0m2W}$mRTd=k#-GIy^P+qwTffKFy41GB&*Gr&3v3@umdnJ&>S%bd0{q=oAD| z<3v%a&x2FQ6D0aF?p5D5qsg7M*SY#nU3FWmhJ{Z*=)Mz5?g%jXPKgm6PvalJker04 zvS>F9U~s(e3Lrn8og*M_3muE0RH|a9eVTaV^JZ`!!0^T*f%u!ivDr=+IJkc4vggD~ zG$s5Q0pPeana?$ri>eL)qPwYA6TsuTsv(0m(vp-c=WjZ1_~r$fN@<4FtUFjG#|L3= z@y!5*s;A*9^ZsD1=G3nNdVeIQdeTow`A-2Lrjx}Uo)^vjiB_lhyOwvAbDN9HdrFI} z7>d3RAD35EZu0=5!jq6cvq(vCB7WiqMGN=7C&Y|dmjUmy6d71V?ng4eFsxa7B%P|Q zz!mo4zR8P6AB?^QmrNQx5v+dyf)wq7lwHCfyB(bs`nzcO^TpYgf2V7%U#K+Y7HLBv zfYhagj`1M}9`XS5aw^7Qz1?!%0<0YMQ5&cTLNDk;5qy<)!~N_alH(6`gkxP7M7w2u z_ck`Etws+z3)Mwi(u!{NPfLgmRvS+o+<#8DeLIrbN%hx@XISksTvN7vj!n;Tb4Br- zz39`aoL1GhxBZjS1#JAZx~y#ZINl@xVZt3vUjM}snA7sD| za;SD#EihGJ)Z#zSFnF;Ywf3#xaEmxJ)EJPX@e{vxTZV{cOMl*epYh5!WW)^MgUyiW zZgqU-n|T_0DZo@xsRL1fR`yJ4u?JF>q1v}H`0L+tNgka^+h-K;`^@uAb)Fk(3Et_oTr)C$Xv>? zES|4rJK2X&uPDPo9KTRLqtdZ^5^Apb{LKb$_V9D3b;oVL1205&+-dHZqx zZ@C^i&&!X$t5z?MLod&fcMtj#dq~JrF3qCDt=rBP?o@XPd1vNfRTE|u_{V?RvH}CXm=})K#xrnZja%FpvrQe6Mx-ZvB^60r2>4C?IKgZ;B zJE6{%N;&Od*yr5kGl^^;Aeo4;EScD+oVUTDtV!^gGIVnKAYR~5Jz9}Bb=A1n*(JP* zQrEitmnbi~!|?`UVW`By&H^h#EG)EHzHZigyc+#fftgC_8QfJK<~DKD#riiH`#Dwk zxGwp}mL>|K^EJ5sS^pF#k1Rw?t(C}K3mn{ZvV3H2-M#dE9DX7MSUMg?w<^HRZWocv zVAUtB3&jD`dM{rYQGJpCR*pBlbLM~cT~ta6>P@Y{Tac9&c@$V%+*+AcK>_e1`A7wRr8NU#e zX?W~4TTrt8sES%+YzQ)RG^-W2|Ppf;2V zgxB-l6Q5I6$03GR`Lurbw?w}(+MxK?=A{v3@_bU-_cz)Mz+t*=t-J%(MDQlf)-4h` zBlFvuq~l+aEInXrEKU-BwPUQXGKcvojLB)L5jK7<8KGd45cqMFO5N0I0TwxN-F=;w z@!Y{AfAj=N`zZ`*HVxT3QIognC*s|GlWcxa3w=9PDe7>d1b_xdEtH^HpNvmuBY$W` zfEFluXM-XPM1N6@e=XGN2sa^hcQmRp5HW7@i6Y;OQs=6Tq1o8n`zX#3HYvK})F53| z1xB3`&PpD9MvZ|X$58ovL;-xgv`t(kxb1>c?FNWPynTrmhGa1D)Dr%*z@Ve^qM7!> zou>S@s+H{BT$5^%J61@a?tS<h*h|m>V=Wt?cw`Z7DG##gpdyrQc4D%6xw;U2R-}-y=s%Nwsu+ zuvVJ$6+qg19QtnbV$D{3H9!Jn#wJEsz?-kp5PkC4lQKbffjZnFe*3UZ?)On`nE7$n ziH#n8p{5~?gbyx4o9Lrfjex0laHXT2Y0+prm!Yh*&w3-Ogjjc97aRdeXPT_(GbVPYKf_ z%PgeP0Q{!X4)teVOLBzzs9foTpsvjWD+a?D8N{H)LyXtq7{<(WTV;9Z(bU;LQrGmd z^rv9oBL^4E_-+IIn568;D*OuL;k+w}&hhVZA7ar^81Ij#C;TQ*R-(&q^Pf+58OYNrzsmbDf|#B|sEZW8WRsI+twkpYcDBDp+! z?4*vP`?>FfP7kqn*U{Ms@t{^PRCjUW$^bdW(IRESCHG)22kBekD_pDE&UJ3$h4G)#D-XLGSVhE;|k~+?aBDpG? zk{X=Ib*)s;L^!@+AmVX?%o94A)Ny6>0ZgA4B}C=^#PI0?OV<}Ma(ZDnjY`KU~%>b8oAGn@N%9#Tn=CT06V>Wzg(9=LM)h(uB^k zwb{ItcLj^=yU>hMW}_1*)cRM*klP?AdeNb#piE-5aT-S%<eFF?KJZtQA`a8tkC^p98iNh+*zd zf$l76+jpbRJIx(m!+&^j9X5d=2t%#hF{Ru>bf0m|@~5+<564KCCbL)$uV!4gD(oB_ zLeD|7QVCIYzplM!@%>6M^KC=*muUHu#ASW`$D<%*x~PPs%tJF1ghgx8Xb6=ePS;Hae0 z3NT+k9>W{6-&7xWc*U2)_dkL}LGbnYmKGaCR&+xlii%nCUOttgiIYX)+Q09KKbN$z zTTQC{p|#tY!w{~^h%_(m1^9hU!^p-tWVdSsQx8dbTUFesL|4bUbmrdqR1rP3+Rf~@ z^{mNcA4&VND&+3Ujbaug-p#yld<r!S7KP)ej;ZU+5o0beGrdXSjE)sO7%tw+DM&bfo=N57sNDW887^ z5yIvuMwk1T77ssl4>X4}+_^qKI(a6b%u!#mhSA|Ak5K&X&7YFYq$*i=V{b3={i>Gedu{v!KKSQri9{=~oUhay$1L?lAdIBSkC45-|OSqnTgk<+KIMXu1GSSQ;Z_ zPx$Y{f~jqrPWNXNwmf;*=C|^3f*GAu7W~KImY7$43q$?P;%gM#|73M7q-mZ@PcT)w)(|!ndGndUc`Nt z5lJRV##X&I5mYq1;9HvjwC20fCLfsy5QHIoNL=?(_i0DmGIe{?>2t)ing5;iE{EP9x%?s6^ zPSV6@KKvf%xY_hELT$Li-2oLU$Le8OwyHJAwEElkOTbu87ChQ7mgztZIiYj-(Z&z3 z7<+=M#Rt6aYNk=qvxSZS3J6ud?!O>KzD4>hTmHlnVu7rDckR_Ak+i#yW3P-utOj?) zkF!wEh-6Tf^T~@8HA~FcT~C6M8K#60YWJ+IBPX{+Qp{58Ce0i8Q}VL(NpfX3r3D3g zT3aOUbuMQI#cR z$kg>62<{fEcp56V5XC;|?L3X2c9m~@iLmJ16GN5v{~4{-_) znM3(XnGI!UMYHBA`v6g$fwX5@b02j=tA+cwi}z7vPD3}gr0ksac2r(JK5+M{^e;!F zy0_mR=5DuNTu^SeW!FCsi;vvrV-%lh(=NBcjhT{j|6Vz*hQAChZ z{>|`=PZ|vTf#xBQAaLZn^uIXLxEom%dxMsC3$4WY7MA;ZOrMP2>c11NrWdwSBZmXp zIuf#YMFg2@AQsd=y@Kb741c)$TzUAI;N#<110KD7Smy0-Y0k{srr(PB~>9O^;y(2mK>IKyY(4I;&WQjMcyAAFrvasoW1Md zg?3&-MM6_iCpY=STYXrOQknBl`cYh5d;^FuFTo}pSEdLNZ89lF={O`t+N{ht_TiK*t~0=(48%oG6OFGPRB zh*B*so(o!{J~9(elDrU8rB;V@ho;2q>fATUWFoV!oH^=vZbR=W#UQXPLZ|OdAyu7i z=*<~R{7!-w&F33|XUA~F=|C=#Cv3EizEKMGpV2)`O?hRC#Z4%QLISm%nK|q!fv??d z>*l_orfhUWAuiOSdv+31HU&7XZ?fu196EydcnO^)Cf*6J>v|fNIGWUwE&4r=b227p z(8V*-Sj8OI%rN5pibZI#0=MSw2WJuaD=@4`S_PEn?rRqA=bFi^5kemy>w0=|Zau(! zUr^m1+!!9)zQTH|E8zdHWuo1Ey8RHD;%V&UZBHyY4AW>XI!C}g0*@KxJOyGEH;|yL zPJ**iCkadXQ3JOpJNKYkdKzyGGq@-@wJ04an&<{M)4wV>UppaRFaklX;jK(p#f!Wh z3N2mSzX#Gz&tfCU1x1n|7Bvi`Cm~IyprTjEmnL}L*YsaHMMg`dScSI!`30?aa?BT6 z_nx^*?Y6P@0HnV1JADEFWGmj)zN*JDkjSs_|HH`n`)n;TqG=$;Nn(8T(>SW_z7$j_ zEcKfVPrY!?e&oG_SuBh&o`qQA45%B-o~8n-_xe8WTXkLs1OH9|kV@*bPGdLk_-k$u zIm1GCygSpz3|}8yqn%K*?EB&V+X84J8XaRb5$WMb9ssdL6Uu07({+&&baql30aQ&< zU2M-e13dr=$*s9DfE#RW;5+Pku1N9C{wpl&>~A(u+qr4fQ2J$vy(gs zu6W&<4C1_v7=NQJF~cN3lX~Lh_HOk4&*H$Q`~@g-x`d-bJ0$fbDEMms@v+S>8$h#ADN3MQCY83^mP6iCpBz^-(~_1&OO5y_C$t)oi=7wp=IjMmWbno zCI*CLqO;oMU|RoJlY`(w?dZY@loM=D#68|lQ)|30@=|1zAr|N-dG?q5*92j9mv{tO zhMDKA%YUYm;dCUX%jKs}x*>Bp2d{Nk$}yO}0)}vB1laJ080vk~?^Jk&I@E<$qYQXa zNY>r9fA$zyUnfq7GSU>s9@~?uthaC&z*%735)?gq4nk@65G7_t6=z0&sIII38RbO! z7V4?_s|}CuJBio5$#!p$953;zJv2K!e<*8Ie=yI2-jfF?5f@Nv6kz6(&_?>==NX(s z1T=<4H3h{|B|uMAGtPE2nnHT!0cFZ@twvLapHKej$%n=r(D88CVCy7(FMk|Xkd zp+IL%`tmtmFb;PBh|MNZr3r|t5rgZiZP`eyvWS7b<1V&$C!Z+{2& zW%3F2>8O8s?x|+sX~x4}4}OZTlKAOAyjNmUh`Df!HKLWie z@W;zdL%0tkA&YPFQ2yLe2{PB0|Argh&#`K0-Q0T}6Vr5i!H%6|f33d1b#X)Zcg#>C%Df8WXWqQpiTY- z%{w@kDA*4Fepzo~-V*#xZ$e>Y5=i#f?wc$hndGm}H+~oF>*zgt5%ecF(ywt1zaxbs!l*%|pv~uxR&kkTXX54h6%5G=b z|9u}0Q&8R1Zh{7Ff_=AR)gNDy3TFYP6US6)pn_6vm z^rzQvv=uL=|C}xquN$d0E(TDf&ec+LVGBFdk!ggnESG_FMCxc47X=>H@p2|%n0b(6 zaw6=`m1zlh3vn&tTCxFDH&M1#N!(#lraGcN#psIU`ewG5;2`Ho0c#)XhP5$uE9CrO zYq>&eHhc>gq*B*Jih^47R^ypp1v0mO;F=j&Fjm3u2%O3GQYIK-EwN(p6jC`hx*y|p zV`wfg0x%?gq*yr*jM5&PcRq+u*Yt1uftj8rFsLDNEt;};Deddvf#EhUB&QBP!Jg- zVg|^M*Ew1UJA;lw+*akQmi1D+*WY}rEwu~KWR*BI4j>J*|F~h36C#&WC5`9tvlxr1 z-RX}rhN~#QyGKILWN658tX-tUP$sc}x=G`#zO5@5>>)FUTBjU*yUI|W=v)6tif}Ues!5qS`SKM18 z1FQ3OL9`J_6b(DSJ1pe#S!^3e2Vy))UXSIVi#I%g&kqFkfaCGgk|Gj7f(K0G!dkL; zaq4SSW4pFI=JP)aibLYE#^2i%^4x{|NoumW3S~!)AfoEa7e(G-C+80`!3r~}E%roG zF&ii~yW3+bRD8$?vdkB29<`{NIf~h*2KWdJlBDqo*}09a6-{MPi2=O1*)0?aDy@Lz zgagvzD7pivbvy^Mjeqwp>LaEwtK;~eF2trmSusO7sfyVOW^f7V#vdWTI07F-4?Yh+ zgT8mgkG6N|Hg+!fhjBYF__wdU`a7pm5eviYyJy#S`A1e!adV%xLFbKxLwN;Z;2z1z z%xX`%7!xjp$~uJV@%N!alimqsEjyp7#ocW>O2Cc;g@@lgy_iqnoPWe_veN_B&P_)p zZu;Sx)-LA`ylew{KLs_W@V$hrz?8%=>m`bKbiyQZY+B3-@)LOA@gtbn8C_tVO@KE7 z%YWD$m`h??f@Wsc<9^$1NVZtQj2Rd3Q?yZzHSzt3c(pdw;{}{Ej-< zmwMZIT<~8!s*4r(;xjfOLbvPdBfkC@jv}we>@=ZKBkx9__7xZ?0ulx);DRWMSsrb8 z-RgOZT#U@1Q|GB-A7f{B0el~938S-EPETGRgu)^DgC!B9?WBY(5|XjcVglqOF`e2m zRoL>(p#F38h_!Q%6;?d)GK=A$Q|_sJFBs?7=7dqi$gJWN=o}EThQju>kiRtzL5Q!% z7o^?oj__``4X~~NQ6u0tVh;QyEZ795tvWuO7xa@R0h!hD9fQE#-{LQG>hwz)n_9Lm zwFUY|S9h#e^=LY5y7EObZ$BLg7F;#S!Wv?v&aWmC^3+Gymnz8QA~eb|clyDPF(2pDiA8Ng6J z2j9Ytk|2?J4JGftJbNeHu1Oj8oYvO0$EqKkEDHO$q;; zqO`bp(!`L0;4|RIVfW+lJ&U6o&J@}k0{&9bK(>HW`5RdkQDKpEkdkh(8zIa)5yb4k ziN1ZOblI;JAbB?!XFVCL=5jQuD^6p{>V&CcUt?${hi5SitSVwjpOg{Y6)QHXM)ZeS zf>^~Hc96=N<%h4Ve$YoH5Hd|aV>vg8?W*+;n->B~BrHi5VGVXmoE6BShtZnRRTEnp zZ3FOOA1rMewye1urG4b$0Z2sqI0YPFeQLqw`x0cr@hg|xDr2m=t(1v4T-GGhhOy!p z_Q>joVJ#jJsLjrO=%(3-O=K6_^$ONbsy{R7HNsvc}uj zaC4N3`Ye;Z1v};zy2lgBR^TH;jT=wG%bcS_mohREHrSPkh$O)D9|f@|UK zVc@A6QiPOSSvyaTo=!*{4Wzkvd*x3Lxszy-5-?P4aWNudPi};8EO*I&?Fq$>1A)}9 zsYH4IHI)v&-Q-&J9~);$#@Me*S@>Va<_Uuok4WEVOfw7L?&9xx z35j7qNcJ8qQ%+Ab%HNNrY^b|hYb+0!GV>wqJwYSEC+`nD#i! zsWgVjE@q&Vx??Q+xc&we@HX@Sa9QvZ+kKFkbRsIHD$=BMYFj4|QAo30{L!R33z?{q zENk26KNh?2nfVB~n8LU(-W8#1Qbs-CSWW?uF8frUXm*GDr)bM=XJY|&pP7_X7m!?U z!iF^cT8}iH3%NtP{xrmfpdr>!zEJzQ`_R~>##6}kp6^Ie@GG;iu(}RVJZBH;Q+;_j zP@v%XHXFLf_KHiM{Xur`M_Q-@oKuHatX)rCTRBT?2-tJA@4?eTFak?mFr)sv5-PDe z3>Dp%I<*-#U%hs6CZeqB>y^S3+VQDHt5xmv&K}*hyL?kk-1seVF*pG^Sw7jFfCoBtikxBXqZjeB0RP+in*EdMh(~FeY`HCO0|03?V1wUeF7dOBBCSjR$BYw78sOWxHR3&zyqn7cl z+_T`fO0*?Gu!v01mi@|R%Ng8cBZasc6YrK&^B0})lL?}Fn>49TIBYSJiZb_>0C8k<+=wDAO&|g!0a@3b<31w$D z&VyKy|8X+}ASb%sNKwWEA%AEDoMydBl}O{0IZlsQ06ztLVD{6w+Mo43)yL$pVMp^h z0qGykBu?!k`08x0OatPNy!sDy{J>()|$g;%dMr9bIJl7g-njWO*bY^2fqGgh7`wc*<9Yg8vIZ!2hi_VNOPFmqI>F5h+U zKMisusflW0>Ff2xi$o|N8ab3ypUU69|FfCyHY|hq%4lRp!G=r*bOB5~g#e?vK@p3V zr`emPWliZA&(?AIY;?r~Wh&t|M9GjubOE+%P|bmNftDxUD~QR2?MEP$?OwZ!WM-0N zqA!l_9Y`dQD=c42TBvBi-U7q0ad{gA5m=M0O-ssVwVW3+t7twr6oJNdtbhNhu?F|K6?{3 z@ug>9|1*<|V0Qf(<#1mWV?c{Vx3}T{A2^Q>?jqv}$;w+OLeU>4O1Hvpx*|oI5@c@!nnk`rOYtNqp&)M%vULoIS%Qj)*n0js|AQx%+thiBLwe|DZ0r zpS{OA+*!*enBu9NaOT^WPGCi)e}5MB&f=qKM$6b#D(`I~Le*ZM`F$ zUD(G*?)h#LpX{#Z-^ zWmgo3D;6|p?A{#9r740&EL4m;+?ONS1Imf#mj6Nr*@a%Qk^ai@wQBYEBJH??*iVxJ zx&`oy+ZWSAsTWgnCjQ5UGNXtXL!w=jT%dofRQ-l6aZu9oPwzqQJj&^dCsV{w^ot=#VlbaVbk>uYqNzwYx&Ch%NHO zAhCIOcP*27PEt8pyS;sgJSsQ%Yv`p6? z?3n+a7^GMRvSyLjq2xpe(5AU*=$v~Lcq?l@KbM}|JqnG9?hFx4wwg5f{;clu{yqBQ z>|g-i-755RKo`LHHGVV6Cn_M!Cv<9@IT|ES5u~064$87vbiPnYDxChl#%bH&@Imhh z6Hk+LF0Arqx?_UZm~K#A+Gh$Wbe*8TSsFvtd-%gytMz8{i+jl#P}FIBuaj!{z6yG+ zn4LL6!)XEog}HZt@!9W@jv%rvj7xvVjl|B>l5N7~2$GB?@PQ*dCp4orzDSphkZG5ad60biT>c%31shA8Fcft<0WcPC5LO zLMYs1R>)uRz9ImrGV*%~IyO2O%a7I7Zy06&4Nr%-Q!J7KU;N;n_1e@*L~<1e}t{Y0#C}Li@Z|@_J0)!#t`}n2&n!} z{wu=24rw5*7IWNREP|ZluftV1N0GeIUz6~Q{YwU&JaSp#TzISIx4Vq_PyMz4W1wt@ zzEu{+YR|x*0-1XlaEj3z`5%TQCZF&5-s`s^G1Cic$xpChfby+IkB*&1%;WzNdlJ9r zzIV>)uT`3AiYk@&Y=hSD>(x#iWooWGGebnU^95sHbv-@_t-v|4J83Uw{>T5}LhG*N zD=yUE9Urs(rw@Fx0hr9Xe$3p^O-e-Qcj26AX#n;$peSAgD%%Pk(mE|0ed<5av%dkk zBz%`XF_gM9C8zJdAG^hGCY?fhw?D*x%7Rkz9|Ya&V6E~xCeUaxnN<}0X<$L_z>-)g z;tZY{b(xk9Q5JV&Y!$zQIq-89yQdCTggT^w4WKv?04YQMXUm4P zHy+1sI{H4J4mI0*xH)(`-`}6ZICnB+2962V-?eWav2_{x@U=cUdVP6wdvrsR`e^kk z4FS7m0XNqalB{g9Cr6}Lc`qD0nQ4Sa@8Q~b%lrbdiAF;*LRoK@&rqH$|iSW3+n6nKPN^j z;RgnI=m4}B^Hge_jwk_sZ}Xm^WdtWak-%V2ew0PmZxzMYz>QK3I1ZMaOxGXo{9v=Jj*7j4dnHF_ zIO%i=3;GYG1^np~fZoDy2i7CkR;NYJ4)pQ;zFv>QIFViWj6OcZ++;ikBp(>@GwNzF zs4P|{qe8?ADTvF->qd?KRUDHZ1 zpwcjG+LQ-aBCn>6Mk)l*jeOPE~|5f^O}@i&t7aD|8Bq^(mP;( z5tb%h^XJk%!n3<}(Bs5B+1V?%bei!k85?B^;bm;(r-S)cI?hQ>;&=A-eRm_^{1_Q|%EqEXH=ZZJqE z%Ce%8A^BJZtugSxyb@CTSuzPvL=S{q8F_GoI*lh(S1672g)$qN%Xl2svoLTnJ7)66 ztb-4s_^$RT^s)6v8QkVI~&_{j8#O{ z9qej%M({zF+mv?B>o;!7jV2W`wcy1A1!>+=+>a4+)#q2q0~~t;F1bnJ$#Acx4H<0e z3tDC1+==0ly&_U+AIIkV{p&lMACjO}_K?*)r&;cFm3n4{;sLUvgN9qPIY)PTLI!#a z(Gt7R;X<3#sir-GkkLE9$@K8OYznR*7mKbLl74uOLAYizYefXv4~vF!cJB!5MZ<6r zrLQl~rq2NovWe?6)~IM@BQjE$6aQaIu6tIeJ3(Gv@->+1D0oT&$Nf_0h% ztfV2B@;Gie2zv+V=K9BgkY#YMOl#8-24VR=KIZEgVo(OM9Fj=@-TH@q36%;w8aAlC zl$l*vOR-t{`ansK+n=A$i-H&Cimr6NCwIH3l<%UKOJJ8Jz{`jbq{pdM#(Q8+W9%bI#>m#hhwW z>e7)mY~96m=oEIz>AD&$@T@)6|C~-g=Y%`I2|A9M_2>pUtydD8GlT+`cN%R*gf4y` z9KQ`W`R=we>^Bul1=*(S?NH$3ox(+AV)=L*`FS~n1sGpqz&m=|^G@H_h#_D5#BcX} zyk}3eXgvSFRX(()0OKvsa<>dHw^#Jv+&D+@M_~Fg;YI3JRY`GMWZteA# zv9Y(TcG?7DhZe)H-ITo#!tg_1C;l$dQ?ZA+WLBx64dYqhr<|}?CiCUs&!)HUUmhxG z5{0N}HLy@bbUN~MG9RwedTHPzW+#{7|(zRR1I$oz{p^8Cc9WW482+t!E}bmkcQ0E07}&*0nP!tWS(_Cb^#`+q$2!KC#O zYgv{F#eLN3TdZ&0I9kF9Y8YS}k!QnpFKQ*U;cSlRUv-QY-z?`&(;H<5pTm&$H`H2i zk7i%x*_ZN)|1*SeW^uTB?V=B>qYXJ5<;LFEF?~A7f!;C*n}|CWM%0qfcSMoTvho6? z)rKJv2g)=)BZ!JJo;0ugUW7RJ|6DHm#sItM{}}JNRbUgkM4LH#f=KQ_29H$fRGl@w zW~l2n#pfygrx;VYw7(#1{MJ{&yFh?B;`2d%fjk~(jNr&^y>ZHDKhT?3zj{S0a=v`- z=$;FG1-VFFr^}`n#UBTNV=}M9GcfLL5N~5({v9KWoQ?8FGI~)qq+a`gHZa!j#%#dL z_+FM{RU3~6o8~8t`qZ@+dnD2pkEXEiD(8+eyE27v8KMy4tBpcu{f6s9mK#W`#-rl< zcmt*IeQ|cdqJqxZKdxxKxRNb5T$!Vx)~H6AyS%gXs5KfhjRa(hdum{OM}Ng771Lo# zV!HJI`~OcZVQ)wvBm(|F#$KD(+MJmp$4AqzYS^Iq!^~+KiF}t8PpoWLVB6c8&+Q*E z{*QLPPCDX$_vrShuxDjZ6JD~g`0AQAh4voPH$oAfAtW`M>or4V7#unzLlNgtX3fx!P?ETyNv~k>4Je5P;dM(TG z*&uwtF_}rXEQw-2L-3b=lJM&_#MUCeD0aOI#%kNFnKp#9_}C6Tymm6kie;?QC-36X z(!|^<>Y4icCyoT^A~$$tT>vcqu+SRjCvriFBgU^%eJ9 zK6@=+Vu3UwJ8k5RtIG?nw=Z?d1=c3dzkTm?hf+EAtGe_RaUBoz=W1d-uy;6Xa(C@k5L5$ZEz|eO!@^yVLXE65nC+ z;6eW2XsnL#-vN_UG|uu3iLPGaKsL$Pgfh=JUyCE{e}??^KR>=&^L8I+*;%6V>_?ZO z_lV2cA-v6nKh%Wc=~ZqHzsk+%bJ1$)(F>Vk#E_wAS>Mz8CAr>Gb^THEtXIoEO{v~P zAX9nT`Tl(UQ3cY3@>v}*_p(@X43vl+#QZ|KZZTK|EWx(hBpx1p1~j@~&^zu75J^@T zgS^XYu04TXFQla#QqwVv(01QOgDwYDtOJ%ewR1J3f%}FQ#YCx(!`(frV%%=JPWSMU)T(DQS@IkZzqjNL$sC&2Ix?o0lGlhUwpFqve{xJU+zSwOpl;uU5lodcku!fSrx*{Fi zz1DN{%X6z4F+!#7)8lCr%K}}mt?5@Ut{_T@NR_CgJ5|%6U8fGX7={pnS*B_Y2guHr z54{PdMJe(xq}>N+R7FhH(#(BqMfp8EbUy_W+1#6ge!n9==AmPVr#M0`?9oRp8Q@m2 z%}35_VPt(Js`EN2-nI@9EzZiH&g){x%a!LP(#SfL24gktI|D1QexByiOkjgZpW-Ap zC%mhKdKcp}HD*-6Q!JAh;B@iCa&hCsAxZl}A#VH5db2qqXYVOPK7~_o8~(84xHB#%`aQmk2&3wxo+2 zgU5PvNb$F9BS!X!>0G3qh2_~De+V$?kFChR*H!VD$ZqDm!2( z4GW-Da z+|DLh?xy4eA!XN2Pkj1I`IS~6;-7=TTy8092>b1H_5@4|Q8OTkV{P_TxbzXb*o!&n zI^<%*qTs@#L*?G-e7qSW5-m7LW=RoZFE+KI`*rvUD}-Q#H?MqLI&3wm2;UHk9W@D( zIO4nTrdYKRKT<*I&s+mf26;nS`#3VURIxl@=|`2 z=`#nNr@0kw+7|I;A3OKTnP*^(RG1h<&pBD?y(h<*2UmL?wx0}8YnU?_)ELW!hcYmY z`|rMTBV&q6nD7Nc1cYApzV5MA51l5TSSaKQ1ow3-0pwLrx)`zF$Y2!J1yw(U3rycM ze`eVf(GV&Mcbc&%p4(kMsN9;gn{`?6L-`%FD{;0;>z#_G@`YCCV{uhw5ClwpvcQlv zz?ZJlZ|Kq7?S$=zIT?a^f?ywA|Ksbf2!EX?-1|?c+W;0utjH_N-|0mRsMI0W*fprX zAql%}+<&F1WhiFzccZnXWXL7wE8a!Myg^CV(!6uLU!6JbbwM5XLRU}j{cijTVjSsH zBHZ)i(H8!9G-3^>7}rM?XjfU4;){2A{RB~0!HiBN&g#2(d+&E+;BBGMuD z4Kk%~dAzYlSv;d+LSiA0?oKE^+Z;PD#hngPp9!Dx8(fFPD!K9p7^4&?iKb@vLW5lG z^z;BBErj~7<>Hz@Iy1#OJ0C?cfYAL1%e^xk$%m4ThUg5m;j6?obA3l5=1~}D2|%=<2Z8+G-NRA;G=KOHHY6NMUbC* z88NR+48*IF--Q4Bl!5;5uGH1ko&U?9;f||!Z?~r4|AYmca070Rn5*P=?aw6MSQ5Qw zXEkzlAfYMG`znCX$@*Bj5akhPA%5gbBEm)VzL~UJ$IRBn#wuRD7Nd_bs!fLcg&vz{ z^s0Pq8fbG5>mS=i6g99p7B>#pZ&Ft}vQb;P(PQ#Ed%w*pvSxVU_UuXkf`_!Z^FS05 z`SXx$;+x8Y8#7&iKw{pRJlh(Xl2zynY?I>N0s%e$m{reYwnr_WSq?=sM2Z5L-!}}L zeCvV#UWvbpsvp3rA2#!!sxbsA^u)g;PetP^)NC}W2y8b%nU}88P^dgc0?*o4Fa=C2 zL7F}4Uoyr4o7L3Kys=e#ICoq7?(%NPy#HiNq%H93Y$+~INT7|o=%2%0^q<2X62r1e zDSsNY`IODeks;gjklwF~KL>Y=cIKl}3h&S0(#hZU%#%&VpLUf6GGOm1%fl#znlTZ* zVpn)=7sq~0&Q>R6$Kcvy-gpSZHMz^h2KNhg0a`2*}N)|?+)IlvdKz4)tvyg1u8AOI3H~Elsi4LRim`Hr2^qL~##PrnNqtw2*QAF&Z@KZo)6Vs54Jw^ne&s z9VJE*i99ktV2a2Nh!NByQwavsjhV7mPNiZ8I!6`B!&e#-6!-I&G&+W z4&>?lMjsl!lDyUYI{O794J!hgaHp9a+oc3dh>1Gasa)VZ9_*C1qUQWo_wFa$+lJ^M z=9qs}gN!~9)KV4&Cj{hDv^k16Yz@IyHmXJgpz6OiX!=gr z|2z?Z5R4J$MS@Ad!P}y5J-^@f!`Uyc7y6DSw|07vX`8a&BDOZK7miMoOe)284~R@m zR7YBZxytBp1XHZ-ryUo0Tge+nJcYr#Kx1Ti)lo437EW=GfMf-W_qmt5E~=OWKtTz( z@$;mUUrU zP!AdiB&DAwe{yj}VBK#HVEpH+4;74cQHUR_t`pr#=hdKDU?&tWwd!O7FBW?V1t`n0 zc(Qtr zxHYTQ!~6$1(|{$@1C`Rl=&%@`x({zR_F9^+$2*)A4M7! zJOsgHrhWH$rcFt_)~8yAhC`vi#(Pgn0$Lyczn&D(PC3LTG37~U|2PfIEQd*0>a9=W z8i*-t-eU|)1_hjisWMXW%?6j6R{7BCnr7rj(4w8A#rYhGTF_on+)TctdPstP`w&;q zEi8EJdi$RoE^tbV#I)2{`3+p?K)YtHuw%xocaIq~!Qw~M zE;4laVY~cA@y4gfr3VulA26OhIE|JNCHEEeEB;y|bf@DqUR$T*TGgYT&RUHk+Uy5Igy+^i4(OTcS#<#KZvAwcz&?x|7xj(2up~J09Fo zR*#^>O!MAGJO4IO3&^n{h+g{{g_Ue`&3cxgDbBda?R@Oi5mr<-`xXSvgTkHu>@NHu zcty!J@&2o8r7MwC^b_RF&{;6mRMn>@=GDMm#M-QVxiq-PEG6HDnL4g?H^#vj5K9aa zI4-x{;Er>@g;k{Jh)$WRPRBFf+o@tYBIkwOhtj}n)e)6Dtb+h~c~bA9*IE!4NZ2^PfDE zw6xCuJd~yB(M6D*VB~Z4<^*rBdvouKZkY?$e);qq zk7`NHaksFVa}2vt1yCP|7mbd zHzVC}N)Y+V;-2#YS=sukwO#-17HA0sPto}KyR3~M|41X;9+VJOUhRnJe)xmt@y9|L zzoI(P;&DI?OD(yEbbbBTB^_wMKf1j$;Jdb#T^+rM()WcQC6BfJ77R1}Lo%!t3rMk) z^HVD{jmpHnYr0NHn)E72G=BW3@_%Poy3HQ#!qa>?+e!dHoEXQ!-k)aaTG0IVo;GtfMoSZmydrX1|s766zh`h>_F>sY1<9Ghr!52@C%PwlS4$d8tXFF^{fvy>D-rSj zSf&3Id+Oo9)>jD5kXd??nfnm<{kvgn-x&*SxKyT|ueRd6nrFA((Ha>T|Al$I)#-D8 z`rb#qo}OV*^IX_mOjU9JCC()TuHFLTsqL2`;(1bI1wmF6m^o_2VEP`<%%;s{V*2Lh zwW{+;Xk_wb4zf~g7r7g-cWIXpcD z63ii|W%TLK1gIP(&w9|&kK=3dy1OyL5)L01)yQ>p#pSVzZjc`OCQ0vSIaycfx}&_f z7Nz#-$1Q!T$t8EA(`j9f<2P)?eyKGQeY7}KrpASIE?z5Q(^4Eb_i+9>tcmSKUMt&X z{zxO!XIP>CJ9~i8exsq4Nq#(PW>R_YqT+!+b|>m`=+Dsk*+otUpP}$wF~wNcp^ZMk zK%fIhbzX;1ZMe>GX`||hRjQCjY_m`oBj9Qi<$<$FvvE)Vxl{Ay!rJp|3YHGMK08SN z8lW-`;aB)Q>8$X@Wxb;fxg}_!Yai*`)%+~YD~5O{t8gG@T(G4`p`K!abak`Tmp$>v z&U%rr2+VsTC#%4i{r!tighS9k>{-Mg@E;8 zqy5-&$?VSjX^!TvdzbkCC|=JC;vJgxV~61!dgge@^P zn&~Z^*qN?vZ4-}7#*)u9J$k+`n@^@z zJJCEa*fI_9AMg=}2G9&vm%ax_^~iw+N+@J?H(^HQ9}hrha6-Z7?RFUc4;N_r>!OIO!fP%43rn*=fm~|7 zf-CsPh=1sq_TAb353J}~YwQA^c!I{lp@@M9^hfyK(e)nO?g&XDKos;>+6gTyaA}+u zk^(=zIQi7F>Xy)zXN;n4X{;y%G0suXnU;M-!>IV8b*G3V?T-EjzhI1BhvQ~(h?g0!MQ$$c*oit zDCEBs#lqR(;PR?c2qLvyT>bQjpB8B;+V%Yy3$6p>Y-HNmVr#D1yM(m#5^Xl*{qJuS z^3~4#ujE@%cjcTcImgJd*#ABeg2a!N&>GB9fcdhsIbi&v0~I8sDS30fPrPkjN`?{X z*CyGGcNKknQBpq>mpm`D#_Ge6D2DW?h^7Ozib72iA)f(rpvEwUB+MpE{IMI~2`x&q zI`33OEee{Zfx5PBnieMi$R(d9MbDk7kfp43Cg&;u(}`Ho7eeK(cX2zX&_Ur>{}cI@ zboEleo|5~9^(MsNe_5NSwz<@1tPuOeWLlM)=cH{;6gzI(QW^`+3(19cdLs9k<9)lL ztyNxP6Zrya^yA(=(===RF^7Cv7Ke$rr_hYW&I@wqXgi)gdiiPY%+Pi2&|{t*dh`RN zEhbs+Xp4J|3S_+?_GJ{i&-L|?GDvGo$Jmf=PRnLVmj#{gm#hn-1!&HQ zEiY;7r;wugHIdABO=A*SSeTotv`N9(9#LejoL(nfkMnLlWDlq-U%>Rxjh@vJp*7Ow zWKoW7;8O_Qp>YOcCX+@QHa(A_d(XYTk?S=9C73T}m~C!bwdAw5118L}k;n2lC*)F> zrz${N*ygw4rr17w^5u**>V3KdLQVT+*y~|Gdf%~q^!8+|>;d`e{yVp!+m z0rNBmtRR5y$)<>oP!ZVU=t-Gc4btVjC-2$nPqtqVXH&Uv(y{JQm>UxILC+1?` zkWi>K4S9-yxJf3qXxShW2x#4JvYz-uY^$*Gdp=8p&)RcsMfBJ8g=_r~k$R8K&80D* zuMC8?H?rqDA4&S(Q&bYaukVaHk11o4VLZjp(OOCg`3eS@7##EfC^3aNP(q4M`p^!| zxjYbut;vj;&wF}KZaFq0#v?cgZuirPG(|JO2f@FWf?w85Wp?~_Bqz_{*Q{Tu)hrZ?h0@@&8lgk(dn0F|g`X9*3 zg?0@yDSBGw-$X6=!ThI3N^RW4J~kHlKH$t7a7lP=me2Ved_?buT9z_5!BCJ=`3_Bx z_X@_o4;baTpsVH9dv}zI9+Hv#TWRc9<5y|NrCI6foP~_HSjl$LK_R=3A=9)0NAWoyEa?*!?%a;}R~G)1(|<7`&$I z;+;pVKY-m(5=jY~+OpjDG3-71oOd`nFth=Ncy<&_X_{l@`&}EL3^8#AnLIMPb~QUy z&+d4xc;>|%HSfGOz<+N2lcnxR^cR<+7>WgC&=i+|stZ6@zrWj}t>`?_JDO)K^R~?B z5KAlK9?t3#yK^?=A~xHz8Sx>A;-QnfI$^_TzpOknVc z?gdqp;TjPT4`3^w<1AkjZIKsV^)7K{{0UYXO_&@KNvU^ivDcHtPD`g*)7eQaPzwz? zRiyWwLe*O$E_V;ddT^ESyY)emZ_<#U9nA_k=EnW(Y zD76f-po!{8>JAT51d9p#J-rV)Z>D6y3kMCU*5E1+kL6|0=f+}m?%%l1NpA&@G9swmu zR16IQ|7$%%sJ~g|do14Fg4KToC!N)etLQR|PQFdAs>HsT=zAQEB@e-#YBMsFmn^C6 zRiqJyIoZ}Cm(?f4fVIrVBqh4Lld?E33B!iG&9Hl`sZrLlYp41cw4PyA(j?3!lS5dcPp@iYvvek;f&JsPO_a;7EIG4%QF zF}lyZ%7fWyjWE*|Vkf|wkCZA^pMRdp(RE2b=~1vji3-B3hOSy0f)=M7hlvbnXE@KI z3XPUZCZOSEIn%;ku`UCg@lAqey-WLIX5m#}W zTE{2Z47&B}?d-IKv0#4DLtkX389I9;o;J$;G#IsewbiNIXaDQ68G3=V;yb$}+)P;< zo4T*VZ%Ter5grMoz~ujxt{}_3VEVtPZrrt5%_XF+Q?^HZvkbG0qo(*b{NWx+w_Cv9 z=GP;A7)W5jDrgeb3YIq=)UzrVpXtT8Kp#)s&!|nU5O)W1@r;7uuilr~iFYj#^w{r5 zld`UapIsnQP*}H^LR|DQk@kdx&VG6zy~9rZlScUd*M-zEkCv zrq2hxQ+U~z_ORWq>T1Gh{f}D61R(jFY~fA#fi`zwd`l^s#@AV#tBJbvd&o2!%bh$O zg!VT5()Ydi|9z^&+cyvj&6YW}4DnN$GHiUj(sNuNe~X$9Qfr$>l0mvDip@WlqMcLY zpPO@lKv;jT$m=E6RPJKn?>c zhHS);h|?p~2yKp{z~bM68pa|COs0bghGL|yIDcz(XQq(hBpj+nPAn0oaHBO?7nUoS(pB~6Rr6s{r{b(F@LyJ|5qJy zO7(*MUGZ@u;a5|F|DEdE!5W!pRE?a0^!lW^)3W$ZE%U^7b%wflLOeObcZ~_~vSZ0Y z0w)Jj{1z4d&DR&7&W9ub_%^wSD*(dWzP0}QDo*NQZ*?}hwB6;wfwJ-Op14O=bt?Ja z=|;xH-m~KQ-*6iynd^}d_e?u-eI+s=-(Y&-lZ$6yxx-u_V_VuRSI!HkvAvOJANG3r z%{}tet?p`{^1VR65K{}M;5Wj@Eg4ePff;GVmUqj)IdK_SvX>E<0RvIG|C zz;sPiSN801ZXCj$NJ6y#z>8@AN_vwoM;U`Ua_`kZ3f4vepjqi>MFaOi?+=^ebK|MbSo8h=7CdZwOA;iPX`#%nk{~M4!s{z@wT(~8}e+qYE>G@R=TZ77LZv8s1x%922|l^LJeX>nzvL#Mzw}hdQh+27&*f z${hy#qai25W5tO5i_4KpUCOaaj1tin1E1_$&pK!_f;}gaXtefOgivK&Jfb4yKx4o3 z*r@bWNFH1lw(iq^=_TS^_wc97Fd$mc_p&x!)yaGd3UeUAP=?@7GZjgm?7b8njk-m_ z6QdB5lco9&BTwEE!ui3-LbyOw^PHniU2xm2N$d-#xbv;0tnm}?@UvI0jhopg$GGH8 z5fpE+=l=`S5@O5Mu;-_fYyvRAn-}tY%y(TZB2!;lsb9~#Y5W{8GNN#sF){+zJTJ<7 zlIH%rW_YP}6&W`M&i_LZ9g!j(=Qh#I4lx07thb+mY8EFS zRFWJc_cYhNJ3nM>nT@Id&G<)ivGiy`dJ|`VZ*gG$BP z{w^e;Jm~%6CL7o@LIcZPZlfvq!!-vhBBF(cFIlqkcwhs#m*b0b421nYIp_yi`&o{+ z_L(W)~YJ4{vzC(^92Jg=|j2|Z1RU*w}SuaiMU4QDs zKC{x?q0!*Jwp=fiAbF{Ueg96>GF$VoHhl;(8s39F6b(KG0}NR(L~}j+`&cy9{sC0j z&Kq6w?#TgJ!4nWwY%GXyKBanWRl3oa*R&}B{qzT=|0WV>7r}mF_=E< z9LhO7)1EW9GWZ8el&ME;|D(amcE^BIWK&7txn940HJw~(lD-tvq1HO_4fnX$!}i)~pWoeFC?Ib~>xbpZ9r|xq%}H#fP`hE1cX$CH3zjl_L}>B<*=a$M!{9_-!}_B2O1h9U4Ynx`P6PL=ZNC zU6eJFiYA8~g+lhF$eqV=slgfh!b5wTV4{IPOK2A8-xaxB_I4s?ICEQkB zwJcR`7i&mb`=Vc6%=Xn$r>eKX)ir6OGoRron&pg zh=7`(J66Za9hF~xJ)W)bNn3p0VT|zYNV+xl&yTP*d0u&@;wv$4X!N{M76F*7(g|Je z_ZNC*#g1hyK`f9=4<*p6?=E(cIX3Z#1doq_zsG! z-Rg(sZ$yhC^Q;Jwj@Bl9K2#ka-sm~6+;VldtGUbRo}eIt^HbqwK#pVSF9H7)>B9J9 zXTq=Lg$9h~Jd=SbMxwa@CmnW%BoE}Ov z#2TVNBN}2{EC|#V#jRt9teLM)MBta#fQnn9&#uAL=rf-V;aT6( zi*NTfH^P~?Z+*@gsy@=%wYi3WI;qnOBY7$$`C^+O@Ve*o<>Ae3+vQ%(&?paq$D(vi9BMgkwZ_rFfx4x6Svl-h;EC4c} zCyU*#MosTRKD6Y)6l;>Kh`DAfFSK^vvcO%k304l=ErYzv z{>lz+DYMWQ{H`V5_mAwPndhR$*?GJ$N+D_*V0nzNiA$*hEJT#AJ-RS$fCkEyba+_@R= za3B{`@<449F6?^lO_6u&`O)cSeborDZM^njapo_oHj>{0f52SN#u)>}Jep?9!Vlw; z<>fPpKNM7P-jACx6q1+W7~{$8_FQ9nU*D5PsMYSNZ=e++m^{IFGJa${+Ql= zJ-V6=dwD8sz^J40NAl%ZIGKY1*_(L2i`{*`j%vQG(RD01$xY01Kv2O-0P4t;Z!>K` zp~Q4v-d4EftLf}5%8a)+7f}v0sqBCoZ^4z>4Svc`78?US#bLu8*c@SLqgcluGZk7> z63(Z|6sXFOZ$t9@G6*wH(Uvy&>JD^7Z8m9y@n&IG63MooF>fvi*7AF6*RelZvj$z! zMe1>kBsZH?)RcFs(M==9=UPD#?lgexM)_)QUOFHZMKc!s@jim*q(**ngKnw$PmoeSy16v-zDH!)x`s;OFeE*->5dt%Do2E>J56_>M7 zqrl3??;}J0AA90lO|44Bz;ubR7uL0#XrI${!?+AwT|a2r6HHoqhx-9eeBMJbQPsa6 zXy%M$*iT?#tF>g#&^*4;7!Yl^7GsA*89pMOA0|XW#r?gnVO;A-BVG=qgUP zQ%0)tm+IJLjImv2!1JVY>*l_fc@4G0OZ0DGiG2BMC$N1y>r0QMg#wn-GbXNQ| zpHA7sG4)W(fdkTV{?8~?Sb7kgpT1##Ap24+J(oR&%sh?jnwGYe!5`TfnZDJISMJ*& zgvm^9unbWvkY@p&3UQ}IzGh$7u2X{4>$OHi@~9N@5t#($K}@J`=38jz;V5T%3K>t> zUm>VtMVL+t(Z?~oenG-uxjBjtwgP#76-^wO>}OxH0}yL^W~SZ8b+TWH4lWSKH_)_u%Ycz@ct+DV^qn|R_oUe%(G4Uz9d^`zdP%wf*270>*%KbyS zx`kOKeE^@bIvfnb7LwtsDxX-S6~E{8vW+CYx9AU}g8e&gVg|az{YF9@QUXLxkROMt zbZzPpqCEDlBbo_a@JvxE^rm%6Cuc#0u2IxY$M zL#~*e$}XmSgvy%rf$qYDw1rFU#!GCAOIhuHO~b%aH-~n`qXM@Ruio{{sqlARR0#$# zF2}t=PXydrHk+ z3OG=HH!7`qU@$V}DUMhrjdSz*))Q>^S|s-MtHTU5KN68H2se5`8nw7!7k?@B4Xpi= zRFcZQlvRB$?h59bIOZM&WFx-%{?wzHI zcg9Al$?_Y-!}(iRmzz29QraYQn~5yM`z<~hZWZ?Y?+5ET&o#!530s<1I5oa{|2^r$ z6Tj4WdF)b~e89bB+bu9EZm3mq?N?u*^u(TGE3u`Nd*NN%SpHJI+OVVyDKVcS6Oe-u zx5)g4A&sR3n&mD?IAz-*H{Jzb_Hp!+w%71-@rzdVXJcO1;Xk+FyPEo}M7 zDm`KL#oLg+bFK1ACbmiWmwBL&S#|TiMTp{usk6U(=6#S}B%`Ny`o0r` z3X0Z4=GD;QRE-A-U9SpeOMG0htI)OB-DV!@uxC-HdC+Mos*HRZn@$riF4$UN;G|2& zRfgxK;0pFha2A-vp2sqkOnD~%%4E9ryVT%9E}ZvgT6eK6;gfx17Q7ZnDF)!Ryi!b) zr!USewE2XTkBP?A_T}gE4}|K$WoI~4KYm`WyCqy|b7Yyz4)&}sh+NZ4viT@>Ay5JT z;oG_v?hg#IVgX2s``Oa=M@q)?M!v(dnasQgNk|E1QdSoOOEe+F?75i5kF;d56LcT4%Xw~1;B@#NQH6}1r z^M5%#p4k@4UB{)5J^AsNJ5YZ0mTe4R$%L-%EvRKxw2OZ32i8Z;IgM`Y`bYpKGqR&wjR-G_P z+ZzqjX3FW7p(E^7*r`7SV~P@+EjCwHmUSe#V-=jKp zQxyy3RVO8PxVYVmq=Xc0PlxF^Nr;|CB4TWtIybS1+6-dOxkI|&F^!&i`1px=2R#BE zmSi#LcfFi)TtmGS&3OkdMpwxY-guUgm_*X&>X&~|0oFx!dph0nHs4p@CUfT2VP(eR zLlX_x#}3Q+e8Ht;38+%q%w8%Z2X|q81K%Qfk%HFHfmqxEf6Nm>F$QB44wsHvx3(_fw^Z zkmmP?Bsc+#7brRgZg4p}C)qj2c#jBQz$pwho!5Y? zv+BRc;nXxtj~Y@x=vl70X(!~&Tr!16KCvo|(RY;S?RGCPKd*mZkUKJrX8vjKiH@wN z`h)U$m5B3cPhK6?#c8&e7o*pE0o(kF@5Wyxi|OhNQgeMv4`j1GKKzYEuONfgZDAz` z#4)IRHm^~NyZvO?q*7ooGg5SDKv}U4$UbD`H>~Nlw^DnZwjVseKyNBnMe_@XJhR(; zAi;c>VPIprtrelcs0@Ge17~b#+N!Ow$@g|g8_~0W;MY+q^_n5rRiX;b7ye$`XJvVX zO179Tl0=f$Oos1$lsSQl_EL`kPOzpC3}v{3fv_!9DM93k-^_>8jva9^G*gOrfc=S9 z)6mNT)7gig;)V`%d5Jjus>`r#Jq6clG}}i%M0A)Ul<%yI2iX`oCeIIkg4V?Xe%puR z1Mdf)4DH*sW@me>*V>(q78Vy~|9EAb%-vAVuy|c>R6}MjLZv&ZYkg`O~;!bm||aQ2lnvOOb@1v0`3@ z*E5Y5i`)zsps{3F~ih$1;{|NZl@T{B{5xJnxd=Zrc3{q_&>Mb2)ke3{4%+9a)+nYsf`kCBQ8j7!+nYzDNKp&$=1p*oNllJah9Sk|Oyh-~YAC^n|^XBlv zJ}LT*-0w8LWNs=`0ROh#;kZ)1!^#zDLGdhhirF>5X&;X8JnuFziN?e1T-d2> zNwHS1!E`knN(fcCIuMlsUb1n6+|g0JLUhw#HkygVgT(0`xLq-+uk1;-h0NvrF?v}* zxI=crt6|J)2V(7>wM|P=+ufVL#>nuEyzg=NJS3Ae)4Z2y%~hvlY(JL7OPI)&&~Kp5 z`FI03{xL!nOGBWTl#$q#+;1}hBfmKrGq_^S8n#KwEpAIlMC|P{KHuV!AG(;fQs7zK zVodCWfsZr*#77a@crVKaPmM_LXjXJp zcZsH=G}yXV{nt~1I#6qO`7kvm*8w)|?{OA>_o@FHa433OVrtU)(bjRZm9x%jcUmV- z68^+kpeG=Y%_hPZ!*8&*hwo9zdelL~AogvVMZ-?K)@|J6mSL*`ZIr^VL&wG@Z8aYw zTeN8ky!%$io732dbRJOgyQv>dNEdboo-R{fx<7dEFl=U8gID~Z52o&o9J(Oidn|vG zFSHd_0Ls^J@?i_T3e&0c)qG0&s*E5MAQkx)WzZGq0tzuK`61~4=Y~-HzzPAPezDDD z*9v_^vpzpZJ|#?eStypq|9MjRCuYX4z^d^MJLjL!vzsJe44pAR`JZ%kGaa7>vX9zD|a}W$?HPFOYr4U?s24dB2#?)63 z+`+&Y% z+cGMJluXg6UcZDbU<+m~r5F{q3Wfu_7sGaa@BL$=Hl0ZRDhplWo2h$PpK)3U>ls0_ z{1Ynl_C&WrwSpBy9+_`;UtEHziC_c3GQa0XlJtF~|4vB+Jey*Hl!jCvmbc%^eny*= zDz0<%D*Ti)Z_?*rwEtzqb7f}3oB|Arp5SkgSSP7RuWntO)bI5asF76=@=w3@kBEwk z?~NDf<;!v-Oe7$1eh9~MeLRt}LHrJ_)KGT>}yB$vgd!A!u%H49jS6CqkFr+-vHjwS# zKN7)1S0c3ANV`S^>953RXR(Z|s}?=K)rwxmjt+GA8pu+e|s4X@{~ztuoyv71&_ znXQX2!}>dCtIfz@qjzhW91NZwLNnJDNa6Qs(Ggd_yyds|J+#%#sLrFUI-KBr|AIju zHgNn2#&QL;Js|U_xOTReGGCF_s7AXo8+x*n7j)WxV+iMYQhK-Ech%|7djg`8Lt7(= z`3xHlmaX8CF9|9rPajb;0^iL}OV;F_CYQfuph6dM%kJJFunv+<5NT}J+tm|C2)zNh z1#o0BmW}p_ZUrhWy3*)#zguH$zzZWb1n|OCOSrni2dE#Sh;VbJNz{go-darX0jeEt za@3_9p4kV-!kb*d@NV_?a7{%4l+zv(c>+yyUcr3T7ji@}N*S_>(R zlzrbmExg!R5X)b5E&i8Y3`>PQ@#LPC7K*Z4bqi7MMnIjPXhFQThA= z#r;0z#Cy~*T!E~9FCdx+r7Ji+f#QgU1@;PGVV@ud#Sv(fz-jj@FCyC`tD*Km&{Y#PpnH>ZV?m=E5<$`3E=A7Du~DoX6Evwxkg|tuncVQp%AQGC zp)euzJk!D5MQwR9o2F|{E4hZ3b%<9p)swdj_>yhujG`XoS7MtUQ9Yp$F@ z->zM63?E9*<)zUNG3>_CsZ^F5{kn6cP*?^M<#-!f-sZodR`7z2IsFD$%9FE9`6LA= z-4;FlQXlL*B>rk$mHUMrOAt4^B54u_UebQIfE9e?q>%n`xDUt&X+!jyU6C06QXiV3-S+5a% zW>KZGoS!g(!*!|)DWW?5G@CwXv*R+B(mC8Rp}q@WLz6ouVxX>D#qtmvK}$a~d^nr% zePiDjAF9Jxwzu5QG|ZLl9=+YSKv^9?aR|1EakKa{#l0(!OB%gNuW4AsxkHWUj$>;_ zua-ZEGrCMt3r+781T`pBI=y!TZiB1=f!WsSy-A|@8~Vi%vSAEVBb6AUhXluL6M9Se-L}c~Uz@WvsTp;;D^RO4rv1As@l~Gg z%F=~kmo)GF_CHa-Gxk;L1Fa5R*w~B(!={jo@YLaT1>y}+j8J|5XvKI1LtGyi-B_{sZ~2t3P)zUyo6x8QMB>S?6{>>ur<&Pb8kQ#* z1w?U&jby%O8c8KhWTQw~#<7d;S7+w>DdEIn`De%;aMq)8BhAe#xP1O;e!nabfn!U9 zM>8k7y>z5}J?8;=~Qtz&%XKHay zJVz>V47M2>;gT42A3so~EbuozT=(O=-@Va~>Ib%n0{(}7D2>YKC z5!c6$rnc)zS_J{=q1h?0DfKX&E#_7XWGM)f6>OG=o#{0RCv}6sH+^8`N$^2cJW}9yt&dT0IrRdjo;IC7>73GgxJ7LTiOUeXl0TA(-Y0!y8ix=A32d!|D>{@jy@<3-I zp|9V%6@ue9KK~3~EzQ9~zIQM; zZ_+lF$=SJ6m0b@IP2<(RMasQFoDI&~95*Z(v1;iBBJUL(OYINH>dfYsSuu&Qr=i6SxHf$Q2V8YV7dTv- zKsgdEw+)7?pA9&M7vQTytm3>(%OY$BgW>gRj7Q@HW4;*MvCb-xvvkU+se)5k%l0`P;I6Npm@;UIQ=%}4!9o5r~|L;ec2NUs1b^$OQu zub>zqLou$(>(I43p8n)4n7E*u`ckQMFm6_rSx2svQ2x0vaHC)%`l4z^M@_@5k2UE4 zzs3E*y=^icb%_jW^FSv})cg9X91s@W|7BS2r1>eg3sazFU4D(V&^;IZk_>@dorYP_ zb6HoAVL6K_^$lU0$PuzT1fktkKFbv}uUFVI=~aA(h$+k4|2%1@5pbffQElCLx3Z}J z5}sBV{lkJy!C!Kqc{c}!p{f6i1G?w0M5dp`o;5k#$x^$nUU*sm;I10#_Ib}|3G2I2 z{QyL$m(2!}S}w>YwY^{RHaq!HR)73YEB6|62e-Pb&B>~&mM#jCO`;6KI{9g6%&rrt_RuL+MtO~gBF^| z=sz7xzlItyXceY_P}jG@8}cP1B1e@ie)^!+_0^)@ultJWZuz+u@Tt+W9I2!CTh>~+qqQTM?2xMt!$xx!H$@vP8D!syWbGi&= zRW@vhqk{LV(irSNC7E3+#^o?*XRVkjF;$(uGf9lc;5I5WPDIGiB3a)Ny}l$ zJOt853}GFD+Wip2yq8Bkv3!TC(mri#K67p%} zxPn-Xsp4+;?9ZI-4kk7I`qL^1>n7-|&{fJ;DS13b2^XByQbr}MZ`^BRsqGSqWlqZ; zjVTl@Wo|6kvH9S~T*o?%wA)$rM&r`RJAkr;Df4l+ZHLGh6DwBr`cDOO`~Ixs%tg`j z#*d!OcqZH~dly;foK}JIjk5)ImmdS9p#Z2iSI(U!?*r1i^Wx4gxK`hZH=i%4Raza2 z*fOVxc9%Gk06Ep)Wrt}-$n%$J1H$Ih!OT>gp`z(ZzmJ0<)LWD7))F*G^D8baE&xY| zN_Q9hJR!IGz&HX1mLDcpwc_2p8*Hp4t9qMW$?+9q0a>Qpk<+kP0Xm|} zT25-1Xgm(z0cNHo1Wa>2GRGODcxdN zHaGtW>uLF|;pZ8xED0Mc%2y*bOM(fq=rvy?0SF(bd{w&_PB6e@j(<_(=&L0mS6O=d zEPyQoYN13Ox9R%9^e^-R*D%r4PZxMG@C-Fx0aP`#THPkQu1_3bo%}0iHlc*>~cc3lz-0Oh*b03I+m_tnWXg?cw}l(Tl** zZaKF`&)$J%`y!`7p7*iqZj`mR6X zqsuaMpY84q?nD3u_tD5U=J&3T^lxy!zf0$cZ`}~4L#>riOp+rG$a@;|K+r?&W2Pd^ zTI(*`ItKl!Rj>dU$o(tX!dcHcZk(`cl8@{N-oefFGi}ldqk|SK2WHC8&eb@XR!0A* zTbl;2MmwYBRqxBMB(*{oo4eRBVzMLT^ zV^hij3CYhdgM`Md04pZO5|d8!&*?A5AD7GDM*8mAd89){Nfj0O!|R`ufEtoxOU&*m z1p<=4O)w`tTmTEqT^VtI{WAljdGetYK*$Gavf~GpLdH|4P9L@c` z7U{NcR3RgAHOkSl!-iX8lr@^qwm>-OI(9z;J?T2A%|&iz8t$qhivf0u&Whz_r1 zr9pLfIhZUJjR0jemX6qp26yAL!yE{0!*AD-MiL0Xt$J!`tgpqraPo%)IA~*JICztr zepnzj+#>m51q|0IMT3W@qrPtyyMJS`$~5=ekPVuWzxhk?0XM{o=uh* zA7>z)$^r?o-=vTonc&5sGP%*4i?+4=EI)$A0x8Kq7WXIG`8nz(m?71dsa%r( z#i=bNzd5+M`R+fPExo|#=_tW+$lN z&;N-^$kUSnR~e#<8aV zJ2K?Jj@$pKOUX+y<{>^m5b*SOA@OGJ?jKZkfdq%X^ckJ%50n}3?*EcZND z$4*VR&T9wO9yM?dUy3J+%4U??b=TEvYpQNqcuW?C1D*^6$yp{LWS;XNi%r zNSLpJzh7>g5VBsvx10+%34W8T}P znKg~6qk{I=+p<7?8az}-Ql65vy}#$TqWesmMqD0>+d9GF5xWm(c>DPXJc7e9BX-fh zP+k2}Y0|e0!mPHjT}!{GMrlT77z5jND6XLw1-_{nTVmTBeWUQN;Zw-Z;Tc<$m}qD4 zJjw#Yqn?H%%x4-fJR;^&8OHz8PHsQH>{8O;^)Elc1?7S0Oaq%oq(P1U#bt==_bM#| z{UkjxQfU-V=#xs5wM8k8c?J*sla{i5l}<2A|0k{V?$mq4uFchPNBp@mN+;$GZpYZp z8w93W2t#x2$PJ|Gz)a=m8gM+)%w#X!7ayImI@#hEqIq$px)3|67Fp=};yxyco^9my z9+__%;++$?rjQx`>~DxP8r^oh)kQd$;6=EcX-T6f#gZmyxTmc25${6Y+Ss1wfnObK zTY5{+tM!cr%e)6aO>1)Q(Cht?`jVm0&V?r#+gO6VhSvKNWjVNji$*X@FW4byO^vre ziu_wv+D7ocSY@PK_MH#dBL7uJPlZwW{|Y0y6`%hKBa~nkb$+#oe}$2r%KvX+bS@5IB0xpPM>p3<7qzkO&+*iWh?uu7C-`@o61+%JLr)7)G*ceeMg3Vd09( zWi^;xC%mS5HOQ@)>Cqsqa{Vinhu_WnO=url<<|4MdhjECN zPC2%~c!6)+Oi!>`h?+nymTa1-HD{X}=n_bxG{C}7lW3|J0~2(ofI8FfvAxuQ$xt6; zFGZPaL&1XuU-q~?(?_1wt;K1ID#@H@ZMPWg#BI4w9<*0!OPK7Jhm}DP;Uh2X9KLos zEA|f9%i1bm^a_QX1vT*8Hg#{ewEZ?cv-7rjGiS52 z`*GW0p6tW5(J6^0FUqahqHw0#l3$Uu9(kB@ zxXoZDxU4rdd|beuKfhTp%;~;(TSL)tF}QHfPG_+@Ky|6du_YQy7ru8(*wN73kY!8d z7(&JYBxr|Yww{iHjz3m}98v7+wD=WfitJyrMr749^&^}9@?QuFokCH=EXU@J}^0i{P7cNc&|0 zd0Ayc`Zi4?qm**3quTTuiqWoOE!Rtx+Vp?p_KS*6TRiI7TaF64S&1z(um&5ek0&e% zMVIi@TO1>KB(aq@2j|@T$b`_RtV5R&ToG$hMvqoN)^tM;?AHo7H%9Ni9vcs(d)jFn zIue#yE*Sz_YYS5Y#vl!`?DaD)7K6Wkem{K?oEz#H*!9ys1^{xE(-DD@ymXrC`q9n+5sRVuIwR7YCv zyC|PmL{uNmXCUVCh&$F3Ay5m)i}AGkXykJB8P>x_YNrmOh2r3`vYTz#jx#CYK9kKj z^;&-QCWl$VuLM@S>T(4=^xsea5(X0OLNf7L2MzA?jF$642;+4I+fe6OYE5m%w5BA@ zqq&jN->`*5FG(hWuSeFJ$^#hFpYF2|n^L_uWNRJ=XCuPE&8c%Pw)ddec=T+ybX-*NT0=d3cl{+pyjc`uaIB0tCxvOKZ4DTxxzgdUdZJ9X>n-u!6 zP9uJ6bEC6q5ttH})r+4pN-XkC4X?H%Pa*WUon4r9THDrTcm6xObP7b@ut!sa0+p*; z*BZB{1)@ADsED9dJ44itjUL$?ym)Geos-172HLwnzAxo*To_u@TpvSen*jrktp3Jn zf8p~v^=;vHEK57sYnX1~wKY{*ZJ)nTF5>n*(kdaH!Q}}ZbBj5vd$h#LDlYNKQ?|`l zmnyn|to0aR_xaM~A`Hx~iz0nk!b{_+gSzeT7_VVy8@)n=+o^55Ul4cyX3n#?h?vHf zo%pyX?LXPv;!(=QW?P;;70_Z448qjj>JOS_J@|7Im6t8JyR?)CyQNl^ZPO2Xd*sGf z$~F1fNiMk0JjaPe;iEL#iaJIBo%0xcUiM(Ev1F->KF`vhU#Wo2cz`UOGVbmU&-uIi zgDVpgH!o84{;7waKoT^*4Ies;88qrlC&j{;E~`z9g>6&%M|?$mkAkh>=8tJinfo-^ z1=`x5B!`$lo*LAjF2>eo#4#A94+ehOU-`nB<#-AN0v-as*N6n6t#?c9U79Gn$8|yh z-{RuJ8rI7uRX{*IIO)e6f*%~;15e~aTZ`Xqfa7{m?FNXsy!%SI;nk?~JzMed4)Szk zcjFD381nGf;Vcat%^sh+GY(8SJL!FMozvj%0m>Q)EAbpG?OukuL<#9Ml*(yjmt$vf z3a3UPx-%)h_mAA5!=l@dO^T(BVBH#yYe}2j_t-Vjt`g=z@0MBFVcOY-?t1=*R^M3P3#FJ36^AYV%NR zYE-m=CoH1{E+GjKd9~Vjwah?m-QHnP&z2|q`M~!P9NwoH7%O#Vhb4sk@cA)!iTX{GlgxS+u#9^2+gKN+Tu%sigKDNg;I*O%D3y)n@7V zR)5i@@=?y@X-@H=c!7@6ZKPRN9hIJM!C_?64Dd+WNWB<+l6{&RkoQ|)E(!nis&Ovc zva3ah-o`OikY(!*7If1vI>Je;6uWdg<*oHs$XS8T$(T3T?=q^jwnnM#DkP!0rCd-}$nRgR#-Hria0OG8ilavz*o)4$P85 zV<0jqg2MSU7`+I$+l0be5q)iW)#L|nq%dV-3NjWjvsblTkAPP zqGlv*FMOUHWv`-xzePqFvjd;C23UUS9(0nTJXS#?ggP_;8c~aV3RA->&apTo(u!BJ z2HH+8vet7~2Vx;(Hps8$8~rMr$-GR5Ee;}*J?hLbVt@@BZWKKql=kZnVkR4SCnVe` zX2I(}ZwGl&=>n6Hjndwm+6^^1RO*)Qy3q!p+Wpc}D(r|jw-z2UEAP6s2L|FbdR<^z zkf#s$kK`4SQFC)q$|=bml5jhx>rIIP2St=LY)a(jZ`X%fh;* z65fIC&m#RBM1^Co8G~pH01-`_CIR_aym)cbQ7W{pT}dY)U%mbh9P?cU$|J)@DCQ;^ z+}J*=scMvY?Yl0$+_?xI(bM<1q6KcIR;HPp6eSsaTp+Ijj=E?tDw)s+~(Xk(k_XZk%-=6B-rSSi&A1npJ1wlna^h< zZJT;Vap51~Y29yEMg_+&DSAg>P=_4}*jXM~B)t&{s|!hMfROIBkt}Q#`GK;_^lcquthg!jN?1rt7?FU8qV0~f#8o*CG z$=@erOLf}3#mQe5Idku;OdlvHT+OPa72a^awe%OT>6vuwsT!HZ+Ss!bGek~`pW;qq z!aUEvUfev37d7I~K3ADbcDApUEIH&NHQypUPjzj}ZAd_<`M~TdG@LotvLkJq3?odX zTCw7!&@s}Sk<}_PP-IxWkfGd|60$lU%biS0mUsriitkP;(G#~ zwZ1a2`u$fz09$*uEdTGMjrpW2e&}1GR6{kLtaHNL$y=WU5nYqtaogh&RfWhZ*K_R}U{ zBhvuke%=5fDDC!^kRJfpp;P*XmXqvm+G~6BH?qy^HmnSfvgrD6Ql?e$9O~~`6v0n} zr$>r3rZN22<(dA%ZKOVp=j>u7{C^F2t<@?Rv}XGUY4k}NceIx>Gvnvx%dXgCu`1vd zbx9BEZFQ_R^OimOaRtIKvd6ly6ARP)RVn?w5jo($Vc{2Z3@93FuW-4 z*-STI1A8G~xZ>)oO9HXcJJ!kL2#pMhJUjv`Qh@HaJ7J(%2Pi@Y?Z_qB><{Qp zMfYqCycVmWMm0~8MwDypA8nwzw9y;)hn)p@NlSBSG)?r!P1Y;Vh>aO1uMrr*f=Z{5O zdB|~`KZ^>e+(LIouYUqX1w@04ol7S%N1X%;JO&%Pkdd5gj8>@(9ahUNp^15G>RcNV ztP&)l9(Lqf4%WJHqNwo+O8Vos%dX#@z@L-8_!@OYtG3pCu}w?*m>R3aTn4MaV3iym z%TH{2|K6GEbd>6L{6xL&K|kgW`=*zz(>YzPa4kV|AX;DcjmY0X-mk>aatFPlST3fW zh8oRUAD7UwBOQKm&*GtudLxJSSRcGnhvS1!dw+I*O~Pd?EucZm?Qyh$&_l~++swJ2tP7lZ+2sPS&_ z1I(5IVxZFHk+J)BzvI6_ zkL<WHD=8yMr2c+l8b3UEZbZfhNn`( zzW>me&h$=hh~pS;_TYr$9jw(ue2;#+di1r=ZE!M@BzMl6Ptt#_@*O8Kiq7mS4IaoIpK}X`@ zMnx=DJi)-jbI3Wo6duCxK^l4^dPt*}H{_zF)G}#Si}y7Ap@F$}_q^e>=zKm(Z$57Q z_gAUTy(rr Date: Thu, 21 Sep 2023 15:37:54 +0200 Subject: [PATCH 08/18] Rename CVA to CVDB and generate clients, #TASK-4158 --- .../app/misc/clients/r_client_generator.py | 2 +- .../app/misc/clients/rest_client_generator.py | 2 +- .../app/cli/main/OpenCgaCompleter.java | 2 +- .../app/cli/main/OpencgaCliOptionsParser.java | 2 +- opencga-client/src/main/R/R/Admin-methods.R | 2 +- .../src/main/R/R/Alignment-methods.R | 2 +- opencga-client/src/main/R/R/AllGenerics.R | 20 +++++++++---------- .../src/main/R/R/Clinical-methods.R | 4 ++-- opencga-client/src/main/R/R/Cohort-methods.R | 4 ++-- opencga-client/src/main/R/R/Family-methods.R | 4 ++-- opencga-client/src/main/R/R/File-methods.R | 4 ++-- opencga-client/src/main/R/R/GA4GH-methods.R | 2 +- .../src/main/R/R/Individual-methods.R | 4 ++-- opencga-client/src/main/R/R/Job-methods.R | 4 ++-- opencga-client/src/main/R/R/Meta-methods.R | 2 +- .../src/main/R/R/Operation-methods.R | 2 +- opencga-client/src/main/R/R/Panel-methods.R | 4 ++-- opencga-client/src/main/R/R/Project-methods.R | 2 +- opencga-client/src/main/R/R/Sample-methods.R | 4 ++-- opencga-client/src/main/R/R/Study-methods.R | 4 ++-- opencga-client/src/main/R/R/User-methods.R | 4 ++-- opencga-client/src/main/R/R/Variant-methods.R | 2 +- .../client/rest/clients/AdminClient.java | 4 ++-- .../client/rest/clients/AlignmentClient.java | 4 ++-- .../rest/clients/ClinicalAnalysisClient.java | 4 ++-- .../client/rest/clients/CohortClient.java | 4 ++-- .../rest/clients/DiseasePanelClient.java | 4 ++-- .../client/rest/clients/FamilyClient.java | 4 ++-- .../client/rest/clients/FileClient.java | 4 ++-- .../client/rest/clients/GA4GHClient.java | 4 ++-- .../client/rest/clients/IndividualClient.java | 4 ++-- .../client/rest/clients/JobClient.java | 4 ++-- .../client/rest/clients/MetaClient.java | 4 ++-- .../client/rest/clients/ProjectClient.java | 4 ++-- .../client/rest/clients/SampleClient.java | 4 ++-- .../client/rest/clients/StudyClient.java | 4 ++-- .../client/rest/clients/UserClient.java | 4 ++-- .../client/rest/clients/VariantClient.java | 4 ++-- .../rest/clients/VariantOperationClient.java | 4 ++-- opencga-client/src/main/javascript/Admin.js | 2 +- .../src/main/javascript/Alignment.js | 2 +- .../src/main/javascript/ClinicalAnalysis.js | 2 +- opencga-client/src/main/javascript/Cohort.js | 2 +- .../src/main/javascript/DiseasePanel.js | 2 +- opencga-client/src/main/javascript/Family.js | 2 +- opencga-client/src/main/javascript/File.js | 2 +- opencga-client/src/main/javascript/GA4GH.js | 2 +- .../src/main/javascript/Individual.js | 2 +- opencga-client/src/main/javascript/Job.js | 2 +- opencga-client/src/main/javascript/Meta.js | 2 +- opencga-client/src/main/javascript/Project.js | 2 +- opencga-client/src/main/javascript/Sample.js | 2 +- opencga-client/src/main/javascript/Study.js | 2 +- opencga-client/src/main/javascript/User.js | 2 +- opencga-client/src/main/javascript/Variant.js | 2 +- .../src/main/javascript/VariantOperation.js | 2 +- .../pyopencga/rest_clients/admin_client.py | 4 ++-- .../rest_clients/alignment_client.py | 4 ++-- .../rest_clients/clinical_analysis_client.py | 4 ++-- .../pyopencga/rest_clients/cohort_client.py | 4 ++-- .../rest_clients/disease_panel_client.py | 4 ++-- .../pyopencga/rest_clients/family_client.py | 4 ++-- .../pyopencga/rest_clients/file_client.py | 4 ++-- .../pyopencga/rest_clients/ga4gh_client.py | 4 ++-- .../rest_clients/individual_client.py | 4 ++-- .../pyopencga/rest_clients/job_client.py | 4 ++-- .../pyopencga/rest_clients/meta_client.py | 4 ++-- .../pyopencga/rest_clients/project_client.py | 4 ++-- .../pyopencga/rest_clients/sample_client.py | 4 ++-- .../pyopencga/rest_clients/study_client.py | 4 ++-- .../pyopencga/rest_clients/user_client.py | 4 ++-- .../pyopencga/rest_clients/variant_client.py | 4 ++-- .../rest_clients/variant_operation_client.py | 4 ++-- 73 files changed, 126 insertions(+), 126 deletions(-) diff --git a/opencga-app/app/misc/clients/r_client_generator.py b/opencga-app/app/misc/clients/r_client_generator.py index 0e8fd8ca7ec..71dd8948759 100644 --- a/opencga-app/app/misc/clients/r_client_generator.py +++ b/opencga-app/app/misc/clients/r_client_generator.py @@ -26,7 +26,7 @@ def __init__(self, server_url, output_dir): 'Analysis - Clinical': 'Clinical', 'Operations - Variant Storage': 'Operation', 'Meta': 'Meta', - 'Cva': 'Cva', + 'Cvdb': 'Cvdb', 'GA4GH': 'GA4GH', 'Admin': 'Admin' } diff --git a/opencga-app/app/misc/clients/rest_client_generator.py b/opencga-app/app/misc/clients/rest_client_generator.py index bfb3b78489f..6aa1b7ed5ac 100644 --- a/opencga-app/app/misc/clients/rest_client_generator.py +++ b/opencga-app/app/misc/clients/rest_client_generator.py @@ -50,7 +50,7 @@ def __init__(self, rest_api_file, output_dir): 'Analysis - Clinical': 'ClinicalAnalysis', 'Operations - Variant Storage': 'VariantOperation', 'Meta': 'Meta', - 'Cva': 'Cva', + 'Cvdb': 'Cvdb', 'GA4GH': 'GA4GH', 'Admin': 'Admin' } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java index d0de5314db8..c820242e234 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java @@ -1,5 +1,5 @@ /* -* Copyright 2015-2023-09-14 OpenCB +* Copyright 2015-2023-09-21 OpenCB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java index 755c69db1ad..8cd4a5a92c5 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java @@ -1,5 +1,5 @@ /* -* Copyright 2015-2023-09-14 OpenCB +* Copyright 2015-2023-09-21 OpenCB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/opencga-client/src/main/R/R/Admin-methods.R b/opencga-client/src/main/R/R/Admin-methods.R index ed22f7ea37b..fb0eca89883 100644 --- a/opencga-client/src/main/R/R/Admin-methods.R +++ b/opencga-client/src/main/R/R/Admin-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Alignment-methods.R b/opencga-client/src/main/R/R/Alignment-methods.R index abdb140bcee..f2d94f8d87e 100644 --- a/opencga-client/src/main/R/R/Alignment-methods.R +++ b/opencga-client/src/main/R/R/Alignment-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/AllGenerics.R b/opencga-client/src/main/R/R/AllGenerics.R index 622c2b6cfd7..ee079be8a55 100644 --- a/opencga-client/src/main/R/R/AllGenerics.R +++ b/opencga-client/src/main/R/R/AllGenerics.R @@ -1,6 +1,6 @@ # ############################################################################## ## UserClient -setGeneric("userClient", function(OpencgaR, user, users, filterId, endpointName, params=NULL, ...) +setGeneric("userClient", function(OpencgaR, filterId, users, user, endpointName, params=NULL, ...) standardGeneric("userClient")) # ############################################################################## @@ -10,42 +10,42 @@ setGeneric("projectClient", function(OpencgaR, projects, project, endpointName, # ############################################################################## ## StudyClient -setGeneric("studyClient", function(OpencgaR, study, group, variableSet, templateId, studies, members, endpointName, params=NULL, ...) +setGeneric("studyClient", function(OpencgaR, variableSet, templateId, members, study, studies, group, endpointName, params=NULL, ...) standardGeneric("studyClient")) # ############################################################################## ## FileClient -setGeneric("fileClient", function(OpencgaR, file, members, annotationSet, folder, files, endpointName, params=NULL, ...) +setGeneric("fileClient", function(OpencgaR, folder, annotationSet, file, members, files, endpointName, params=NULL, ...) standardGeneric("fileClient")) # ############################################################################## ## JobClient -setGeneric("jobClient", function(OpencgaR, members, jobs, job, endpointName, params=NULL, ...) +setGeneric("jobClient", function(OpencgaR, jobs, members, job, endpointName, params=NULL, ...) standardGeneric("jobClient")) # ############################################################################## ## SampleClient -setGeneric("sampleClient", function(OpencgaR, members, sample, samples, annotationSet, endpointName, params=NULL, ...) +setGeneric("sampleClient", function(OpencgaR, samples, annotationSet, sample, members, endpointName, params=NULL, ...) standardGeneric("sampleClient")) # ############################################################################## ## IndividualClient -setGeneric("individualClient", function(OpencgaR, members, individual, annotationSet, individuals, endpointName, params=NULL, ...) +setGeneric("individualClient", function(OpencgaR, individual, annotationSet, members, individuals, endpointName, params=NULL, ...) standardGeneric("individualClient")) # ############################################################################## ## FamilyClient -setGeneric("familyClient", function(OpencgaR, family, members, annotationSet, families, endpointName, params=NULL, ...) +setGeneric("familyClient", function(OpencgaR, families, members, family, annotationSet, endpointName, params=NULL, ...) standardGeneric("familyClient")) # ############################################################################## ## CohortClient -setGeneric("cohortClient", function(OpencgaR, members, cohorts, cohort, annotationSet, endpointName, params=NULL, ...) +setGeneric("cohortClient", function(OpencgaR, cohort, cohorts, members, annotationSet, endpointName, params=NULL, ...) standardGeneric("cohortClient")) # ############################################################################## ## PanelClient -setGeneric("panelClient", function(OpencgaR, members, panels, endpointName, params=NULL, ...) +setGeneric("panelClient", function(OpencgaR, panels, members, endpointName, params=NULL, ...) standardGeneric("panelClient")) # ############################################################################## @@ -60,7 +60,7 @@ setGeneric("variantClient", function(OpencgaR, endpointName, params=NULL, ...) # ############################################################################## ## ClinicalClient -setGeneric("clinicalClient", function(OpencgaR, clinicalAnalysis, interpretation, members, clinicalAnalyses, interpretations, endpointName, params=NULL, ...) +setGeneric("clinicalClient", function(OpencgaR, interpretation, interpretations, clinicalAnalysis, clinicalAnalyses, members, endpointName, params=NULL, ...) standardGeneric("clinicalClient")) # ############################################################################## diff --git a/opencga-client/src/main/R/R/Clinical-methods.R b/opencga-client/src/main/R/R/Clinical-methods.R index 3c6f7a5cf7e..9153616b670 100644 --- a/opencga-client/src/main/R/R/Clinical-methods.R +++ b/opencga-client/src/main/R/R/Clinical-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -59,7 +59,7 @@ #' [*]: Required parameter #' @export -setMethod("clinicalClient", "OpencgaR", function(OpencgaR, clinicalAnalysis, interpretation, members, clinicalAnalyses, interpretations, endpointName, params=NULL, ...) { +setMethod("clinicalClient", "OpencgaR", function(OpencgaR, interpretation, interpretations, clinicalAnalysis, clinicalAnalyses, members, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/analysis/clinical/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Cohort-methods.R b/opencga-client/src/main/R/R/Cohort-methods.R index b35ee7a1a63..15fdb9a0e6b 100644 --- a/opencga-client/src/main/R/R/Cohort-methods.R +++ b/opencga-client/src/main/R/R/Cohort-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("cohortClient", "OpencgaR", function(OpencgaR, members, cohorts, cohort, annotationSet, endpointName, params=NULL, ...) { +setMethod("cohortClient", "OpencgaR", function(OpencgaR, cohort, cohorts, members, annotationSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/cohorts/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Family-methods.R b/opencga-client/src/main/R/R/Family-methods.R index d65abc404f3..8439bc5eed1 100644 --- a/opencga-client/src/main/R/R/Family-methods.R +++ b/opencga-client/src/main/R/R/Family-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -38,7 +38,7 @@ #' [*]: Required parameter #' @export -setMethod("familyClient", "OpencgaR", function(OpencgaR, family, members, annotationSet, families, endpointName, params=NULL, ...) { +setMethod("familyClient", "OpencgaR", function(OpencgaR, families, members, family, annotationSet, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/families/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/File-methods.R b/opencga-client/src/main/R/R/File-methods.R index 23df515b2b5..c3163af8bc1 100644 --- a/opencga-client/src/main/R/R/File-methods.R +++ b/opencga-client/src/main/R/R/File-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -54,7 +54,7 @@ #' [*]: Required parameter #' @export -setMethod("fileClient", "OpencgaR", function(OpencgaR, file, members, annotationSet, folder, files, endpointName, params=NULL, ...) { +setMethod("fileClient", "OpencgaR", function(OpencgaR, folder, annotationSet, file, members, files, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/files/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/GA4GH-methods.R b/opencga-client/src/main/R/R/GA4GH-methods.R index 8ca1438e547..91e50ab9a75 100644 --- a/opencga-client/src/main/R/R/GA4GH-methods.R +++ b/opencga-client/src/main/R/R/GA4GH-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Individual-methods.R b/opencga-client/src/main/R/R/Individual-methods.R index 4133f71de23..84cbf3c13c8 100644 --- a/opencga-client/src/main/R/R/Individual-methods.R +++ b/opencga-client/src/main/R/R/Individual-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("individualClient", "OpencgaR", function(OpencgaR, members, individual, annotationSet, individuals, endpointName, params=NULL, ...) { +setMethod("individualClient", "OpencgaR", function(OpencgaR, individual, annotationSet, members, individuals, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/individuals/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Job-methods.R b/opencga-client/src/main/R/R/Job-methods.R index 94abcdf48cd..5f0ea55bf3a 100644 --- a/opencga-client/src/main/R/R/Job-methods.R +++ b/opencga-client/src/main/R/R/Job-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -40,7 +40,7 @@ #' [*]: Required parameter #' @export -setMethod("jobClient", "OpencgaR", function(OpencgaR, members, jobs, job, endpointName, params=NULL, ...) { +setMethod("jobClient", "OpencgaR", function(OpencgaR, jobs, members, job, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/jobs/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Meta-methods.R b/opencga-client/src/main/R/R/Meta-methods.R index bfabf7acca2..17c8aec917c 100644 --- a/opencga-client/src/main/R/R/Meta-methods.R +++ b/opencga-client/src/main/R/R/Meta-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Operation-methods.R b/opencga-client/src/main/R/R/Operation-methods.R index 335dff1c3a6..73008a6dc58 100644 --- a/opencga-client/src/main/R/R/Operation-methods.R +++ b/opencga-client/src/main/R/R/Operation-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Panel-methods.R b/opencga-client/src/main/R/R/Panel-methods.R index 4f765b41361..4f7e71e90dc 100644 --- a/opencga-client/src/main/R/R/Panel-methods.R +++ b/opencga-client/src/main/R/R/Panel-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -36,7 +36,7 @@ #' [*]: Required parameter #' @export -setMethod("panelClient", "OpencgaR", function(OpencgaR, members, panels, endpointName, params=NULL, ...) { +setMethod("panelClient", "OpencgaR", function(OpencgaR, panels, members, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/panels/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Project-methods.R b/opencga-client/src/main/R/R/Project-methods.R index a0a51b7c452..4ab8c70d386 100644 --- a/opencga-client/src/main/R/R/Project-methods.R +++ b/opencga-client/src/main/R/R/Project-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/R/R/Sample-methods.R b/opencga-client/src/main/R/R/Sample-methods.R index 3ceadc72b5c..f23d51b52ad 100644 --- a/opencga-client/src/main/R/R/Sample-methods.R +++ b/opencga-client/src/main/R/R/Sample-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("sampleClient", "OpencgaR", function(OpencgaR, members, sample, samples, annotationSet, endpointName, params=NULL, ...) { +setMethod("sampleClient", "OpencgaR", function(OpencgaR, samples, annotationSet, sample, members, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/samples/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Study-methods.R b/opencga-client/src/main/R/R/Study-methods.R index 7869db7f30a..054e4bbcac4 100644 --- a/opencga-client/src/main/R/R/Study-methods.R +++ b/opencga-client/src/main/R/R/Study-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -46,7 +46,7 @@ #' [*]: Required parameter #' @export -setMethod("studyClient", "OpencgaR", function(OpencgaR, study, group, variableSet, templateId, studies, members, endpointName, params=NULL, ...) { +setMethod("studyClient", "OpencgaR", function(OpencgaR, variableSet, templateId, members, study, studies, group, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/studies/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/User-methods.R b/opencga-client/src/main/R/R/User-methods.R index 4cefb8e2c86..1584c0e075a 100644 --- a/opencga-client/src/main/R/R/User-methods.R +++ b/opencga-client/src/main/R/R/User-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. @@ -38,7 +38,7 @@ #' [*]: Required parameter #' @export -setMethod("userClient", "OpencgaR", function(OpencgaR, user, users, filterId, endpointName, params=NULL, ...) { +setMethod("userClient", "OpencgaR", function(OpencgaR, filterId, users, user, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/users/login: diff --git a/opencga-client/src/main/R/R/Variant-methods.R b/opencga-client/src/main/R/R/Variant-methods.R index 615c413c941..58fa54e96f8 100644 --- a/opencga-client/src/main/R/R/Variant-methods.R +++ b/opencga-client/src/main/R/R/Variant-methods.R @@ -2,7 +2,7 @@ # WARNING: AUTOGENERATED CODE # # This code was generated by a tool. -# Autogenerated on: 2023-09-14 +# Autogenerated on: 2023-09-21 # # Manual changes to this file may cause unexpected behavior in your application. # Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java index dc48ff2bb33..410029a185c 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -45,7 +45,7 @@ /** * This class contains methods for the Admin webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: admin */ public class AdminClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java index 226e648621e..cb76d509e42 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AlignmentClient.java @@ -40,7 +40,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -49,7 +49,7 @@ /** * This class contains methods for the Alignment webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: analysis/alignment */ public class AlignmentClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java index 886b7a6c547..d2a34debe8b 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ClinicalAnalysisClient.java @@ -52,7 +52,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -61,7 +61,7 @@ /** * This class contains methods for the ClinicalAnalysis webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: analysis/clinical */ public class ClinicalAnalysisClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java index 78a7e0a0b6e..464bccf9f3c 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/CohortClient.java @@ -37,7 +37,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -46,7 +46,7 @@ /** * This class contains methods for the Cohort webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: cohorts */ public class CohortClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java index ae91502ee96..28509381a9b 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/DiseasePanelClient.java @@ -35,7 +35,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -44,7 +44,7 @@ /** * This class contains methods for the DiseasePanel webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: panels */ public class DiseasePanelClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java index a60018dae29..fc41fdc86b6 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FamilyClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -45,7 +45,7 @@ /** * This class contains methods for the Family webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: families */ public class FamilyClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java index 0daaed2ba76..f813fd06120 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/FileClient.java @@ -43,7 +43,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -52,7 +52,7 @@ /** * This class contains methods for the File webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: files */ public class FileClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java index f2ab9689941..13f517d8fbc 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/GA4GHClient.java @@ -27,7 +27,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -36,7 +36,7 @@ /** * This class contains methods for the GA4GH webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: ga4gh */ public class GA4GHClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java index 7df1783bb70..7241abf1571 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/IndividualClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -45,7 +45,7 @@ /** * This class contains methods for the Individual webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: individuals */ public class IndividualClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java index 3903233ddf2..741ce9b9f4c 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/JobClient.java @@ -37,7 +37,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -46,7 +46,7 @@ /** * This class contains methods for the Job webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: jobs */ public class JobClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java index feb157ed16c..1f53e93943d 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/MetaClient.java @@ -28,7 +28,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -37,7 +37,7 @@ /** * This class contains methods for the Meta webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: meta */ public class MetaClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java index 612b5aef340..7fb04407c13 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/ProjectClient.java @@ -32,7 +32,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -41,7 +41,7 @@ /** * This class contains methods for the Project webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: projects */ public class ProjectClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java index d67240c583c..d8e4df075da 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/SampleClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -45,7 +45,7 @@ /** * This class contains methods for the Sample webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: samples */ public class SampleClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java index a2b23f256cb..6c0ccc7405b 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/StudyClient.java @@ -45,7 +45,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -54,7 +54,7 @@ /** * This class contains methods for the Study webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: studies */ public class StudyClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java index 01186549f6d..de64fc0bbf3 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java @@ -36,7 +36,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -45,7 +45,7 @@ /** * This class contains methods for the User webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: users */ public class UserClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java index 2633b78b62f..c8c64eb994a 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantClient.java @@ -62,7 +62,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -71,7 +71,7 @@ /** * This class contains methods for the Variant webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: analysis/variant */ public class VariantClient extends AbstractParentClient { diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java index f0f7b325297..25d8baaccdb 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/VariantOperationClient.java @@ -50,7 +50,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. -* Autogenerated on: 2023-09-14 +* Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. @@ -59,7 +59,7 @@ /** * This class contains methods for the VariantOperation webservices. - * Client version: 2.11.0-SNAPSHOT + * Client version: 2.12.0-SNAPSHOT * PATH: operation */ public class VariantOperationClient extends AbstractParentClient { diff --git a/opencga-client/src/main/javascript/Admin.js b/opencga-client/src/main/javascript/Admin.js index f95c4cc1992..d2360169f0a 100644 --- a/opencga-client/src/main/javascript/Admin.js +++ b/opencga-client/src/main/javascript/Admin.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Alignment.js b/opencga-client/src/main/javascript/Alignment.js index 26b1793b8a6..bc89db681be 100644 --- a/opencga-client/src/main/javascript/Alignment.js +++ b/opencga-client/src/main/javascript/Alignment.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/ClinicalAnalysis.js b/opencga-client/src/main/javascript/ClinicalAnalysis.js index 3ebee290063..e2f78ab24e8 100644 --- a/opencga-client/src/main/javascript/ClinicalAnalysis.js +++ b/opencga-client/src/main/javascript/ClinicalAnalysis.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Cohort.js b/opencga-client/src/main/javascript/Cohort.js index a07d53ecddb..dc60589a027 100644 --- a/opencga-client/src/main/javascript/Cohort.js +++ b/opencga-client/src/main/javascript/Cohort.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/DiseasePanel.js b/opencga-client/src/main/javascript/DiseasePanel.js index 7a6141e0f6e..7110b11e7aa 100644 --- a/opencga-client/src/main/javascript/DiseasePanel.js +++ b/opencga-client/src/main/javascript/DiseasePanel.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Family.js b/opencga-client/src/main/javascript/Family.js index d2e39a4cd6e..ba2b0388341 100644 --- a/opencga-client/src/main/javascript/Family.js +++ b/opencga-client/src/main/javascript/Family.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/File.js b/opencga-client/src/main/javascript/File.js index a836e51b9ba..911ef949bfb 100644 --- a/opencga-client/src/main/javascript/File.js +++ b/opencga-client/src/main/javascript/File.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/GA4GH.js b/opencga-client/src/main/javascript/GA4GH.js index 33a4dfe1c19..27512d50b5c 100644 --- a/opencga-client/src/main/javascript/GA4GH.js +++ b/opencga-client/src/main/javascript/GA4GH.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Individual.js b/opencga-client/src/main/javascript/Individual.js index dd0432d66b6..f74942a95bd 100644 --- a/opencga-client/src/main/javascript/Individual.js +++ b/opencga-client/src/main/javascript/Individual.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Job.js b/opencga-client/src/main/javascript/Job.js index beac42dcea1..1025b47d6c1 100644 --- a/opencga-client/src/main/javascript/Job.js +++ b/opencga-client/src/main/javascript/Job.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Meta.js b/opencga-client/src/main/javascript/Meta.js index 96e8aac93c4..01db0c77a91 100644 --- a/opencga-client/src/main/javascript/Meta.js +++ b/opencga-client/src/main/javascript/Meta.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Project.js b/opencga-client/src/main/javascript/Project.js index 7c5c23edfdd..cbb8f11c136 100644 --- a/opencga-client/src/main/javascript/Project.js +++ b/opencga-client/src/main/javascript/Project.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Sample.js b/opencga-client/src/main/javascript/Sample.js index b2497dbe695..8946f10ceb7 100644 --- a/opencga-client/src/main/javascript/Sample.js +++ b/opencga-client/src/main/javascript/Sample.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Study.js b/opencga-client/src/main/javascript/Study.js index 51e51d76a8d..fca98d52c02 100644 --- a/opencga-client/src/main/javascript/Study.js +++ b/opencga-client/src/main/javascript/Study.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/User.js b/opencga-client/src/main/javascript/User.js index 31faec99671..35915ec5f1b 100644 --- a/opencga-client/src/main/javascript/User.js +++ b/opencga-client/src/main/javascript/User.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/Variant.js b/opencga-client/src/main/javascript/Variant.js index a2f6fe4e762..d9439f7c98d 100644 --- a/opencga-client/src/main/javascript/Variant.js +++ b/opencga-client/src/main/javascript/Variant.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/javascript/VariantOperation.js b/opencga-client/src/main/javascript/VariantOperation.js index 12076aa53c7..b9098424bfe 100644 --- a/opencga-client/src/main/javascript/VariantOperation.js +++ b/opencga-client/src/main/javascript/VariantOperation.js @@ -12,7 +12,7 @@ * WARNING: AUTOGENERATED CODE * * This code was generated by a tool. - * Autogenerated on: 2023-09-14 + * Autogenerated on: 2023-09-21 * * Manual changes to this file may cause unexpected behavior in your application. * Manual changes to this file will be overwritten if the code is regenerated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py index c3eed0d4d60..f31cdfdbe0a 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Admin(_ParentRestClient): """ This class contains methods for the 'Admin' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/admin """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py index 386d9376f12..9bc67c570d9 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/alignment_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Alignment(_ParentRestClient): """ This class contains methods for the 'Analysis - Alignment' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/analysis/alignment """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py index 5af123acb9f..42726068e16 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/clinical_analysis_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class ClinicalAnalysis(_ParentRestClient): """ This class contains methods for the 'Analysis - Clinical' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/analysis/clinical """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py index 214599c70ed..56a6bc14438 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/cohort_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Cohort(_ParentRestClient): """ This class contains methods for the 'Cohorts' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/cohorts """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py index da5155d2819..a1726e8c0b1 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/disease_panel_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class DiseasePanel(_ParentRestClient): """ This class contains methods for the 'Disease Panels' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/panels """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py index a86a4bccc99..b60403516bf 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/family_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Family(_ParentRestClient): """ This class contains methods for the 'Families' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/families """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py index 6449bda6899..fad895ae19c 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/file_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class File(_ParentRestClient): """ This class contains methods for the 'Files' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/files """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py index 782c27b4c72..642f1991404 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/ga4gh_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class GA4GH(_ParentRestClient): """ This class contains methods for the 'GA4GH' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/ga4gh """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py index 48b4e7ad2c9..f5e40232abc 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/individual_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Individual(_ParentRestClient): """ This class contains methods for the 'Individuals' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/individuals """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py index f4d6ebadbdc..5e89ae5ab93 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/job_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Job(_ParentRestClient): """ This class contains methods for the 'Jobs' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/jobs """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py index 02b50b88ec8..9a6b7d564d2 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/meta_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Meta(_ParentRestClient): """ This class contains methods for the 'Meta' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/meta """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py index aac7327696d..4ab51325764 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/project_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Project(_ParentRestClient): """ This class contains methods for the 'Projects' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/projects """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py index fefa4362e9d..be639101daf 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/sample_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Sample(_ParentRestClient): """ This class contains methods for the 'Samples' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/samples """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py index 89c1fade434..07b4dea33f6 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/study_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Study(_ParentRestClient): """ This class contains methods for the 'Studies' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/studies """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py index bfe0c2d9c7d..ce4d480c293 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class User(_ParentRestClient): """ This class contains methods for the 'Users' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/users """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py index 1b71a2e4ee5..1e36904b911 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/variant_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class Variant(_ParentRestClient): """ This class contains methods for the 'Analysis - Variant' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/analysis/variant """ diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py index f34c9918dd4..40fc3e805f2 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/variant_operation_client.py @@ -2,7 +2,7 @@ WARNING: AUTOGENERATED CODE This code was generated by a tool. - Autogenerated on: 2023-09-14 + Autogenerated on: 2023-09-21 Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated. @@ -14,7 +14,7 @@ class VariantOperation(_ParentRestClient): """ This class contains methods for the 'Operations - Variant Storage' webservices - Client version: 2.11.0-SNAPSHOT + Client version: 2.12.0-SNAPSHOT PATH: /{apiVersion}/operation """ From a16d6d8413e1d05a80ed4424f670955825bd79d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Mon, 16 Oct 2023 16:19:25 +0200 Subject: [PATCH 09/18] catalog: fix clinical analysis loading, #TASK-4610, #TASK-4158 --- .../managers/ClinicalAnalysisManager.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) 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 3ceb9b2e0f4..6f9cc944028 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 @@ -56,6 +56,7 @@ import org.opencb.opencga.core.models.common.FlagAnnotation; import org.opencb.opencga.core.models.common.FlagValue; import org.opencb.opencga.core.models.family.Family; +import org.opencb.opencga.core.models.family.FamilyCreateParams; import org.opencb.opencga.core.models.file.File; import org.opencb.opencga.core.models.file.FileReferenceParam; import org.opencb.opencga.core.models.individual.Individual; @@ -63,6 +64,7 @@ import org.opencb.opencga.core.models.panel.Panel; import org.opencb.opencga.core.models.panel.PanelReferenceParam; import org.opencb.opencga.core.models.sample.Sample; +import org.opencb.opencga.core.models.sample.SampleCreateParams; import org.opencb.opencga.core.models.sample.SampleReferenceParam; import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.study.StudyPermissions; @@ -643,20 +645,22 @@ private void load(ClinicalAnalysis clinicalAnalysis, String study, String token) individualSamples.put(member.getId(), member.getSamples().stream().map(Sample::getId).collect(Collectors.toList())); for (Sample sample : member.getSamples()) { try { - catalogManager.getSampleManager().create(study, sample, QueryOptions.empty(), token); + Sample sampleForCreation = SampleCreateParams.of(sample).toSample(); + sampleForCreation.setIndividualId(null); + catalogManager.getSampleManager().create(study, sampleForCreation, QueryOptions.empty(), token); } catch (CatalogException e) { if (!e.getMessage().contains("already exists")) { throw e; } } } - member.setSamples(null); } } // Create family with individuals try { - catalogManager.getFamilyManager().create(study, clinicalAnalysis.getFamily(), QueryOptions.empty(), token); + Family family = FamilyCreateParams.of(clinicalAnalysis.getFamily()).toFamily(); + catalogManager.getFamilyManager().create(study, family, QueryOptions.empty(), token); } catch (CatalogException e) { if (!e.getMessage().contains("already exists")) { throw e; @@ -687,7 +691,9 @@ private void load(ClinicalAnalysis clinicalAnalysis, String study, String token) clinicalAnalysis.setInterpretation(null); // Create Clinical Analysis - catalogManager.getClinicalAnalysisManager().create(study, clinicalAnalysis, QueryOptions.empty(), token); + ClinicalAnalysis caToCreate = ClinicalAnalysisCreateParams.of(clinicalAnalysis).toClinicalAnalysis(); + caToCreate.setAnalyst(new ClinicalAnalyst()); + catalogManager.getClinicalAnalysisManager().create(study, caToCreate, QueryOptions.empty(), token); if (interpretation == null) { interpretation = interpretationList.get(0); @@ -696,12 +702,14 @@ private void load(ClinicalAnalysis clinicalAnalysis, String study, String token) // Add interpretations interpretation.setId(null); + interpretation.setAnalyst(new ClinicalAnalyst()); catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), interpretation, PRIMARY, QueryOptions.empty(), token); for (int i = 0, interpretationListSize = interpretationList.size(); i < interpretationListSize; i++) { Interpretation tmpInterpretation = interpretationList.get(i); tmpInterpretation.setId(null); + tmpInterpretation.setAnalyst(new ClinicalAnalyst()); catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), tmpInterpretation, SECONDARY, QueryOptions.empty(), token); } From 2daade7786d5a317fcf32c0acd682de50b86742e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Thu, 9 Nov 2023 16:05:27 +0100 Subject: [PATCH 10/18] analysis: update tool factory, runner and execution daemon to execute analysis/tools from a third-party package, #TASK-4158 --- .../clinical/ClinicalAnalysisLoadTask.java | 2 +- .../opencga/analysis/tools/ToolFactory.java | 51 ++++++++++++++++--- .../opencga/analysis/tools/ToolRunner.java | 29 +++++++++-- .../docker/compose/conf/configuration.yml | 2 + .../executors/InternalCommandExecutor.java | 2 +- .../executors/ToolsCommandExecutor.java | 9 +++- .../src/test/resources/configuration-test.yml | 2 + .../opencb/opencga/core/config/Analysis.java | 12 +++++ .../src/main/resources/configuration.yml | 2 + .../master/monitor/MonitorService.java | 4 +- .../monitor/daemons/ExecutionDaemon.java | 25 ++++++--- .../monitor/daemons/MonitorParentDaemon.java | 4 ++ 12 files changed, 122 insertions(+), 22 deletions(-) diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java index 735d3f55e11..d099fc2f3a7 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/clinical/ClinicalAnalysisLoadTask.java @@ -15,7 +15,7 @@ import java.nio.file.Paths; import java.util.Map; -@Tool(id = ClinicalAnalysisLoadTask.ID, resource = Enums.Resource.DISEASE_PANEL, description = ClinicalAnalysisLoadTask.DESCRIPTION) +@Tool(id = ClinicalAnalysisLoadTask.ID, resource = Enums.Resource.CLINICAL_ANALYSIS, description = ClinicalAnalysisLoadTask.DESCRIPTION) public class ClinicalAnalysisLoadTask extends OpenCgaToolScopeStudy { public final static String ID = "load"; public static final String DESCRIPTION = "Load clinical analyses from a file"; diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolFactory.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolFactory.java index 4fac5476896..1125e86d7d4 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolFactory.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolFactory.java @@ -37,14 +37,16 @@ public class ToolFactory { private static Map>> duplicatedTools; private static List> toolsList; - private static synchronized Map> loadTools() { + public static final String DEFAULT_PACKAGE = "org.opencb.opencga"; + + private static synchronized Map> loadTools(List packages) { if (toolsCache == null) { Reflections reflections = new Reflections(new ConfigurationBuilder() .setScanners( new SubTypesScanner(), new TypeAnnotationsScanner().filterResultsBy(s -> StringUtils.equals(s, Tool.class.getName())) ) - .addUrls(getUrls()) + .addUrls(getUrlsFromPackages(packages)) .filterInputsBy(input -> input != null && input.endsWith(".class")) ); @@ -85,9 +87,20 @@ private static synchronized Map> loadTools( } return toolsCache; } + static Collection getUrlsFromPackages(List packages) { + Collection urls = new LinkedList<>(); + for (String pack :packages){ + for (URL url : ClasspathHelper.forPackage(pack)) { + String name = url.getPath().substring(url.getPath().lastIndexOf('/') + 1); + if (name.isEmpty() || (name.contains("opencga") && !name.contains("opencga-storage-hadoop-deps"))) { + urls.add(url); + } + } + } + return urls; + } static Collection getUrls() { - // TODO: What if there are third party libraries that implement Tools? // Currently they must contain "opencga" in the jar name. // e.g. acme-rockets-opencga-5.4.0.jar Collection urls = new LinkedList<>(); @@ -101,6 +114,10 @@ static Collection getUrls() { } public final Class getToolClass(String toolId) throws ToolException { + return getToolClass(toolId, Collections.singletonList(DEFAULT_PACKAGE)); + } + + public final Class getToolClass(String toolId, List packages) throws ToolException { Objects.requireNonNull(toolId); Class aClass = null; @@ -112,7 +129,7 @@ public final Class getToolClass(String toolId) throws Too } catch (ClassNotFoundException ignore) { } if (aClass == null) { - aClass = loadTools().get(toolId); + aClass = loadTools(packages).get(toolId); } if (aClass == null) { throw new ToolException("Tool '" + toolId + "' not found"); @@ -121,11 +138,19 @@ public final Class getToolClass(String toolId) throws Too } public Tool getTool(String toolId) throws ToolException { - return getToolClass(toolId).getAnnotation(Tool.class); + return getTool(toolId, Collections.singletonList(DEFAULT_PACKAGE)); + } + + public Tool getTool(String toolId, List packages) throws ToolException { + return getToolClass(toolId, packages).getAnnotation(Tool.class); } public final OpenCgaTool createTool(String toolId) throws ToolException { - return createTool(getToolClass(toolId)); + return createTool(toolId, Collections.singletonList(DEFAULT_PACKAGE)); + } + + public final OpenCgaTool createTool(String toolId, List packages) throws ToolException { + return createTool(getToolClass(toolId, packages)); } public final OpenCgaTool createTool(Class aClass) throws ToolException { @@ -141,12 +166,22 @@ public final OpenCgaTool createTool(Class aClass) throws } public Collection> getTools() { - loadTools(); + loadTools(Collections.singletonList(DEFAULT_PACKAGE)); + return toolsList; + } + + public Collection> getTools(List packages) { + loadTools(packages); return toolsList; } public Map>> getDuplicatedTools() { - loadTools(); + loadTools(Collections.singletonList(DEFAULT_PACKAGE)); + return duplicatedTools; + } + + public Map>> getDuplicatedTools(List packages) { + loadTools(packages); return duplicatedTools; } } diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolRunner.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolRunner.java index 88a96004a3f..830110c7c23 100644 --- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolRunner.java +++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/tools/ToolRunner.java @@ -16,12 +16,14 @@ package org.opencb.opencga.analysis.tools; +import org.apache.commons.collections4.CollectionUtils; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.analysis.variant.manager.VariantStorageManager; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.managers.CatalogManager; import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.config.Configuration; import org.opencb.opencga.core.exceptions.ToolException; import org.opencb.opencga.core.models.job.Job; import org.opencb.opencga.core.tools.ToolParams; @@ -32,6 +34,7 @@ import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; public class ToolRunner { @@ -42,16 +45,30 @@ public class ToolRunner { private final String opencgaHome; private final ToolFactory toolFactory; + private final Configuration configuration; + public ToolRunner(String opencgaHome, CatalogManager catalogManager, StorageEngineFactory storageEngineFactory) { this(opencgaHome, catalogManager, new VariantStorageManager(catalogManager, storageEngineFactory)); } + public ToolRunner(String opencgaHome, CatalogManager catalogManager, StorageEngineFactory storageEngineFactory, + Configuration configuration) { + this(opencgaHome, catalogManager, new VariantStorageManager(catalogManager, storageEngineFactory), configuration); + } + public ToolRunner(String opencgaHome, CatalogManager catalogManager, VariantStorageManager variantStorageManager) { + this(opencgaHome, catalogManager, variantStorageManager, null); + } + + public ToolRunner(String opencgaHome, CatalogManager catalogManager, VariantStorageManager variantStorageManager, + Configuration configuration) { this.opencgaHome = opencgaHome; this.catalogManager = catalogManager; this.variantStorageManager = variantStorageManager; this.toolFactory = new ToolFactory(); + + this.configuration = configuration; } /** @@ -102,9 +119,14 @@ public ExecutionResult execute(Job job, Path outDir, String token) throws Catalo * @throws ToolException if the execution fails */ public ExecutionResult execute(String toolId, ObjectMap params, Path outDir, String jobId, String token) throws ToolException { - return toolFactory - .createTool(toolId) - .setUp(opencgaHome, catalogManager, variantStorageManager, params, outDir, jobId, token) + OpenCgaTool tool; + if (configuration != null && configuration.getAnalysis() != null + && CollectionUtils.isNotEmpty(configuration.getAnalysis().getPackages())) { + tool = toolFactory.createTool(toolId, configuration.getAnalysis().getPackages()); + } else { + tool = toolFactory.createTool(toolId); + } + return tool.setUp(opencgaHome, catalogManager, variantStorageManager, params, outDir, jobId, token) .start(); } @@ -177,6 +199,7 @@ public ExecutionResult execute(Class tool, ToolParams too * @throws ToolException if the execution fails */ public ExecutionResult execute(Class tool, ObjectMap params, Path outDir, String jobId, String token) throws ToolException { + return toolFactory .createTool(tool) .setUp(opencgaHome, catalogManager, variantStorageManager, params, outDir, jobId, token) diff --git a/opencga-app/app/cloud/docker/compose/conf/configuration.yml b/opencga-app/app/cloud/docker/compose/conf/configuration.yml index 627644ab8b0..76f098dd12c 100644 --- a/opencga-app/app/cloud/docker/compose/conf/configuration.yml +++ b/opencga-app/app/cloud/docker/compose/conf/configuration.yml @@ -107,6 +107,8 @@ healthCheck: analysis: + packages: # List of packages where to find analysis tools + - "org.opencb.opencga" scratchDir: "" # Scratch folder for the analysis. execution: # Accepted values are "local", "SGE", "azure-batch", "k8s" diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/InternalCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/InternalCommandExecutor.java index bffc809a1da..896cb0e4427 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/InternalCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/InternalCommandExecutor.java @@ -52,7 +52,7 @@ protected void configure() throws IllegalAccessException, ClassNotFoundException // Creating StorageManagerFactory storageEngineFactory = StorageEngineFactory.get(storageConfiguration); - toolRunner = new ToolRunner(appHome, catalogManager, storageEngineFactory); + toolRunner = new ToolRunner(appHome, catalogManager, storageEngineFactory, configuration); } protected Map getStudyIds(String sessionId) throws CatalogException { diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ToolsCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ToolsCommandExecutor.java index 7a0f6b55a6f..b075f5de782 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ToolsCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/internal/executors/ToolsCommandExecutor.java @@ -1,5 +1,6 @@ package org.opencb.opencga.app.cli.internal.executors; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.opencga.analysis.tools.OpenCgaTool; @@ -61,7 +62,13 @@ private void executeJob() throws CatalogException, ToolException { } private void listTools() { - Collection> tools = new ToolFactory().getTools(); + Collection> tools; + if (configuration != null && configuration.getAnalysis() != null + && CollectionUtils.isNotEmpty(configuration.getAnalysis().getPackages())) { + tools = new ToolFactory().getTools(configuration.getAnalysis().getPackages()); + } else { + tools = new ToolFactory().getTools(); + } int toolIdSize = tools.stream().mapToInt(c -> c.getAnnotation(Tool.class).id().length()).max().orElse(0) + 2; int toolTypeSize = Arrays.stream(Tool.Type.values()).mapToInt(e->e.toString().length()).max().orElse(0) + 2; int toolResourceSize = Arrays.stream(Enums.Resource.values()).mapToInt(e->e.toString().length()).max().orElse(0) + 2; diff --git a/opencga-catalog/src/test/resources/configuration-test.yml b/opencga-catalog/src/test/resources/configuration-test.yml index 3ac6f46fe0b..0759ca3c376 100644 --- a/opencga-catalog/src/test/resources/configuration-test.yml +++ b/opencga-catalog/src/test/resources/configuration-test.yml @@ -16,6 +16,8 @@ audit: maxSize: 100 # Maximum size that the audit collection will have in Gigabytes (GB). analysis: + packages: # List of packages where to find analysis tools + - "org.opencb.opencga" scratchDir: "/tmp/" # Scratch folder for the analysis. execution: # Accepted values are "local", "SGE", "azure-batch", "k8s" diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java index 7d19bd65184..fbd9ab5e7fd 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java @@ -23,6 +23,8 @@ public class Analysis { + private List packages; + private String scratchDir; private Execution execution; @@ -30,10 +32,20 @@ public class Analysis { private List frameworks; public Analysis() { + packages = new ArrayList<>(); execution = new Execution(); frameworks = new ArrayList<>(); } + public List getPackages() { + return packages; + } + + public Analysis setPackages(List packages) { + this.packages = packages; + return this; + } + public String getScratchDir() { return scratchDir; } diff --git a/opencga-core/src/main/resources/configuration.yml b/opencga-core/src/main/resources/configuration.yml index ff81c0b196c..ac4acf283d8 100644 --- a/opencga-core/src/main/resources/configuration.yml +++ b/opencga-core/src/main/resources/configuration.yml @@ -107,6 +107,8 @@ healthCheck: analysis: + packages: # List of packages where to find analysis tools + - "org.opencb.opencga" scratchDir: "${OPENCGA.ANALYSIS.SCRATCH.DIR}" # Scratch folder for the analysis. execution: # Accepted values are "local", "SGE", "azure-batch", "k8s" diff --git a/opencga-master/src/main/java/org/opencb/opencga/master/monitor/MonitorService.java b/opencga-master/src/main/java/org/opencb/opencga/master/monitor/MonitorService.java index 5b63b01078a..c43064f4f0a 100644 --- a/opencga-master/src/main/java/org/opencb/opencga/master/monitor/MonitorService.java +++ b/opencga-master/src/main/java/org/opencb/opencga/master/monitor/MonitorService.java @@ -63,7 +63,6 @@ public class MonitorService { protected static Logger logger; - public MonitorService(Configuration configuration, StorageConfiguration storageConfiguration, String appHome, String token) throws CatalogException { this.configuration = configuration; @@ -102,7 +101,8 @@ private void init(String token) throws CatalogException { nonExpiringToken, catalogManager, storageConfiguration, - appHome); + appHome, + configuration.getAnalysis().getPackages()); // fileDaemon = new FileDaemon(configuration.getMonitor().getFileDaemonInterval(), // configuration.getMonitor().getDaysToRemove(), nonExpiringToken, catalogManager); 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 e7920c53303..c74307a9dc5 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 @@ -148,6 +148,8 @@ public class ExecutionDaemon extends MonitorParentDaemon { private final Map jobsCountByType = new HashMap<>(); private final Map retainedLogsTime = new HashMap<>(); + private List packages; + private Path defaultJobDir; private static final Map TOOL_CLI_MAP; @@ -249,9 +251,13 @@ public class ExecutionDaemon extends MonitorParentDaemon { }}; } - public ExecutionDaemon(int interval, String token, - CatalogManager catalogManager, StorageConfiguration storageConfiguration, String appHome) - throws CatalogDBException { + public ExecutionDaemon(int interval, String token, CatalogManager catalogManager, StorageConfiguration storageConfiguration, + String appHome) throws CatalogDBException { + this(interval, token, catalogManager, storageConfiguration, appHome, Collections.singletonList(ToolFactory.DEFAULT_PACKAGE)); + } + + public ExecutionDaemon(int interval, String token, CatalogManager catalogManager, StorageConfiguration storageConfiguration, + String appHome, List packages) throws CatalogDBException { super(interval, token, catalogManager); this.jobManager = catalogManager.getJobManager(); @@ -269,6 +275,13 @@ public ExecutionDaemon(int interval, String token, .append(QueryOptions.SORT, Arrays.asList(JobDBAdaptor.QueryParams.PRIORITY.key(), JobDBAdaptor.QueryParams.CREATION_DATE.key())) .append(QueryOptions.ORDER, QueryOptions.ASCENDING); + + if (CollectionUtils.isEmpty(packages)) { + this.packages = Collections.singletonList(ToolFactory.DEFAULT_PACKAGE); + } else { + this.packages = packages; + } + logger.info("Packages where to find tools/analyses: " + StringUtils.join(this.packages, ", ")); } @Override @@ -304,7 +317,7 @@ public void run() { } } - protected void checkJobs() { + public void checkJobs() { long pendingJobs = -1; long queuedJobs = -1; long runningJobs = -1; @@ -482,7 +495,7 @@ protected int checkPendingJob(Job job) { Tool tool; try { - tool = new ToolFactory().getTool(job.getTool().getId()); + tool = new ToolFactory().getTool(job.getTool().getId(), packages); } catch (Exception e) { logger.error(e.getMessage(), e); return abortJob(job, "Tool " + job.getTool().getId() + " not found", e); @@ -592,7 +605,7 @@ protected int checkPendingJob(Job job) { } protected void checkToolExecutionPermission(Job job) throws Exception { - Tool tool = new ToolFactory().getTool(job.getTool().getId()); + Tool tool = new ToolFactory().getTool(job.getTool().getId(), packages); if (catalogManager.getAuthorizationManager().isInstallationAdministrator(job.getUserId())) { // Installation administrator user can run everything diff --git a/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/MonitorParentDaemon.java b/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/MonitorParentDaemon.java index 8fcd7a59c18..05048323856 100644 --- a/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/MonitorParentDaemon.java +++ b/opencga-master/src/main/java/org/opencb/opencga/master/monitor/daemons/MonitorParentDaemon.java @@ -71,4 +71,8 @@ static String getJobTemporaryFolderName(long jobId) { return "J_" + jobId; } + public MonitorParentDaemon setBatchExecutor(BatchExecutor batchExecutor) { + this.batchExecutor = batchExecutor; + return this; + } } From 266b3715645de56cc83fa1cf4a7c15b072dedc7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Mon, 13 Nov 2023 22:36:02 +0100 Subject: [PATCH 11/18] core: add ignore annotation for packages in configuration, #TASK-4158 --- .../main/java/org/opencb/opencga/core/config/Analysis.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java index fbd9ab5e7fd..e139b294e94 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java @@ -16,11 +16,13 @@ package org.opencb.opencga.core.config; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + import java.util.ArrayList; import java.util.List; import java.util.Map; - +@JsonIgnoreProperties({"packages"}) public class Analysis { private List packages; From 4c25393ede8b709b760d35dbdbd89347638a6283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Tue, 14 Nov 2023 07:46:46 +0100 Subject: [PATCH 12/18] core: fix configuration, #TASK-4158 --- .../src/main/java/org/opencb/opencga/core/config/Analysis.java | 1 - 1 file changed, 1 deletion(-) diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java index e139b294e94..7831a33a517 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.Map; -@JsonIgnoreProperties({"packages"}) public class Analysis { private List packages; From adfa52c854d27fd4018e9e83d7b807c40d0401dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Tue, 14 Nov 2023 10:41:09 +0100 Subject: [PATCH 13/18] core: remove unused imports, #TASK-4158 --- .../src/main/java/org/opencb/opencga/core/config/Analysis.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java index 7831a33a517..6eb2beecb49 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Analysis.java @@ -16,11 +16,8 @@ package org.opencb.opencga.core.config; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - import java.util.ArrayList; import java.util.List; -import java.util.Map; public class Analysis { From a2de5e2d16f782a24cacb173cbbee089e9d44dd9 Mon Sep 17 00:00:00 2001 From: pfurio Date: Wed, 15 Nov 2023 10:30:34 +0100 Subject: [PATCH 14/18] catalog: generate method to get effective permissions, #TASK-4158 --- .../authorization/AuthorizationDBAdaptor.java | 3 + .../authorization/AuthorizationManager.java | 22 ++ .../CatalogAuthorizationManager.java | 20 ++ .../mongodb/AuthorizationMongoDBAdaptor.java | 200 ++++++++++++++++++ .../catalog/managers/AdminManager.java | 40 ++++ .../catalog/managers/CatalogManagerTest.java | 3 +- .../opencga/core/api/ParamConstants.java | 7 + .../org/opencb/opencga/core/models/Acl.java | 108 ++++++++++ .../opencga/core/models/AclEntryList.java | 2 +- .../opencga/core/models/common/Enums.java | 76 +++++++ .../core/models/study/StudyPermissions.java | 8 + .../opencga/core/response/OpenCGAResult.java | 4 + .../server/rest/admin/AdminWSServer.java | 19 ++ 13 files changed, 510 insertions(+), 2 deletions(-) create mode 100644 opencga-core/src/main/java/org/opencb/opencga/core/models/Acl.java diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationDBAdaptor.java index 58d9cd54ec5..bfe3aae8302 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationDBAdaptor.java @@ -20,6 +20,7 @@ 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.core.models.Acl; import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.study.PermissionRule; @@ -65,6 +66,8 @@ > OpenCGAResult> get(List resourceIds, L Map> userGroups, Enums.Resource entry, Class clazz) throws CatalogException; + List effectivePermissions(long studyUid, List resourceIdList, Enums.Resource entry) throws CatalogDBException; + /** * Remove all the Acls defined for the member in the resource for the study. * diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationManager.java index 550faae0ea0..4cfa865258d 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/AuthorizationManager.java @@ -18,6 +18,7 @@ import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.utils.ParamUtils; +import org.opencb.opencga.core.models.Acl; import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisPermissions; import org.opencb.opencga.core.models.cohort.CohortPermissions; @@ -138,6 +139,27 @@ void checkFamilyPermission(long studyId, long familyId, String userId, FamilyPer void checkClinicalAnalysisPermission(long studyId, long analysisId, String userId, ClinicalAnalysisPermissions permission) throws CatalogException; + default List getEffectivePermissions(long studyUid, String resourceId, Enums.Resource resource) throws CatalogException { + return getEffectivePermissions(studyUid, Collections.singletonList(resourceId), Collections.emptyList(), resource); + } + + default List getEffectivePermissions(long studyUid, List resourceIdList, Enums.Resource resource) throws CatalogException { + return getEffectivePermissions(studyUid, resourceIdList, Collections.emptyList(), resource); + } + + default List getEffectivePermissions(long studyUid, String resourceId, String permission, Enums.Resource resource) + throws CatalogException { + return getEffectivePermissions(studyUid, Collections.singletonList(resourceId), Collections.singletonList(permission), resource); + } + + default List getEffectivePermissions(long studyUid, List resourceIdList, String permission, Enums.Resource resource) + throws CatalogException { + return getEffectivePermissions(studyUid, resourceIdList, Collections.singletonList(permission), resource); + } + + List getEffectivePermissions(long studyUid, List resourceIdList, List permission, Enums.Resource resource) + throws CatalogException; + //------------------------- Study ACL ----------------------------- /** diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java index 8a34fc6e17b..628b6a8e1b8 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java @@ -27,6 +27,7 @@ import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.config.Configuration; +import org.opencb.opencga.core.models.Acl; import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.clinical.ClinicalAnalysisPermissions; import org.opencb.opencga.core.models.cohort.CohortPermissions; @@ -434,6 +435,25 @@ public void checkClinicalAnalysisPermission(long studyId, long analysisId, Strin throw CatalogAuthorizationException.deny(userId, permission.toString(), "ClinicalAnalysis", analysisId, null); } + @Override + public List getEffectivePermissions(long studyUid, List resourceIdList, List permissions, Enums.Resource resource) + throws CatalogException { + List acls = aclDBAdaptor.effectivePermissions(studyUid, resourceIdList, resource); + if (CollectionUtils.isNotEmpty(permissions)) { + // Filter out other permissions + for (Acl acl : acls) { + List permissionList = new ArrayList<>(permissions.size()); + for (Acl.Permission aclPermission : acl.getPermissions()) { + if (permissions.contains(aclPermission.getId())) { + permissionList.add(aclPermission); + } + } + acl.setPermissions(permissionList); + } + } + return acls; + } + @Override public OpenCGAResult> getAllStudyAcls(String userId, long studyId) throws CatalogException { 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..3e27a70a6f1 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 @@ -39,7 +39,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.core.api.ParamConstants; +import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.Configuration; +import org.opencb.opencga.core.models.Acl; import org.opencb.opencga.core.models.AclEntry; import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.common.Enums; @@ -263,6 +266,203 @@ private EntryPermission internalGet(long resourceId, List membersList, E return entryPermission; } + /** + * Fetch the effective permissions of every user. + * + * @param studyUid Study where the resources belong. + * @param resourceIdList List of resource ids. + * @param entry Entity where the query will be performed. + * @return A list of ACL object. + * @throws CatalogDBException CatalogDBException if the study or the resourcers cannot be found. + */ + public List effectivePermissions(long studyUid, List resourceIdList, Enums.Resource entry) throws CatalogDBException { + // Get groups and array of ACLs from the study document + MongoDBCollection studyCollection = dbCollectionMap.get(Enums.Resource.STUDY).get(0); + Bson studyQuery = Filters.eq(PRIVATE_UID, studyUid); + Bson studyProjection = Projections.include(StudyDBAdaptor.QueryParams.GROUPS.key(), QueryParams.ACL.key()); + DataResult studyResult = studyCollection.find(studyQuery, studyProjection, null); + if (studyResult.getNumMatches() == 0) { + throw new CatalogDBException("Study uid '" + studyUid + "' not found"); + } + Document studyDocument = studyResult.first(); + + Map> groupsMap = getGroupUsersMap(studyDocument); + Map> studyUserPermissionsMap = extractUserPermissionsMap(groupsMap, studyDocument); + + // Retrieve ACL list for the resources requested + MongoDBCollection collection = dbCollectionMap.get(entry).get(0); + Bson query = Filters.and( + Filters.eq(PRIVATE_STUDY_UID, studyUid), + Filters.eq(ID, resourceIdList) + ); + Bson projection = Projections.include(QueryParams.ID.key(), QueryParams.ACL.key()); + + logger.debug("Get Acl: {}", query.toBsonDocument()); + DataResult dataResult = collection.find(query, projection, null); + Map dataResultMap = new HashMap<>(); + for (Document result : dataResult.getResults()) { + dataResultMap.put(result.getString(ID), result); + } + + // Process resourceIdList in order + List aclList = new ArrayList<>(resourceIdList.size()); + for (String resourceId : resourceIdList) { + if (!dataResultMap.containsKey(resourceId)) { + throw new CatalogDBException("Resource id '" + resourceId + "' not found."); + } + Document resourceDocument = dataResultMap.get(resourceId); + Map> resourceUserPermissionsMap = extractUserPermissionsMap(groupsMap, resourceDocument); + Acl acl = convertPermissionsToAcl(studyUserPermissionsMap, resourceUserPermissionsMap, resourceId, entry); + aclList.add(acl); + } + + return aclList; + } + + private Acl convertPermissionsToAcl(Map> studyUserPermissionsMap, + Map> resourceUserPermissionsMap, String id, Enums.Resource resource) { + // Init permission map + Map> permissionMap = new HashMap<>(); + List resourcePermissions = resource.getFullPermissionList(); + for (String permission : resource.getFullPermissionList()) { + permissionMap.put(permission, new HashSet<>()); + } + permissionMap.put("NONE", new HashSet<>()); + + // Store permissions at the resource level + for (Map.Entry> entry : resourceUserPermissionsMap.entrySet()) { + for (String permission : entry.getValue()) { + permissionMap.get(permission).add(entry.getKey()); + } + } + + // List of correspondence of study permissions + Map studyPermissionsToResourcePermissionsMap = new HashMap<>(); + for (String resourcePermission : resourcePermissions) { + String studyPermission = resource.toStudyPermission(resourcePermission); + studyPermissionsToResourcePermissionsMap.put(studyPermission, resourcePermission); + } + studyPermissionsToResourcePermissionsMap.put("NONE", "NONE"); + + // Iterate and only store permissions at the study level if no permissions were given at the resource level + for (Map.Entry> entry : studyUserPermissionsMap.entrySet()) { + String userId = entry.getKey(); + Set studyPermissions = entry.getValue(); + if (resourceUserPermissionsMap.get(userId).isEmpty()) { + // Loop over all study permissions given to the user + for (String studyPermission : studyPermissions) { + if (studyPermissionsToResourcePermissionsMap.containsKey(studyPermission)) { + String resourcePermission = studyPermissionsToResourcePermissionsMap.get(studyPermission); + permissionMap.get(resourcePermission).add(userId); + } + } + } + } + + // Generate ACL object + List permissionList = new ArrayList<>(permissionMap.size()); + for (Map.Entry> entry : permissionMap.entrySet()) { + permissionList.add(new Acl.Permission(entry.getKey(), new ArrayList<>(entry.getValue()))); + } + + return new Acl(id, resource.name(), permissionList, TimeUtils.getDate().getTime()); + } + + private Map> extractUserPermissionsMap(Map> groupsMap, Document studyDocument) { + Set allUsers = groupsMap.get(ParamConstants.MEMBERS_GROUP); + + // Map of userId - List of study permissions + Map> studyUserPermissionsMap = new HashMap<>(); + for (String userId : allUsers) { + studyUserPermissionsMap.put(userId, new HashSet<>()); + } + + // Group ACLs + List aclList = studyDocument.getList(QueryParams.ACL.key(), String.class); + List personalAcls = new ArrayList<>(aclList.size()); + List groupAcls = new ArrayList<>(aclList.size()); + List anonymousAcls = new ArrayList<>(aclList.size()); + + for (String acl : aclList) { + if (acl.startsWith("@")) { + groupAcls.add(acl); + } else if (acl.startsWith(ParamConstants.ANONYMOUS_USER_ID + INTERNAL_DELIMITER)) { + anonymousAcls.add(acl); + } else { + personalAcls.add(acl); + } + } + + boolean simplifyPermissions = configuration.getOptimizations() != null && configuration.getOptimizations().isSimplifyPermissions(); + Set userIdsWithPermissions = new HashSet<>(); + + // Personal ACLs + for (String acl : personalAcls) { + String[] split = acl.split(INTERNAL_DELIMITER, 2); + String userId = split[0]; + String permission = split[1]; + if (studyUserPermissionsMap.containsKey(userId)) { + throw new IllegalStateException("User id '" + userId + "' with permissions was not found in the '" + + ParamConstants.MEMBERS_GROUP + "' group."); + } + userIdsWithPermissions.add(userId); + studyUserPermissionsMap.get(userId).add(permission); + } + + // Anonymous ACLs + List anonymousPermissions = new ArrayList<>(anonymousAcls.size()); + for (String acl : anonymousAcls) { + String[] split = acl.split(INTERNAL_DELIMITER, 2); + String permission = split[1]; + anonymousPermissions.add(permission); + } + // Assign anonymous permissions + for (Map.Entry> tmpEntry : studyUserPermissionsMap.entrySet()) { + // Only add permissions if "simplifyPermissions" or if the user hasn't been given any acls personally + if (simplifyPermissions || tmpEntry.getValue().isEmpty()) { + tmpEntry.getValue().addAll(anonymousPermissions); + userIdsWithPermissions.add(tmpEntry.getKey()); + } + } + + // Group ACLs + Map> groupPermissions = new HashMap<>(); + for (String acl : groupAcls) { + String[] split = acl.split(INTERNAL_DELIMITER, 2); + String groupId = split[0]; + String permission = split[1]; + + if (!groupPermissions.containsKey(groupId)) { + groupPermissions.put(groupId, new LinkedList<>()); + } + groupPermissions.get(groupId).add(permission); + } + // Assign group permissions + for (Map.Entry> tmpEntry : groupPermissions.entrySet()) { + String groupId = tmpEntry.getKey(); + List tmpPermissionList = tmpEntry.getValue(); + + for (String userId : groupsMap.get(groupId)) { + if (simplifyPermissions || !userIdsWithPermissions.contains(userId)) { + studyUserPermissionsMap.get(userId).addAll(tmpPermissionList); + } + } + } + return studyUserPermissionsMap; + } + + private static Map> getGroupUsersMap(Document studyDocument) { + // Generate a map of group - set of userIds + Map> groupsMap = new HashMap<>(); + List groups = studyDocument.getList(StudyDBAdaptor.QueryParams.GROUPS.key(), Document.class); + for (Document group : groups) { + String groupId = group.getString(ID); + List userIds = group.getList("userIds", String.class); + groupsMap.put(groupId, new HashSet<>(userIds)); + } + return groupsMap; + } + static class EntryPermission { /** * Entry id. diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java index b57426ebf04..b3305264a5b 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java @@ -1,6 +1,7 @@ package org.opencb.opencga.catalog.managers; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.time.StopWatch; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; @@ -12,6 +13,7 @@ import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.config.Configuration; +import org.opencb.opencga.core.models.Acl; import org.opencb.opencga.core.models.audit.AuditRecord; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.study.Group; @@ -22,7 +24,10 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; public class AdminManager extends AbstractManager { @@ -171,4 +176,39 @@ public OpenCGAResult syncRemoteGroups(String userId, List remoteG } } + public OpenCGAResult getEffectivePermissions(String studyStr, List entryIdList, List permissionList, + String category, String token) throws CatalogException { + StopWatch stopWatch = StopWatch.createStarted(); + ObjectMap auditParams = new ObjectMap() + .append("studyStr", studyStr) + .append("entryIdList", entryIdList) + .append("permissionList", permissionList) + .append("category", category) + .append("token", token); + String userId = catalogManager.getUserManager().getUserId(token); + try { + Study study = catalogManager.getStudyManager().resolveId(studyStr, userId); + authorizationManager.checkIsOwnerOrAdmin(study.getUid(), userId); + + ParamUtils.checkParameter(category, "category"); + ParamUtils.checkObj(entryIdList, "entry id list"); + + Enums.Resource resource; + try { + resource = Enums.Resource.valueOf(category.toUpperCase()); + } catch (IllegalArgumentException e) { + String allowedResources = Arrays.stream(Enums.Resource.values()).map(Enum::name).collect(Collectors.joining(", ")); + throw new CatalogException("Unexpected category '" + category + "' passed. Allowed categories are: " + allowedResources); + } + List effectivePermissions = authorizationManager.getEffectivePermissions(study.getUid(), entryIdList, permissionList, + resource); + auditManager.audit(userId, Enums.Action.FETCH_ACLS, Enums.Resource.STUDY, "", "", "", "", auditParams, + new AuditRecord.Status(AuditRecord.Status.Result.SUCCESS)); + return new OpenCGAResult<>((int) stopWatch.getTime(TimeUnit.MILLISECONDS), effectivePermissions); + } catch (CatalogException e) { + auditManager.audit(userId, Enums.Action.FETCH_ACLS, Enums.Resource.STUDY, "", "", "", "", auditParams, + new AuditRecord.Status(AuditRecord.Status.Result.ERROR, e.getError())); + throw e; + } + } } diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java index 8e468806d04..d4cb567f6fa 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java @@ -1847,7 +1847,6 @@ public void testForceDeleteVariableSetWithAnnotations() throws CatalogException } } - @Test public void generateCohortFromSampleQuery() throws CatalogException, IOException { String studyId = "user@1000G:phase1"; @@ -1870,4 +1869,6 @@ public void generateCohortFromSampleQuery() throws CatalogException, IOException assertEquals("description", myCohort.getStatus().getDescription()); } + // Effective permissions testing + } \ No newline at end of file diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java b/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java index ebcfa07153c..60a6034506c 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/api/ParamConstants.java @@ -38,6 +38,10 @@ public class ParamConstants { public static final String RELEASE_DESCRIPTION = "Release when it was created"; public static final String INTERNAL_STATUS_PARAM = "internalStatus"; public static final String INTERNAL_STATUS_DESCRIPTION = "Filter by internal status"; + public static final String ENTRY_ID_LIST_DESCRIPTION = "Comma separated list of entry ids."; + public static final String ENTRY_ID_LIST = "entryIds"; + public static final String PERMISSION_LIST_DESCRIPTION = "Comma separated list of permissions to be retrieved."; + public static final String PERMISSION_LIST = "permissions"; private static final String REGEX_SUPPORT = ". Also admits basic regular expressions using the operator '~', " + "i.e. '~{perl-regex}' e.g. '~value' for case sensitive, '~/value/i' for case insensitive search."; @Deprecated // Use INTERNAL_VARIANT_INDEX_STATUS_PARAM @@ -87,6 +91,9 @@ public class ParamConstants { public static final String OPENCGA_TOKEN_CLI_PARAM = "--opencga-token"; + public static final String CATEGORY = "category"; + public static final String CATEGORY_DESCRIPTION = "Category corresponding to the id's provided."; + // --------------------------------------------- public static final String FORCE = "force"; public static final String ANNOTATION_DOC_URL = "http://docs.opencb.org/display/opencga/AnnotationSets+1.4.0"; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/Acl.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/Acl.java new file mode 100644 index 00000000000..0552db21f37 --- /dev/null +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/Acl.java @@ -0,0 +1,108 @@ +package org.opencb.opencga.core.models; + +import java.util.List; + +public class Acl { + + private String id; + private String type; + private List permissions; + private long timestamp; + + public Acl() { + } + + public Acl(String id, String type, List permissions, long timestamp) { + this.id = id; + this.type = type; + this.permissions = permissions; + this.timestamp = timestamp; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Acl{"); + sb.append("id='").append(id).append('\''); + sb.append(", type='").append(type).append('\''); + sb.append(", permissions=").append(permissions); + sb.append(", timestamp=").append(timestamp); + sb.append('}'); + return sb.toString(); + } + + public String getId() { + return id; + } + + public Acl setId(String id) { + this.id = id; + return this; + } + + public String getType() { + return type; + } + + public Acl setType(String type) { + this.type = type; + return this; + } + + public List getPermissions() { + return permissions; + } + + public Acl setPermissions(List permissions) { + this.permissions = permissions; + return this; + } + + public long getTimestamp() { + return timestamp; + } + + public Acl setTimestamp(long timestamp) { + this.timestamp = timestamp; + return this; + } + + public static class Permission { + private String id; + private List userIds; + + public Permission() { + } + + public Permission(String id, List userIds) { + this.id = id; + this.userIds = userIds; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Permission{"); + sb.append("id='").append(id).append('\''); + sb.append(", userIds=").append(userIds); + sb.append('}'); + return sb.toString(); + } + + public String getId() { + return id; + } + + public Permission setId(String id) { + this.id = id; + return this; + } + + public List getUserIds() { + return userIds; + } + + public Permission setUserIds(List userIds) { + this.userIds = userIds; + return this; + } + } +} diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/AclEntryList.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/AclEntryList.java index 4d221ade795..393cf86549f 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/AclEntryList.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/AclEntryList.java @@ -21,7 +21,7 @@ public AclEntryList setId(String id) { @Override public String toString() { - final StringBuilder sb = new StringBuilder("SampleAclEntryList{"); + final StringBuilder sb = new StringBuilder("AclEntryList{"); sb.append("id='").append(id).append('\''); sb.append(", acl=").append(acl); sb.append('}'); diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/common/Enums.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/common/Enums.java index 764a07b409c..a2b8262fe3d 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/common/Enums.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/common/Enums.java @@ -105,6 +105,82 @@ public List getFullPermissionList() { return Collections.emptyList(); } } + + public String toStudyPermission(String permission) { + switch (this) { + case SAMPLE: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.SAMPLE).name(); + case FILE: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.FILE).name(); + case COHORT: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.COHORT).name(); + case INDIVIDUAL: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.INDIVIDUAL).name(); + case FAMILY: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.FAMILY).name(); + case JOB: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.JOB).name(); + case DISEASE_PANEL: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.DISEASE_PANEL).name(); + case CLINICAL_ANALYSIS: + return StudyPermissions.Permissions.getStudyPermission(permission, StudyPermissions.CLINICAL_ANALYSIS).name(); + case STUDY: + default: + return permission; + } + } + + public String fromStudyPermission(String permission) { + StudyPermissions.Permissions studyPermission = StudyPermissions.Permissions.valueOf(permission); + switch (this) { + case SAMPLE: + if (studyPermission.getType() == StudyPermissions.SAMPLE) { + return studyPermission.getPermission(); + } + break; + case FILE: + if (studyPermission.getType() == StudyPermissions.FILE) { + return studyPermission.getPermission(); + } + break; + case COHORT: + if (studyPermission.getType() == StudyPermissions.COHORT) { + return studyPermission.getPermission(); + } + break; + case INDIVIDUAL: + if (studyPermission.getType() == StudyPermissions.INDIVIDUAL) { + return studyPermission.getPermission(); + } + break; + case FAMILY: + if (studyPermission.getType() == StudyPermissions.FAMILY) { + return studyPermission.getPermission(); + } + break; + case JOB: + if (studyPermission.getType() == StudyPermissions.JOB) { + return studyPermission.getPermission(); + } + break; + case DISEASE_PANEL: + if (studyPermission.getType() == StudyPermissions.DISEASE_PANEL) { + return studyPermission.getPermission(); + } + break; + case CLINICAL_ANALYSIS: + if (studyPermission.getType() == StudyPermissions.CLINICAL_ANALYSIS) { + return studyPermission.getPermission(); + } + break; + case STUDY: + default: + return permission; + } + throw new IllegalArgumentException("Study permission '" + permission + "' does not seem to have an equivalent valid permission" + + " for the entity '" + this + "'"); + } + } public enum Action { 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..022b878ab6c 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 @@ -132,6 +132,14 @@ public static Permissions getStudyPermission(String permission, int type) { return map.get(permission + "-" + type); } + public String getPermission() { + return permission; + } + + public int getType() { + return type; + } + public List getImplicitPermissions() { return implicitPermissions; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/response/OpenCGAResult.java b/opencga-core/src/main/java/org/opencb/opencga/core/response/OpenCGAResult.java index fb331f8a326..59af48fbef8 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/response/OpenCGAResult.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/response/OpenCGAResult.java @@ -30,6 +30,10 @@ public class OpenCGAResult extends DataResult { public OpenCGAResult() { } + public OpenCGAResult(int time, List results) { + super(time, Collections.emptyList(), results.size(), results, results.size()); + } + public OpenCGAResult(int time, List events, int numResults, List results, long numMatches) { super(time, events, numResults, results, numMatches); } diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java index 835c9632af1..e8785cb7310 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java @@ -32,6 +32,7 @@ import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.exceptions.VersionException; +import org.opencb.opencga.core.models.Acl; import org.opencb.opencga.core.models.admin.*; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.job.Job; @@ -154,6 +155,24 @@ public Response remoteImport(@ApiParam(value = "JSON containing the parameters", } } + @GET + @Path("/users/permissions") + @ApiOperation(value = "User permissions", notes = "Effective permissions assigned to the users for a given list of entries.", + response = Acl.class) + public Response effectivePermissions( + @ApiParam(value = ParamConstants.STUDY_DESCRIPTION) @QueryParam(ParamConstants.STUDY_PARAM) String studyStr, + @ApiParam(value = ParamConstants.ENTRY_ID_LIST_DESCRIPTION) @QueryParam(ParamConstants.ENTRY_ID_LIST) String entryIdList, + @ApiParam(value = ParamConstants.PERMISSION_LIST_DESCRIPTION) @QueryParam(ParamConstants.PERMISSION_LIST) String permissionList, + @ApiParam(value = ParamConstants.CATEGORY_DESCRIPTION) @QueryParam(ParamConstants.CATEGORY) String category) { + try { + return createOkResponse(catalogManager.getAdminManager().getEffectivePermissions(studyStr, getIdList(entryIdList), + getIdListOrEmpty(permissionList), category, token)); + } catch (Exception e) { + return createErrorResponse(e); + } + + } + @POST @Path("/users/{user}/groups/update") @Consumes(MediaType.APPLICATION_JSON) From 6af436b3880aa567838d7e13284a0151913934a7 Mon Sep 17 00:00:00 2001 From: pfurio Date: Wed, 15 Nov 2023 14:41:50 +0100 Subject: [PATCH 15/18] catalog: test effective permission implementation, #TASK-4158 --- .../CatalogAuthorizationManager.java | 10 + .../mongodb/AuthorizationMongoDBAdaptor.java | 178 ++++++++++------- .../catalog/managers/AdminManager.java | 12 +- .../catalog/managers/CatalogManagerTest.java | 187 ++++++++++++++++++ 4 files changed, 309 insertions(+), 78 deletions(-) diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java index 628b6a8e1b8..32296c96a43 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authorization/CatalogAuthorizationManager.java @@ -17,6 +17,7 @@ package org.opencb.opencga.catalog.auth.authorization; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.Query; import org.opencb.opencga.catalog.db.DBAdaptorFactory; import org.opencb.opencga.catalog.db.api.*; @@ -24,6 +25,7 @@ import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.exceptions.CatalogParameterException; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.config.Configuration; @@ -438,6 +440,14 @@ public void checkClinicalAnalysisPermission(long studyId, long analysisId, Strin @Override public List getEffectivePermissions(long studyUid, List resourceIdList, List permissions, Enums.Resource resource) throws CatalogException { + HashSet resourcePermissions = new HashSet<>(resource.getFullPermissionList()); + for (String permission : permissions) { + if (!resourcePermissions.contains(permission)) { + throw new CatalogParameterException("Permission '" + permission + "' does not correspond with any of the available" + + " permissions. This is the full list of possible permissions: " + StringUtils.join(resourcePermissions, ", ")); + } + } + List acls = aclDBAdaptor.effectivePermissions(studyUid, resourceIdList, resource); if (CollectionUtils.isNotEmpty(permissions)) { // Filter out other permissions 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 3e27a70a6f1..c0eb3d707a4 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 @@ -20,6 +20,7 @@ import com.mongodb.client.model.Aggregates; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Projections; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.bson.Document; import org.bson.conversions.Bson; @@ -29,7 +30,6 @@ import org.opencb.commons.datastore.core.QueryParam; import org.opencb.commons.datastore.mongodb.MongoDBCollection; import org.opencb.commons.datastore.mongodb.MongoDBIterator; -import org.opencb.commons.utils.CollectionUtils; import org.opencb.opencga.catalog.auth.authorization.AuthorizationDBAdaptor; import org.opencb.opencga.catalog.auth.authorization.AuthorizationManager; import org.opencb.opencga.catalog.auth.authorization.CatalogAuthorizationManager; @@ -279,7 +279,8 @@ public List effectivePermissions(long studyUid, List resourceIdList // Get groups and array of ACLs from the study document MongoDBCollection studyCollection = dbCollectionMap.get(Enums.Resource.STUDY).get(0); Bson studyQuery = Filters.eq(PRIVATE_UID, studyUid); - Bson studyProjection = Projections.include(StudyDBAdaptor.QueryParams.GROUPS.key(), QueryParams.ACL.key()); + Bson studyProjection = Projections.include(StudyDBAdaptor.QueryParams.GROUPS.key(), QueryParams.ACL.key(), + StudyDBAdaptor.QueryParams.OWNER.key()); DataResult studyResult = studyCollection.find(studyQuery, studyProjection, null); if (studyResult.getNumMatches() == 0) { throw new CatalogDBException("Study uid '" + studyUid + "' not found"); @@ -293,7 +294,7 @@ public List effectivePermissions(long studyUid, List resourceIdList MongoDBCollection collection = dbCollectionMap.get(entry).get(0); Bson query = Filters.and( Filters.eq(PRIVATE_STUDY_UID, studyUid), - Filters.eq(ID, resourceIdList) + Filters.in(ID, resourceIdList) ); Bson projection = Projections.include(QueryParams.ID.key(), QueryParams.ACL.key()); @@ -312,22 +313,23 @@ public List effectivePermissions(long studyUid, List resourceIdList } Document resourceDocument = dataResultMap.get(resourceId); Map> resourceUserPermissionsMap = extractUserPermissionsMap(groupsMap, resourceDocument); - Acl acl = convertPermissionsToAcl(studyUserPermissionsMap, resourceUserPermissionsMap, resourceId, entry); + Acl acl = convertPermissionsToAcl(groupsMap, studyUserPermissionsMap, resourceUserPermissionsMap, resourceId, entry); aclList.add(acl); } return aclList; } - private Acl convertPermissionsToAcl(Map> studyUserPermissionsMap, + private Acl convertPermissionsToAcl(Map> groupsMap, Map> studyUserPermissionsMap, Map> resourceUserPermissionsMap, String id, Enums.Resource resource) { + Set adminUsers = groupsMap.get(ParamConstants.ADMINS_GROUP); + // Init permission map Map> permissionMap = new HashMap<>(); List resourcePermissions = resource.getFullPermissionList(); for (String permission : resource.getFullPermissionList()) { permissionMap.put(permission, new HashSet<>()); } - permissionMap.put("NONE", new HashSet<>()); // Store permissions at the resource level for (Map.Entry> entry : resourceUserPermissionsMap.entrySet()) { @@ -339,10 +341,14 @@ private Acl convertPermissionsToAcl(Map> studyUserPermission // List of correspondence of study permissions Map studyPermissionsToResourcePermissionsMap = new HashMap<>(); for (String resourcePermission : resourcePermissions) { - String studyPermission = resource.toStudyPermission(resourcePermission); + String studyPermission; + if (!"NONE".equals(resourcePermission)) { + studyPermission = resource.toStudyPermission(resourcePermission); + } else { + studyPermission = "NONE"; + } studyPermissionsToResourcePermissionsMap.put(studyPermission, resourcePermission); } - studyPermissionsToResourcePermissionsMap.put("NONE", "NONE"); // Iterate and only store permissions at the study level if no permissions were given at the resource level for (Map.Entry> entry : studyUserPermissionsMap.entrySet()) { @@ -359,96 +365,111 @@ private Acl convertPermissionsToAcl(Map> studyUserPermission } } + Set usersWithNoAccess = new HashSet<>(studyUserPermissionsMap.keySet()); + usersWithNoAccess.removeAll(adminUsers); // Generate ACL object - List permissionList = new ArrayList<>(permissionMap.size()); - for (Map.Entry> entry : permissionMap.entrySet()) { - permissionList.add(new Acl.Permission(entry.getKey(), new ArrayList<>(entry.getValue()))); + List permissionList = new ArrayList<>(resourcePermissions.size()); + for (String resourcePermission : resourcePermissions) { + Set userIdSet = permissionMap.get(resourcePermission); + if (!"NONE".equals(resourcePermission)) { + // Remove users with access from usersWithNoAccess set + usersWithNoAccess.removeAll(userIdSet); + // Add admin users to users with this permission + userIdSet.addAll(adminUsers); + + permissionList.add(new Acl.Permission(resourcePermission, new ArrayList<>(userIdSet))); + } } + permissionList.add(new Acl.Permission("NONE", new ArrayList<>(usersWithNoAccess))); return new Acl(id, resource.name(), permissionList, TimeUtils.getDate().getTime()); } - private Map> extractUserPermissionsMap(Map> groupsMap, Document studyDocument) { + private Map> extractUserPermissionsMap(Map> groupsMap, Document document) { Set allUsers = groupsMap.get(ParamConstants.MEMBERS_GROUP); - // Map of userId - List of study permissions - Map> studyUserPermissionsMap = new HashMap<>(); + // Map of userId - List of permissions + Map> userPermissionsMap = new HashMap<>(); for (String userId : allUsers) { - studyUserPermissionsMap.put(userId, new HashSet<>()); + userPermissionsMap.put(userId, new HashSet<>()); } // Group ACLs - List aclList = studyDocument.getList(QueryParams.ACL.key(), String.class); - List personalAcls = new ArrayList<>(aclList.size()); - List groupAcls = new ArrayList<>(aclList.size()); - List anonymousAcls = new ArrayList<>(aclList.size()); - - for (String acl : aclList) { - if (acl.startsWith("@")) { - groupAcls.add(acl); - } else if (acl.startsWith(ParamConstants.ANONYMOUS_USER_ID + INTERNAL_DELIMITER)) { - anonymousAcls.add(acl); - } else { - personalAcls.add(acl); + List aclList = document.getList(QueryParams.ACL.key(), String.class); + if (CollectionUtils.isNotEmpty(aclList)) { + List personalAcls = new ArrayList<>(aclList.size()); + List groupAcls = new ArrayList<>(aclList.size()); + List anonymousAcls = new ArrayList<>(aclList.size()); + + for (String acl : aclList) { + if (acl.startsWith("@")) { + groupAcls.add(acl); + } else if (acl.startsWith(ParamConstants.ANONYMOUS_USER_ID + INTERNAL_DELIMITER)) { + anonymousAcls.add(acl); + } else { + personalAcls.add(acl); + } } - } - boolean simplifyPermissions = configuration.getOptimizations() != null && configuration.getOptimizations().isSimplifyPermissions(); - Set userIdsWithPermissions = new HashSet<>(); - - // Personal ACLs - for (String acl : personalAcls) { - String[] split = acl.split(INTERNAL_DELIMITER, 2); - String userId = split[0]; - String permission = split[1]; - if (studyUserPermissionsMap.containsKey(userId)) { - throw new IllegalStateException("User id '" + userId + "' with permissions was not found in the '" - + ParamConstants.MEMBERS_GROUP + "' group."); + boolean simplifyPermissions = configuration.getOptimizations() != null + && configuration.getOptimizations().isSimplifyPermissions(); + Set userIdsWithPermissions = new HashSet<>(); + + // Personal ACLs + for (String acl : personalAcls) { + String[] split = acl.split(INTERNAL_DELIMITER, 2); + String userId = split[0]; + String permission = split[1]; + if (!userPermissionsMap.containsKey(userId)) { + throw new IllegalStateException("User id '" + userId + "' with permissions was not found in the '" + + ParamConstants.MEMBERS_GROUP + "' group."); + } + userIdsWithPermissions.add(userId); + userPermissionsMap.get(userId).add(permission); } - userIdsWithPermissions.add(userId); - studyUserPermissionsMap.get(userId).add(permission); - } - // Anonymous ACLs - List anonymousPermissions = new ArrayList<>(anonymousAcls.size()); - for (String acl : anonymousAcls) { - String[] split = acl.split(INTERNAL_DELIMITER, 2); - String permission = split[1]; - anonymousPermissions.add(permission); - } - // Assign anonymous permissions - for (Map.Entry> tmpEntry : studyUserPermissionsMap.entrySet()) { - // Only add permissions if "simplifyPermissions" or if the user hasn't been given any acls personally - if (simplifyPermissions || tmpEntry.getValue().isEmpty()) { - tmpEntry.getValue().addAll(anonymousPermissions); - userIdsWithPermissions.add(tmpEntry.getKey()); + // Anonymous ACLs + List anonymousPermissions = new ArrayList<>(anonymousAcls.size()); + for (String acl : anonymousAcls) { + String[] split = acl.split(INTERNAL_DELIMITER, 2); + String permission = split[1]; + anonymousPermissions.add(permission); + } + // Assign anonymous permissions + if (!anonymousPermissions.isEmpty()) { + for (Map.Entry> tmpEntry : userPermissionsMap.entrySet()) { + // Only add permissions if "simplifyPermissions" or if the user hasn't been given any acls personally + if (simplifyPermissions || tmpEntry.getValue().isEmpty()) { + tmpEntry.getValue().addAll(anonymousPermissions); + } + } } - } - // Group ACLs - Map> groupPermissions = new HashMap<>(); - for (String acl : groupAcls) { - String[] split = acl.split(INTERNAL_DELIMITER, 2); - String groupId = split[0]; - String permission = split[1]; - - if (!groupPermissions.containsKey(groupId)) { - groupPermissions.put(groupId, new LinkedList<>()); + // Group ACLs + Map> groupPermissions = new HashMap<>(); + for (String acl : groupAcls) { + String[] split = acl.split(INTERNAL_DELIMITER, 2); + String groupId = split[0]; + String permission = split[1]; + + if (!groupPermissions.containsKey(groupId)) { + groupPermissions.put(groupId, new LinkedList<>()); + } + groupPermissions.get(groupId).add(permission); } - groupPermissions.get(groupId).add(permission); - } - // Assign group permissions - for (Map.Entry> tmpEntry : groupPermissions.entrySet()) { - String groupId = tmpEntry.getKey(); - List tmpPermissionList = tmpEntry.getValue(); - - for (String userId : groupsMap.get(groupId)) { - if (simplifyPermissions || !userIdsWithPermissions.contains(userId)) { - studyUserPermissionsMap.get(userId).addAll(tmpPermissionList); + // Assign group permissions + for (Map.Entry> tmpEntry : groupPermissions.entrySet()) { + String groupId = tmpEntry.getKey(); + List tmpPermissionList = tmpEntry.getValue(); + + for (String userId : groupsMap.get(groupId)) { + if (simplifyPermissions || !userIdsWithPermissions.contains(userId)) { + userPermissionsMap.get(userId).addAll(tmpPermissionList); + } } } } - return studyUserPermissionsMap; + return userPermissionsMap; } private static Map> getGroupUsersMap(Document studyDocument) { @@ -460,6 +481,11 @@ private static Map> getGroupUsersMap(Document studyDocument) List userIds = group.getList("userIds", String.class); groupsMap.put(groupId, new HashSet<>(userIds)); } + + // Add owner to @admins group + String ownerId = studyDocument.getString(StudyDBAdaptor.QueryParams.OWNER.key()); + groupsMap.get(ParamConstants.ADMINS_GROUP).add(ownerId); + return groupsMap; } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java index b3305264a5b..89209769821 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/AdminManager.java @@ -9,6 +9,7 @@ import org.opencb.opencga.catalog.db.DBAdaptorFactory; import org.opencb.opencga.catalog.db.api.UserDBAdaptor; import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.exceptions.CatalogParameterException; import org.opencb.opencga.catalog.io.CatalogIOManager; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; @@ -25,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -176,6 +178,11 @@ public OpenCGAResult syncRemoteGroups(String userId, List remoteG } } + public OpenCGAResult getEffectivePermissions(String studyStr, List entryIdList, String category, String token) + throws CatalogException { + return getEffectivePermissions(studyStr, entryIdList, Collections.emptyList(), category, token); + } + public OpenCGAResult getEffectivePermissions(String studyStr, List entryIdList, List permissionList, String category, String token) throws CatalogException { StopWatch stopWatch = StopWatch.createStarted(); @@ -191,14 +198,15 @@ public OpenCGAResult getEffectivePermissions(String studyStr, List authorizationManager.checkIsOwnerOrAdmin(study.getUid(), userId); ParamUtils.checkParameter(category, "category"); - ParamUtils.checkObj(entryIdList, "entry id list"); + ParamUtils.checkNotEmptyArray(entryIdList, "entry id list"); Enums.Resource resource; try { resource = Enums.Resource.valueOf(category.toUpperCase()); } catch (IllegalArgumentException e) { String allowedResources = Arrays.stream(Enums.Resource.values()).map(Enum::name).collect(Collectors.joining(", ")); - throw new CatalogException("Unexpected category '" + category + "' passed. Allowed categories are: " + allowedResources); + throw new CatalogParameterException("Unexpected category '" + category + "' passed. Allowed categories are: " + + allowedResources); } List effectivePermissions = authorizationManager.getEffectivePermissions(study.getUid(), entryIdList, permissionList, resource); diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java index d4cb567f6fa..a0b4591538a 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java @@ -33,6 +33,7 @@ import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.common.TimeUtils; +import org.opencb.opencga.core.models.Acl; import org.opencb.opencga.core.models.AclEntry; import org.opencb.opencga.core.models.AclEntryList; import org.opencb.opencga.core.models.cohort.Cohort; @@ -1870,5 +1871,191 @@ public void generateCohortFromSampleQuery() throws CatalogException, IOException } // Effective permissions testing + @Test + public void getEffectivePermissionsNoAdmins() throws CatalogException { + catalogManager.getStudyManager().updateGroup(studyFqn, ParamConstants.MEMBERS_GROUP, ParamUtils.BasicUpdateAction.ADD, + new GroupUpdateParams(Collections.singletonList("user2")), token); + thrown.expect(CatalogAuthorizationException.class); + thrown.expectMessage("owners or administrative"); + catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Arrays.asList(s_1, s_2), Enums.Resource.SAMPLE.name(), + sessionIdUser2); + } + + @Test + public void getEffectivePermissionsMissingEntries() throws CatalogException { + thrown.expect(CatalogParameterException.class); + thrown.expectMessage("entry id list"); + catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Collections.emptyList(), "sampl", token); + } + + @Test + public void getEffectivePermissionsWrongCategory() throws CatalogException { + thrown.expect(CatalogParameterException.class); + thrown.expectMessage("category"); + catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Collections.singletonList(s_1), "sampl", token); + } + + @Test + public void getEffectivePermissionsWrongPermission() throws CatalogException { + thrown.expect(CatalogParameterException.class); + thrown.expectMessage("permission"); + catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Collections.singletonList(s_1), Collections.singletonList("VIE"), + Enums.Resource.SAMPLE.name(), token); + } + + @Test + public void getEffectivePermissions() throws CatalogException { + OpenCGAResult aclList = catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Collections.singletonList(s_1), + Collections.singletonList(SamplePermissions.VIEW.name()), Enums.Resource.SAMPLE.name(), token); + assertEquals(1, aclList.getNumResults()); + assertEquals(s_1, aclList.first().getId()); + assertEquals(1, aclList.first().getPermissions().size()); + assertEquals(SamplePermissions.VIEW.name(), aclList.first().getPermissions().get(0).getId()); + assertEquals(1, aclList.first().getPermissions().get(0).getUserIds().size()); + assertEquals("user", aclList.first().getPermissions().get(0).getUserIds().get(0)); + + aclList = catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Collections.singletonList(s_1), + Enums.Resource.SAMPLE.name(), token); + assertEquals(1, aclList.getNumResults()); + assertEquals(s_1, aclList.first().getId()); + assertEquals(8, aclList.first().getPermissions().size()); + for (Acl.Permission permission : aclList.first().getPermissions()) { + if ("NONE".equals(permission.getId())) { + assertTrue(permission.getUserIds().isEmpty()); + } else { + assertEquals(1, permission.getUserIds().size()); + assertEquals("user", permission.getUserIds().get(0)); + } + } + + generatePermissionScenario(); + aclList = catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Arrays.asList(s_1, s_2, s_3), + Enums.Resource.SAMPLE.name(), token); + assertEquals(3, aclList.getNumResults()); + assertPermissions("s_1", Arrays.asList("user", "user2", "user3", "user4", "user5", "user6", "user7"), + Arrays.asList("user", "user2", "user6", "user7"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6"), Arrays.asList("user", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6"), Collections.emptyList(), aclList.getResults().get(0)); + assertPermissions("s_2", Arrays.asList("user", "user2", "user4", "user5", "user6", "user7"), + Arrays.asList("user", "user2", "user6", "user7"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6"), Arrays.asList("user", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6"), Collections.singletonList("user3"), aclList.getResults().get(1)); + assertPermissions("s_3", Arrays.asList("user", "user2", "user3", "user5", "user6"), + Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user2", "user3", "user5", "user6"), Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user2", "user3", "user5", "user6"), Arrays.asList("user4", "user7"), aclList.getResults().get(2)); + + // Now, we grant view_only access to anonymous user + catalogManager.getStudyManager().updateAcl(studyFqn, ParamConstants.ANONYMOUS_USER_ID, new StudyAclParams(null, "view_only"), + ParamUtils.AclAction.SET, token); + + aclList = catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Arrays.asList(s_1, s_2, s_3), + Enums.Resource.SAMPLE.name(), token); + assertEquals(3, aclList.getNumResults()); + assertPermissions("s_1", Arrays.asList("user", "user2", "user3", "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), + Arrays.asList("user", "user2", "user6", "user7"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Collections.emptyList(), aclList.getResults().get(0)); + assertPermissions("s_2", Arrays.asList("user", "user2", "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), + Arrays.asList("user", "user2", "user6", "user7"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Collections.singletonList("user3"), aclList.getResults().get(1)); + assertPermissions("s_3", Arrays.asList("user", "user2", "user3", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), + Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user2", "user3", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user2", "user3", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user4", "user7"), aclList.getResults().get(2)); + } + + private void assertPermissions(String id, List view, List write, List delete, List viewAnnots, + List writeAnnots, List deleteAnnots, List viewVariants, List none, + Acl acl) { + assertEquals(id, acl.getId()); + for (Acl.Permission permission : acl.getPermissions()) { + SamplePermissions samplePermission = SamplePermissions.valueOf(permission.getId()); + switch (samplePermission) { + case NONE: + assertEquals(none.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(none)); + break; + case VIEW: + assertEquals(view.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(view)); + break; + case WRITE: + assertEquals(write.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(write)); + break; + case DELETE: + assertEquals(delete.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(delete)); + break; + case VIEW_ANNOTATIONS: + assertEquals(viewAnnots.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(viewAnnots)); + break; + case WRITE_ANNOTATIONS: + assertEquals(writeAnnots.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(writeAnnots)); + break; + case DELETE_ANNOTATIONS: + assertEquals(deleteAnnots.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(deleteAnnots)); + break; + case VIEW_VARIANTS: + assertEquals(viewVariants.size(), permission.getUserIds().size()); + assertTrue(permission.getUserIds().containsAll(viewVariants)); + break; + } + } + } + private void generatePermissionScenario() throws CatalogException { + /* + Study groups - permissions - users + @members - - user1 + @view - VIEW_ONLY (annots included) - user2, user7 + @write - VIEW_WRITE (annots included) - user3 + Study permissions to users + user4 - NONE + user5 - VIEW (annots included) + user6 - VIEW_WRITE (annots included) + user7 - NONE + + Sample s_1 permissions + ====================== + Group permissions + @view - WRITE + @write - NONE + User permissions + user3, user4 - VIEW + + Sample s_2 permissions + ====================== + Group permissions + @view - WRITE + @write - NONE + User permissions + user4 - VIEW + + Sample s_3 permissions + ====================== + No permissions assigned + */ + catalogManager.getUserManager().create("user4", "User Name", "mail@ebi.ac.uk", TestParamConstants.PASSWORD, "", null, Account.AccountType.FULL, opencgaToken); + catalogManager.getUserManager().create("user5", "User2 Name", "mail2@ebi.ac.uk", TestParamConstants.PASSWORD, "", null, Account.AccountType.FULL, opencgaToken); + catalogManager.getUserManager().create("user6", "User3 Name", "user.2@e.mail", TestParamConstants.PASSWORD, "ACME", null, Account.AccountType.FULL, opencgaToken); + catalogManager.getUserManager().create("user7", "User3 Name", "user.2@e.mail", TestParamConstants.PASSWORD, "ACME", null, Account.AccountType.FULL, opencgaToken); + + catalogManager.getStudyManager().createGroup(studyFqn, "@view", Arrays.asList("user2", "user7"), token); + catalogManager.getStudyManager().createGroup(studyFqn, "@write", Collections.singletonList("user3"), token); + + catalogManager.getStudyManager().updateAcl(studyFqn, "@view,user5", new StudyAclParams(null, "view_only"), ParamUtils.AclAction.SET, token); + catalogManager.getStudyManager().updateAcl(studyFqn, "@write,user6", new StudyAclParams(null, "analyst"), ParamUtils.AclAction.SET, token); + catalogManager.getStudyManager().updateAcl(studyFqn, "user4,user7", new StudyAclParams(null, null), ParamUtils.AclAction.SET, token); + + catalogManager.getSampleManager().updateAcl(studyFqn, Arrays.asList(s_1, s_2), "@view", new SampleAclParams(null, null, null, null, "WRITE"), ParamUtils.AclAction.SET, token); + catalogManager.getSampleManager().updateAcl(studyFqn, Arrays.asList(s_1, s_2), "@write", new SampleAclParams(null, null, null, null, null), ParamUtils.AclAction.SET, token); + catalogManager.getSampleManager().updateAcl(studyFqn, Arrays.asList(s_1, s_2), "user4", new SampleAclParams(null, null, null, null, "VIEW"), ParamUtils.AclAction.SET, token); + catalogManager.getSampleManager().updateAcl(studyFqn, Collections.singletonList(s_1), "user3", new SampleAclParams(null, null, null, null, "VIEW"), ParamUtils.AclAction.SET, token); + } } \ No newline at end of file From 2045340a2c68e5c137ab167e58df511a03bb7270 Mon Sep 17 00:00:00 2001 From: pfurio Date: Thu, 16 Nov 2023 09:39:56 +0100 Subject: [PATCH 16/18] catalog: test with simplifyPermissions configuration, #TASK-4158 --- .../catalog/managers/CatalogManagerTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java index a0b4591538a..b50d8452d4e 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java @@ -1964,6 +1964,24 @@ public void getEffectivePermissions() throws CatalogException { Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), Arrays.asList("user", "user2", "user3", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), Arrays.asList("user", "user2", "user3", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user4", "user7"), aclList.getResults().get(2)); + + catalogManager.getConfiguration().getOptimizations().setSimplifyPermissions(true); + aclList = catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Arrays.asList(s_1, s_2, s_3), + Enums.Resource.SAMPLE.name(), token); + assertEquals(3, aclList.getNumResults()); + assertPermissions("s_1", Arrays.asList("user", "user2", "user3", "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), + Arrays.asList("user", "user2", "user6", "user7"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Collections.emptyList(), aclList.getResults().get(0)); + assertPermissions("s_2", Arrays.asList("user", "user2", "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), + Arrays.asList("user", "user2", "user6", "user7"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Collections.singletonList("user3"), aclList.getResults().get(1)); + assertPermissions("s_3", Arrays.asList("user", "user2", "user3", "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), + Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user2", "user3", "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user", "user3", "user6"), Collections.singletonList("user"), + Arrays.asList("user", "user2", "user3", "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), Collections.emptyList(), aclList.getResults().get(2)); + } private void assertPermissions(String id, List view, List write, List delete, List viewAnnots, From 8c54a86d00c692fcdf2256d9b84fff9c4ad6d9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Thu, 16 Nov 2023 13:31:50 +0100 Subject: [PATCH 17/18] catalog: improve clinical analysis loading, #TASK-4610, #TASK-TASK-4158 --- .../managers/ClinicalAnalysisManager.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) 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 6f9cc944028..facacbfa62b 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 @@ -684,33 +684,30 @@ private void load(ClinicalAnalysis clinicalAnalysis, String study, String token) } } - List interpretationList = clinicalAnalysis.getSecondaryInterpretations(); + // Save primary and secondary interpretations for creating later + Interpretation primaryInterpretation = clinicalAnalysis.getInterpretation(); + clinicalAnalysis.setInterpretation(null); + + List secondaryInterpretations = clinicalAnalysis.getSecondaryInterpretations(); clinicalAnalysis.setSecondaryInterpretations(null); - Interpretation interpretation = clinicalAnalysis.getInterpretation(); - clinicalAnalysis.setInterpretation(null); - // Create Clinical Analysis + // Create clinical analysis ClinicalAnalysis caToCreate = ClinicalAnalysisCreateParams.of(clinicalAnalysis).toClinicalAnalysis(); caToCreate.setAnalyst(new ClinicalAnalyst()); catalogManager.getClinicalAnalysisManager().create(study, caToCreate, QueryOptions.empty(), token); - if (interpretation == null) { - interpretation = interpretationList.get(0); - interpretationList = interpretationList.subList(1, interpretationList.size()); + // Create primary interpretation + if (primaryInterpretation != null) { + primaryInterpretation.setId(null); + catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), primaryInterpretation, PRIMARY, + QueryOptions.empty(), token); } - // Add interpretations - interpretation.setId(null); - interpretation.setAnalyst(new ClinicalAnalyst()); - catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), interpretation, PRIMARY, QueryOptions.empty(), - token); - - for (int i = 0, interpretationListSize = interpretationList.size(); i < interpretationListSize; i++) { - Interpretation tmpInterpretation = interpretationList.get(i); - tmpInterpretation.setId(null); - tmpInterpretation.setAnalyst(new ClinicalAnalyst()); - catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), tmpInterpretation, SECONDARY, + // Create secondary interpretations + for (Interpretation secondaryInterpretation : secondaryInterpretations) { + secondaryInterpretation.setId(null); + catalogManager.getInterpretationManager().create(study, clinicalAnalysis.getId(), secondaryInterpretation, SECONDARY, QueryOptions.empty(), token); } } From 41f3ca068fd8fcd2cff5a80ec229797a995498fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20T=C3=A1rraga=20Gim=C3=A9nez?= Date: Fri, 17 Nov 2023 11:02:13 +0100 Subject: [PATCH 18/18] client: generate clients, #TASK-4158 --- opencga-client/src/main/R/R/AllGenerics.R | 20 +++++++++---------- .../src/main/R/R/Clinical-methods.R | 2 +- opencga-client/src/main/R/R/Cohort-methods.R | 2 +- opencga-client/src/main/R/R/Family-methods.R | 2 +- opencga-client/src/main/R/R/File-methods.R | 2 +- opencga-client/src/main/R/R/GA4GH-methods.R | 2 +- .../src/main/R/R/Individual-methods.R | 2 +- opencga-client/src/main/R/R/Job-methods.R | 2 +- opencga-client/src/main/R/R/Project-methods.R | 2 +- opencga-client/src/main/R/R/Sample-methods.R | 2 +- opencga-client/src/main/R/R/Study-methods.R | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/opencga-client/src/main/R/R/AllGenerics.R b/opencga-client/src/main/R/R/AllGenerics.R index 9430f009b50..ecfaa4384b4 100644 --- a/opencga-client/src/main/R/R/AllGenerics.R +++ b/opencga-client/src/main/R/R/AllGenerics.R @@ -5,42 +5,42 @@ setGeneric("userClient", function(OpencgaR, user, filterId, users, endpointName, # ############################################################################## ## ProjectClient -setGeneric("projectClient", function(OpencgaR, projects, project, endpointName, params=NULL, ...) +setGeneric("projectClient", function(OpencgaR, project, projects, endpointName, params=NULL, ...) standardGeneric("projectClient")) # ############################################################################## ## StudyClient -setGeneric("studyClient", function(OpencgaR, group, members, templateId, studies, study, variableSet, endpointName, params=NULL, ...) +setGeneric("studyClient", function(OpencgaR, study, variableSet, templateId, studies, group, members, endpointName, params=NULL, ...) standardGeneric("studyClient")) # ############################################################################## ## FileClient -setGeneric("fileClient", function(OpencgaR, members, files, file, annotationSet, folder, endpointName, params=NULL, ...) +setGeneric("fileClient", function(OpencgaR, annotationSet, file, files, folder, members, endpointName, params=NULL, ...) standardGeneric("fileClient")) # ############################################################################## ## JobClient -setGeneric("jobClient", function(OpencgaR, job, jobs, members, endpointName, params=NULL, ...) +setGeneric("jobClient", function(OpencgaR, job, members, jobs, endpointName, params=NULL, ...) standardGeneric("jobClient")) # ############################################################################## ## SampleClient -setGeneric("sampleClient", function(OpencgaR, annotationSet, sample, samples, members, endpointName, params=NULL, ...) +setGeneric("sampleClient", function(OpencgaR, members, annotationSet, samples, sample, endpointName, params=NULL, ...) standardGeneric("sampleClient")) # ############################################################################## ## IndividualClient -setGeneric("individualClient", function(OpencgaR, individuals, individual, annotationSet, members, endpointName, params=NULL, ...) +setGeneric("individualClient", function(OpencgaR, members, annotationSet, individuals, individual, endpointName, params=NULL, ...) standardGeneric("individualClient")) # ############################################################################## ## FamilyClient -setGeneric("familyClient", function(OpencgaR, annotationSet, family, families, members, endpointName, params=NULL, ...) +setGeneric("familyClient", function(OpencgaR, members, annotationSet, families, family, endpointName, params=NULL, ...) standardGeneric("familyClient")) # ############################################################################## ## CohortClient -setGeneric("cohortClient", function(OpencgaR, annotationSet, cohort, cohorts, members, endpointName, params=NULL, ...) +setGeneric("cohortClient", function(OpencgaR, cohort, members, annotationSet, cohorts, endpointName, params=NULL, ...) standardGeneric("cohortClient")) # ############################################################################## @@ -60,7 +60,7 @@ setGeneric("variantClient", function(OpencgaR, endpointName, params=NULL, ...) # ############################################################################## ## ClinicalClient -setGeneric("clinicalClient", function(OpencgaR, interpretations, clinicalAnalyses, interpretation, members, clinicalAnalysis, endpointName, params=NULL, ...) +setGeneric("clinicalClient", function(OpencgaR, clinicalAnalysis, clinicalAnalyses, interpretation, interpretations, members, endpointName, params=NULL, ...) standardGeneric("clinicalClient")) # ############################################################################## @@ -75,7 +75,7 @@ setGeneric("metaClient", function(OpencgaR, endpointName, params=NULL, ...) # ############################################################################## ## GA4GHClient -setGeneric("ga4ghClient", function(OpencgaR, study, file, endpointName, params=NULL, ...) +setGeneric("ga4ghClient", function(OpencgaR, file, study, endpointName, params=NULL, ...) standardGeneric("ga4ghClient")) # ############################################################################## diff --git a/opencga-client/src/main/R/R/Clinical-methods.R b/opencga-client/src/main/R/R/Clinical-methods.R index 4bcc44672e0..a3143e1de64 100644 --- a/opencga-client/src/main/R/R/Clinical-methods.R +++ b/opencga-client/src/main/R/R/Clinical-methods.R @@ -59,7 +59,7 @@ #' [*]: Required parameter #' @export -setMethod("clinicalClient", "OpencgaR", function(OpencgaR, interpretations, clinicalAnalyses, interpretation, members, clinicalAnalysis, endpointName, params=NULL, ...) { +setMethod("clinicalClient", "OpencgaR", function(OpencgaR, clinicalAnalysis, clinicalAnalyses, interpretation, interpretations, members, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/analysis/clinical/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Cohort-methods.R b/opencga-client/src/main/R/R/Cohort-methods.R index 0b812191131..1db543c51c8 100644 --- a/opencga-client/src/main/R/R/Cohort-methods.R +++ b/opencga-client/src/main/R/R/Cohort-methods.R @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("cohortClient", "OpencgaR", function(OpencgaR, annotationSet, cohort, cohorts, members, endpointName, params=NULL, ...) { +setMethod("cohortClient", "OpencgaR", function(OpencgaR, cohort, members, annotationSet, cohorts, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/cohorts/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Family-methods.R b/opencga-client/src/main/R/R/Family-methods.R index c01e7bfad72..ab6185d8641 100644 --- a/opencga-client/src/main/R/R/Family-methods.R +++ b/opencga-client/src/main/R/R/Family-methods.R @@ -38,7 +38,7 @@ #' [*]: Required parameter #' @export -setMethod("familyClient", "OpencgaR", function(OpencgaR, annotationSet, family, families, members, endpointName, params=NULL, ...) { +setMethod("familyClient", "OpencgaR", function(OpencgaR, members, annotationSet, families, family, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/families/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/File-methods.R b/opencga-client/src/main/R/R/File-methods.R index 80ae32a893a..f7ec62a9eac 100644 --- a/opencga-client/src/main/R/R/File-methods.R +++ b/opencga-client/src/main/R/R/File-methods.R @@ -54,7 +54,7 @@ #' [*]: Required parameter #' @export -setMethod("fileClient", "OpencgaR", function(OpencgaR, members, files, file, annotationSet, folder, endpointName, params=NULL, ...) { +setMethod("fileClient", "OpencgaR", function(OpencgaR, annotationSet, file, files, folder, members, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/files/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/GA4GH-methods.R b/opencga-client/src/main/R/R/GA4GH-methods.R index 2086f8f9079..0437d411e14 100644 --- a/opencga-client/src/main/R/R/GA4GH-methods.R +++ b/opencga-client/src/main/R/R/GA4GH-methods.R @@ -31,7 +31,7 @@ #' [*]: Required parameter #' @export -setMethod("ga4ghClient", "OpencgaR", function(OpencgaR, study, file, endpointName, params=NULL, ...) { +setMethod("ga4ghClient", "OpencgaR", function(OpencgaR, file, study, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/ga4gh/reads/search: diff --git a/opencga-client/src/main/R/R/Individual-methods.R b/opencga-client/src/main/R/R/Individual-methods.R index 328998e187b..a7e377ad0eb 100644 --- a/opencga-client/src/main/R/R/Individual-methods.R +++ b/opencga-client/src/main/R/R/Individual-methods.R @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("individualClient", "OpencgaR", function(OpencgaR, individuals, individual, annotationSet, members, endpointName, params=NULL, ...) { +setMethod("individualClient", "OpencgaR", function(OpencgaR, members, annotationSet, individuals, individual, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/individuals/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Job-methods.R b/opencga-client/src/main/R/R/Job-methods.R index caab36df7fc..07622301fbc 100644 --- a/opencga-client/src/main/R/R/Job-methods.R +++ b/opencga-client/src/main/R/R/Job-methods.R @@ -40,7 +40,7 @@ #' [*]: Required parameter #' @export -setMethod("jobClient", "OpencgaR", function(OpencgaR, job, jobs, members, endpointName, params=NULL, ...) { +setMethod("jobClient", "OpencgaR", function(OpencgaR, job, members, jobs, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/jobs/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Project-methods.R b/opencga-client/src/main/R/R/Project-methods.R index b46a4aacbeb..a4432945c30 100644 --- a/opencga-client/src/main/R/R/Project-methods.R +++ b/opencga-client/src/main/R/R/Project-methods.R @@ -34,7 +34,7 @@ #' [*]: Required parameter #' @export -setMethod("projectClient", "OpencgaR", function(OpencgaR, projects, project, endpointName, params=NULL, ...) { +setMethod("projectClient", "OpencgaR", function(OpencgaR, project, projects, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/projects/create: diff --git a/opencga-client/src/main/R/R/Sample-methods.R b/opencga-client/src/main/R/R/Sample-methods.R index be3ac2fedd7..e6ec548f309 100644 --- a/opencga-client/src/main/R/R/Sample-methods.R +++ b/opencga-client/src/main/R/R/Sample-methods.R @@ -39,7 +39,7 @@ #' [*]: Required parameter #' @export -setMethod("sampleClient", "OpencgaR", function(OpencgaR, annotationSet, sample, samples, members, endpointName, params=NULL, ...) { +setMethod("sampleClient", "OpencgaR", function(OpencgaR, members, annotationSet, samples, sample, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/samples/acl/{members}/update: diff --git a/opencga-client/src/main/R/R/Study-methods.R b/opencga-client/src/main/R/R/Study-methods.R index 6c8b18b8a5c..23e5e5cafda 100644 --- a/opencga-client/src/main/R/R/Study-methods.R +++ b/opencga-client/src/main/R/R/Study-methods.R @@ -46,7 +46,7 @@ #' [*]: Required parameter #' @export -setMethod("studyClient", "OpencgaR", function(OpencgaR, group, members, templateId, studies, study, variableSet, endpointName, params=NULL, ...) { +setMethod("studyClient", "OpencgaR", function(OpencgaR, study, variableSet, templateId, studies, group, members, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/studies/acl/{members}/update: