diff --git a/application.properties b/application.properties index 716d01e84..b5716568e 100644 --- a/application.properties +++ b/application.properties @@ -3,4 +3,4 @@ app.grails.version=2.3.11 app.name=transmart app.servlet.version=2.5 -app.version=tranSMART 16.2 CI +app.version=16.2.1.UMich.1 diff --git a/grails-app/conf/BootStrap.groovy b/grails-app/conf/BootStrap.groovy index b3f7493f4..01cfda89b 100644 --- a/grails-app/conf/BootStrap.groovy +++ b/grails-app/conf/BootStrap.groovy @@ -78,7 +78,7 @@ class BootStrap { } if (!tsAppRScriptsDir || !tsAppRScriptsDir.isDirectory()) { - throw new RuntimeException('Could not determine proper for ' + + throw new RuntimeException('Could not determine proper value for ' + 'com.recomdata.transmart.data.export.rScriptDirectory') } c.com.recomdata.transmart.data.export.rScriptDirectory = tsAppRScriptsDir.canonicalPath diff --git a/grails-app/conf/BuildConfig.groovy b/grails-app/conf/BuildConfig.groovy old mode 100644 new mode 100755 index b92c2f3ab..8707a8009 --- a/grails-app/conf/BuildConfig.groovy +++ b/grails-app/conf/BuildConfig.groovy @@ -18,6 +18,8 @@ grails.project.fork = [ console: forkSettingsOther] grails.project.war.file = "target/${appName}.war" +grails.plugin.location.'rdc-rmodules' = '../Rmodules-kretzlerLab' +grails.plugin.location.'transmart-core' = '../transmart-core-db-kretzlerLab' /* we need at least servlet-api 2.4 because of HttpServletResponse::setCharacterEncoding */ grails.servlet.version = "2.5" @@ -58,7 +60,7 @@ grails.project.dependency.resolution = { runtime 'org.javassist:javassist:3.16.1-GA' - compile 'org.transmartproject:transmart-core-api:16.2-SNAPSHOT' + compile 'org.transmartproject:transmart-core-api:16.2' compile 'antlr:antlr:2.7.7' compile 'net.sf.opencsv:opencsv:2.3' @@ -71,6 +73,7 @@ grails.project.dependency.resolution = { excludes 'groovy', 'nekohtml', 'httpclient', 'httpcore' } compile 'org.rosuda:Rserve:1.7.3' + compile 'org.rosuda:REngine:1.7.3' compile 'com.google.guava:guava:18.0' compile 'net.sf.ehcache:ehcache:2.9.0' compile 'org.apache.httpcomponents:httpclient:4.4.1' @@ -134,30 +137,33 @@ grails.project.dependency.resolution = { compile ":codenarc:0.21" if (!dm) { - runtime ':smart-r:1.1-STABLE-SNAPSHOT' - compile ':rdc-rmodules:16.2-SNAPSHOT' - runtime ':transmart-core:16.2-SNAPSHOT' - compile ':transmart-gwas:16.2-SNAPSHOT' + runtime ':smart-r:16.2-STABLE' + compile ':rdc-rmodules:16.2' + runtime ':transmart-core:16.2' + compile ':transmart-gwas:16.2' + compile ':transmart-gwas-plink:16.2' //// already included in transmart-gwas - //compile ':transmart-legacy-db:16.2-SNAPSHOT' + //compile ':transmart-legacy-db:16.2' //// already included in transmart-gwas - //compile ':folder-management:16.2-SNAPSHOT' + //compile ':folder-management:16.2' //// already included in transmart-gwas, folder-management - //compile ':search-domain:16.2-SNAPSHOT' + //compile ':search-domain:16.2' //// already included in search-domain, transmart-gwas, // folder-management - //compile ':biomart-domain:16.2-SNAPSHOT' + //compile ':biomart-domain:16.2' //// already included in biomart-domain - //compile ':transmart-java:16.2-SNAPSHOT' - runtime ':dalliance-plugin:16.2-SNAPSHOT' - runtime ':transmart-mydas:16.2-SNAPSHOT' - runtime ':transmart-rest-api:16.2-SNAPSHOT' - runtime ':blend4j-plugin:16.2-SNAPSHOT' - runtime ':transmart-metacore-plugin:16.2-SNAPSHOT' - runtime ':transmart-xnat-importer:16.2-SNAPSHOT' - runtime ':xnat-viewer:16.2-SNAPSHOT' - - test ':transmart-core-db-tests:16.2-SNAPSHOT' + //compile ':transmart-java:16.2' + // -- Genome Browser plugin -- + // to include the plugin, uncomment the following two lines + //runtime ':dalliance-plugin:16.2' + //runtime ':transmart-mydas:16.2' + runtime ':transmart-rest-api:16.2' + runtime ':blend4j-plugin:16.2' + runtime ':transmart-metacore-plugin:16.2' + runtime ':transmart-xnat-importer:16.2' + runtime ':xnat-viewer:16.2' + + //test ':transmart-core-db-tests:16.2' } else { dm.internalDependencies delegate } @@ -170,15 +176,18 @@ grails.project.dependency.resolution = { dm?.with { configureInternalPlugin 'compile', 'rdc-rmodules' configureInternalPlugin 'runtime', 'transmart-core' - configureInternalPlugin 'test', 'transmart-core-db-tests' + //configureInternalPlugin 'test', 'transmart-core-db-tests' configureInternalPlugin 'compile', 'transmart-gwas' + configureInternalPlugin 'compile', 'transmart-gwas-plink' configureInternalPlugin 'compile', 'transmart-java' configureInternalPlugin 'compile', 'biomart-domain' configureInternalPlugin 'compile', 'search-domain' configureInternalPlugin 'compile', 'folder-management' configureInternalPlugin 'compile', 'transmart-legacy-db' - configureInternalPlugin 'runtime', 'dalliance-plugin' - configureInternalPlugin 'runtime', 'transmart-mydas' + // -- Genome Browser plugin -- + // to include the plugin, uncomment the following two lines + //configureInternalPlugin 'runtime', 'dalliance-plugin' + //configureInternalPlugin 'runtime', 'transmart-mydas' configureInternalPlugin 'runtime', 'transmart-rest-api' configureInternalPlugin 'runtime', 'blend4j-plugin' configureInternalPlugin 'runtime', 'transmart-metacore-plugin' diff --git a/grails-app/conf/Config.groovy b/grails-app/conf/Config.groovy old mode 100644 new mode 100755 diff --git a/grails-app/conf/GlobalResources.groovy b/grails-app/conf/GlobalResources.groovy old mode 100644 new mode 100755 index 99b45bf56..0b27f2954 --- a/grails-app/conf/GlobalResources.groovy +++ b/grails-app/conf/GlobalResources.groovy @@ -116,6 +116,7 @@ modules = { resource url: 'css/datasetExplorer.css' resource url: 'css/querypanels.css' resource url: 'css/sanofi.css' + resource url: 'css/font-awesome-4.7.0/css/font-awesome.min.css' } uploadDataTab { diff --git a/grails-app/conf/UrlMappings.groovy b/grails-app/conf/UrlMappings.groovy old mode 100644 new mode 100755 diff --git a/grails-app/controllers/ChartController.groovy b/grails-app/controllers/ChartController.groovy old mode 100644 new mode 100755 index 05478e1c6..8c9ac97be --- a/grails-app/controllers/ChartController.groovy +++ b/grails-app/controllers/ChartController.groovy @@ -83,7 +83,7 @@ class ChartController { log.trace(user.toString()); def concept_key = params.concept_key; log.trace("Requested counts for parent_concept_path=" + concept_key); - def counts = i2b2HelperService.getChildrenWithPatientCountsForConcept(concept_key) + def counts = i2b2HelperService.getChildrenWithPatientCountsForConcept(concept_key,user) def access = i2b2HelperService.getChildrenWithAccessForUserNew(concept_key, user) log.trace("access:" + (access as JSON)); log.trace("counts = " + (counts as JSON)) @@ -222,7 +222,6 @@ class ChartController { def subsets = chartService.computeChartsForSubsets(chartService.getSubsetsFromRequest(params)) def concepts = chartService.getConceptsForSubsets(subsets) concepts.putAll(chartService.getHighDimensionalConceptsForSubsets(subsets)) - // Time to delivery ! render(template: "summaryStatistics", model: [subsets: subsets, concepts: concepts]) } @@ -232,6 +231,7 @@ class ChartController { String concept_key = params.concept_key; def result_instance_id1 = params.result_instance_id1; def result_instance_id2 = params.result_instance_id2; + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) /*which subsets are present? */ boolean s1 = (result_instance_id1 == "" || result_instance_id1 == null) ? false : true; @@ -245,8 +245,8 @@ class ChartController { if (table == null) { table = new ExportTableNew(); - if (s1) i2b2HelperService.addAllPatientDemographicDataForSubsetToTable(table, result_instance_id1, "subset1"); - if (s2) i2b2HelperService.addAllPatientDemographicDataForSubsetToTable(table, result_instance_id2, "subset2"); + if (s1) i2b2HelperService.addAllPatientDemographicDataForSubsetToTable(table, result_instance_id1, "subset1", user); + if (s2) i2b2HelperService.addAllPatientDemographicDataForSubsetToTable(table, result_instance_id2, "subset2", user); List keys = i2b2HelperService.getConceptKeysInSubsets(result_instance_id1, result_instance_id2); Set uniqueConcepts = i2b2HelperService.getDistinctConceptSet(result_instance_id1, result_instance_id2); @@ -258,8 +258,8 @@ class ChartController { if (!i2b2HelperService.isHighDimensionalConceptKey(keys.get(i))) { log.trace("adding concept data for " + keys.get(i)); - if (s1) i2b2HelperService.addConceptDataToTable(table, keys.get(i), result_instance_id1); - if (s2) i2b2HelperService.addConceptDataToTable(table, keys.get(i), result_instance_id2); + if (s1) i2b2HelperService.addConceptDataToTable(table, keys.get(i), result_instance_id1, user); + if (s2) i2b2HelperService.addConceptDataToTable(table, keys.get(i), result_instance_id2, user); } } @@ -272,8 +272,6 @@ class ChartController { PrintWriter pw = new PrintWriter(response.getOutputStream()); if (concept_key && !concept_key.isEmpty()) { - - // We retrieve the highdimension parameters from the client, if they were passed def omics_params = [:] params.findAll { k, v -> @@ -281,7 +279,7 @@ class ChartController { }.each { k, v -> omics_params[k] = v } - if (omics_params) { // empty maps are coerced to false by groovy + if (omics_params) { omics_params.concept_key = concept_key if (s1) highDimensionQueryService.addHighDimConceptDataToTable(table, omics_params, result_instance_id1) if (s2) highDimensionQueryService.addHighDimConceptDataToTable(table, omics_params, result_instance_id2) @@ -302,8 +300,8 @@ class ChartController { conceptKeys.add(concept_key); for (ck in conceptKeys) { - if (s1) i2b2HelperService.addConceptDataToTable(table, ck, result_instance_id1); - if (s2) i2b2HelperService.addConceptDataToTable(table, ck, result_instance_id2); + if (s1) i2b2HelperService.addConceptDataToTable(table, ck, result_instance_id1, user); + if (s2) i2b2HelperService.addConceptDataToTable(table, ck, result_instance_id2, user); } } @@ -326,8 +324,8 @@ class ChartController { accessLogService.report(currentUserBean, 'Grid View Data Export', eventMessage: "User (IP: ${request.getHeader('X-FORWARDED-FOR') ?: request.remoteAddr}) just exported" + - " data for tieal(s) (${trialsCsv}): variables (${exportedVariablesCsv}) measurements for the" + - " folowing patients set(s): " + + " data for trial(s) (${trialsCsv}): variables (${exportedVariablesCsv}) measurements for the" + + " following patient set(s): " + [params.result_instance_id1, params.result_instance_id2].findAll().join(', '), requestURL: request.forwardURI) diff --git a/grails-app/controllers/DataAttestationController.groovy b/grails-app/controllers/DataAttestationController.groovy new file mode 100755 index 000000000..8a27bf93f --- /dev/null +++ b/grails-app/controllers/DataAttestationController.groovy @@ -0,0 +1,45 @@ +import DataAttestation +import org.springframework.web.servlet.support.RequestContextUtils +import org.transmart.searchapp.AuthUser + +class DataAttestationController { + + def springSecurityService + def messageSource + + def index = { + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) + if (DataAttestation.needsDataAttestation(user)) + render(view:"attestation") + else + redirect(uri:'/datasetExplorer/index'); + } + def agree = { + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) + DataAttestation.updateOrAddNewAgreementDate(user) + if (springSecurityService.currentUser.changePassword) { + flash.message = messageSource.getMessage('changePassword', new Objects[0], RequestContextUtils.getLocale(request)) + redirect(controller: 'changeMyPassword') + } else { + redirect(uri: '/datasetExplorer/index') + } + } + + def disagree = { + redirect(uri: '/logout') + } + + def clear = { + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) + def da = DataAttestation.findByAuthUserId(user.id) + if (da != null) { + try { + da.delete(flush: true) + } + catch (org.springframework.dao.DataIntegrityViolationException e) { + flash.message = "Could not delete user ${user}" + } + } + redirect(uri: '/logout') + } +} diff --git a/grails-app/controllers/GeneSignatureController.groovy b/grails-app/controllers/GeneSignatureController.groovy old mode 100644 new mode 100755 index bbc1421ac..c16fd1920 --- a/grails-app/controllers/GeneSignatureController.groovy +++ b/grails-app/controllers/GeneSignatureController.groovy @@ -12,6 +12,7 @@ import org.transmart.searchapp.GeneSignatureFileSchema import org.transmart.searchapp.SearchKeyword import org.transmart.searchapp.SearchKeywordTerm import org.transmartproject.core.users.User +import com.recomdata.db.DBHelper import javax.servlet.ServletOutputStream @@ -80,7 +81,7 @@ class GeneSignatureController { session.setAttribute(WIZ_DETAILS_ATTRIBUTE, null) // logged in user - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) def bAdmin = user.isAdmin() log.info "Admin? " + bAdmin @@ -122,9 +123,9 @@ class GeneSignatureController { */ def createWizard = { // initialize session model data - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) - // initialzize new gs inst + // initialize new gene signature instance def geneSigInst = new GeneSignature(); geneSigInst.properties.createdByAuthUser = user; geneSigInst.properties.publicFlag = false; @@ -144,7 +145,7 @@ class GeneSignatureController { // initialize session model data def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) - // initialize new gs inst + // initialize new gene signature instance def geneSigInst = new GeneSignature(); geneSigInst.properties.createdByAuthUser = user; geneSigInst.properties.publicFlag = false; @@ -163,14 +164,15 @@ class GeneSignatureController { */ def editWizard = { // initialize session model data - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) // load gs instance def geneSigInst = GeneSignature.get(params.id) def clone = geneSigInst.clone() - clone.modifiedByAuthUser = AuthUser.findByUsername(user.username) + clone.modifiedByAuthUser = user if (clone.experimentTypeCellLine?.id == null) clone.experimentTypeCellLine = null // this is hack, don't know how to get around this! + log.debug "experimentTypeCellLine: " + clone.experimentTypeCellLine + "; null? " + (clone.experimentTypeCellLine == null) // set onto session def newWizard = new WizardModelDetails(loggedInUser: user, geneSigInst: clone, wizardType: WizardModelDetails.WIZ_TYPE_EDIT, editId: geneSigInst.id); @@ -184,12 +186,12 @@ class GeneSignatureController { */ def cloneWizard = { // initialize session model data - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) // load gs inst to clone def geneSigInst = GeneSignature.get(params.id) def clone = geneSigInst.clone() - clone.createdByAuthUser = AuthUser.findByUsername(user.username) + clone.createdByAuthUser = user clone.modifiedByAuthUser = null; clone.name = clone.name + " (clone)" clone.description = clone.description + " (clone)" @@ -214,9 +216,9 @@ class GeneSignatureController { * set the indicated gs public for access by everyone */ def makePublic = { - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) def gsInst = GeneSignature.get(params.id) - gsInst.modifiedByAuthUser = AuthUser.findByUsername(user.username) + gsInst.modifiedByAuthUser = user geneSignatureService.makePublic(gsInst, true) flash.message = "GeneSignature '${gsInst.name}' was made public to everyone" @@ -227,9 +229,9 @@ class GeneSignatureController { * set the indicated gs private */ def makePrivate = { - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) def gsInst = GeneSignature.get(params.id) - gsInst.modifiedByAuthUser = AuthUser.findByUsername(user.username) + gsInst.modifiedByAuthUser = user geneSignatureService.makePublic(gsInst, false) flash.message = "GeneSignature '${gsInst.name}' was made private" @@ -240,9 +242,9 @@ class GeneSignatureController { * mark the indicated gs as deleted by setting deletedFlag as true */ def delete = { - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) def gsInst = GeneSignature.get(params.id) - gsInst.modifiedByAuthUser = AuthUser.findByUsername(user.username) + gsInst.modifiedByAuthUser = user geneSignatureService.delete(gsInst) flash.message = "GeneSignature '${gsInst.name}' was marked as deleted" @@ -463,6 +465,7 @@ class GeneSignatureController { def wizard = session.getAttribute(WIZ_DETAILS_ATTRIBUTE) def gs = wizard.geneSigInst gs.properties.list = true + if (!params.boolean('isEdit')) { assert null == gs.properties.id } @@ -479,8 +482,9 @@ class GeneSignatureController { gs.speciesConceptCode = ConceptCode.findByCodeTypeNameAndBioConceptCode("OTHER", "OTHER") // technology platforms - gs.techPlatform = BioAssayPlatform.findByName("Multiple or Unknown") - + gs.techPlatform = BioAssayPlatform.findByName("Other") + if(!gs.techPlatform) {gs.techPlatform = BioAssayPlatform.findByName("None")} + // p value cutoffs gs.pValueCutoffConceptCode = ConceptCode.findByCodeTypeNameAndBioConceptCode(P_VAL_CUTOFF_CATEGORY, "UNDEFINED") @@ -532,7 +536,6 @@ class GeneSignatureController { List markers = [] if(key.startsWith("biomarker_") && val != null && val != "") { markers.add(val.trim()) - } def gsItems = geneSignatureService.loadGeneSigItemsFromList(markers) def geneSigUniqueIds = gs.geneSigItems*.bioDataUniqueId @@ -577,7 +580,7 @@ class GeneSignatureController { * update gene signature and the associated items (new file only) */ def update = { - def user = springSecurityService.getPrincipal() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) // retrieve clone def wizard = session.getAttribute(WIZ_DETAILS_ATTRIBUTE) @@ -590,7 +593,7 @@ class GeneSignatureController { def gsReal = GeneSignature.get(wizard.editId) def origFile = gsReal.uploadFile clone.copyPropertiesTo(gsReal) - gsReal.modifiedByAuthUser = AuthUser.findByUsername(user.username) + gsReal.modifiedByAuthUser = user gsReal.uploadFile = origFile // refresh items if new file uploaded @@ -1007,8 +1010,8 @@ class GeneSignatureController { wizard.expTypes = ConceptCode.findAllByCodeTypeName(EXP_TYPE_CATEGORY, [sort: "bioConceptCode"]) // technology platforms - def platforms = BioAssayPlatform.findAll("from BioAssayPlatform as p where p.vendor is not null order by p.vendor, p.array"); - BioAssayPlatform other = new BioAssayPlatform(); + // removed the check of loaded platforms - BR + def platforms = BioAssayPlatform.findAll("from BioAssayPlatform as p where p.vendor is not null order by p.vendor, p.array"); BioAssayPlatform other = new BioAssayPlatform(); other.accession = "other" //platforms.add(other); wizard.platforms = platforms; diff --git a/grails-app/controllers/HighDimensionFilterController.groovy b/grails-app/controllers/HighDimensionFilterController.groovy index ec9bf2d38..375947329 100644 --- a/grails-app/controllers/HighDimensionFilterController.groovy +++ b/grails-app/controllers/HighDimensionFilterController.groovy @@ -1,9 +1,7 @@ import grails.converters.JSON import org.transmartproject.core.dataquery.highdim.projections.Projection -import org.transmartproject.core.querytool.ConstraintByOmicsValue import org.transmartproject.db.dataquery.highdim.DeGplInfo import org.transmartproject.db.dataquery.highdim.DeSubjectSampleMapping - /** * Author: Denny Verbeeck (dverbeec@its.jnj.com) */ @@ -43,14 +41,23 @@ class HighDimensionFilterController { def resource = highDimensionResourceService.getHighDimDataTypeResourceFromConcept(concept_key) - def model = [gpl_id: platform.id, - marker_type: platform.markerType, - filter_type: resource.getHighDimensionFilterType(), - searchable_properties: resource.getSearchableAnnotationProperties().collectEntries {[it, searchableAnnotationPropertiesDictionary.get(it, it)]}, - filter: filter, - projections: resource.getSearchableProjections().collectEntries {[it, Projection.prettyNames.get(it, it)]}] - - render(template: template, model: model) + if (resource.dataTypeName == 'vcf') { + render "Small Variant data is not supperted yet, stay tuned!" + } + else { + def model = [gpl_id : platform.id, + marker_type : platform.markerType, + filter_type : resource.getHighDimensionFilterType(), + searchable_properties: resource.getSearchableAnnotationProperties().collectEntries { + [it, searchableAnnotationPropertiesDictionary.get(it, it)] + }, + filter : filter, + projections : resource.getSearchableProjections().collectEntries { + [it, Projection.prettyNames.get(it, it)] + }] + + render(template: template, model: model) + } } /** @@ -104,6 +111,7 @@ class HighDimensionFilterController { */ static Map searchableAnnotationPropertiesDictionary = ['geneSymbol': 'Gene Symbol', + 'probeId': 'Probe ID', 'cytoband': 'Cytoband', 'name': ' Region Name', 'hmdbId': 'HMDB ID', @@ -118,5 +126,6 @@ class HighDimensionFilterController { 'position': 'Position', 'rsId': 'RSID', 'referenceAllele': 'Reference Allele', - 'detector': 'miRNA Symbol'] + 'detector': 'miRNA Symbol', + 'transcriptId': 'Transcript ID'] } \ No newline at end of file diff --git a/grails-app/controllers/MonitorController.groovy b/grails-app/controllers/MonitorController.groovy new file mode 100755 index 000000000..8c00ed47c --- /dev/null +++ b/grails-app/controllers/MonitorController.groovy @@ -0,0 +1,19 @@ +class MonitorController { + + def monitorService + + def index = { + def dbStatusText = "" + def otherStatusText = monitorService.otherStatusText + + try { + dbStatusText = monitorService.getDatabaseStatusText(false, "") + } catch (e) { + dbStatusText = monitorService.getDatabaseStatusText(true, e.getMessage()) + } + + render(text: dbStatusText + otherStatusText) + + } + +} diff --git a/grails-app/controllers/OntologyController.groovy b/grails-app/controllers/OntologyController.groovy old mode 100644 new mode 100755 index 5a62a9083..fb75d1c28 --- a/grails-app/controllers/OntologyController.groovy +++ b/grails-app/controllers/OntologyController.groovy @@ -6,7 +6,6 @@ import grails.converters.JSON import org.transmart.biomart.Experiment import org.transmart.searchapp.AuthUser import org.transmartproject.core.dataquery.highdim.HighDimensionResource -import org.transmartproject.core.dataquery.highdim.Platform import org.transmartproject.core.dataquery.highdim.assayconstraints.AssayConstraint import org.transmartproject.core.ontology.ConceptsResource import org.transmartproject.core.ontology.OntologyTerm @@ -98,13 +97,23 @@ class OntologyController { .getSubResourcesAssayMultiMap([dataTypeConstraint]) } + if (i2b2HelperService.isXTrialsConcept(params.conceptKey)) { + def node = conceptsResourceService.getByKey(params.conceptKey); + def tags = []; + if (node.modifierDimension.tooltip != null) { + tags.add(new i2b2.OntNodeTag(tagtype: 'Details', tag: node.modifierDimension.tooltip)) + model.tags = tags; + } + } + else { + //ontology term tags + def tagsMap = ontologyTermTagsResourceService.getTags([ term ] as Set, false) + model.tags = tagsMap?.get(term) + } + //browse tab tags model.browseStudyInfo = getBrowseStudyInfo(term) - //ontology term tags - def tagsMap = ontologyTermTagsResourceService.getTags([ term ] as Set, false) - model.tags = tagsMap?.get(term) - render template: 'showDefinition', model: model } diff --git a/grails-app/controllers/SubsetController.groovy b/grails-app/controllers/SubsetController.groovy index cf0b60564..3becc2d3f 100644 --- a/grails-app/controllers/SubsetController.groovy +++ b/grails-app/controllers/SubsetController.groovy @@ -1,6 +1,7 @@ import com.recomdata.transmart.domain.searchapp.Subset import grails.converters.JSON -import org.transmartproject.core.querytool.QueriesResource +import org.transmartproject.core.dataquery.highdim.projections.Projection +import org.transmartproject.core.querytool.ConstraintByOmicsValue class SubsetController { @@ -99,9 +100,11 @@ class SubsetController { Subset subset = Subset.get(subsetId) - def queryID1 = queriesResourceService.getQueryDefinitionForResult( - queriesResourceService.getQueryResultFromId(subset.queryID1)) - displayQuery1 = generateDisplayOutput(queryID1) + if (subset.queryID1 != -1) { + def queryID1 = queriesResourceService.getQueryDefinitionForResult( + queriesResourceService.getQueryResultFromId(subset.queryID1)) + displayQuery1 = generateDisplayOutput(queryID1) + } if (subset.queryID2 != -1) { def queryID2 = queriesResourceService.getQueryDefinitionForResult( @@ -122,6 +125,25 @@ class SubsetController { if (i.constraint) { result += "( with constraints )" } + if (i.constraintByOmicsValue) { + result += " - " + i.constraintByOmicsValue.selector + " " + + Projection.prettyNames.get(i.constraintByOmicsValue.projectionType, + i.constraintByOmicsValue.projectionType) + " " + + i.constraintByOmicsValue.operator.value + " " + if (i.constraintByOmicsValue.operator == ConstraintByOmicsValue.Operator.BETWEEN) { + String[] bounds = i.constraintByOmicsValue.constraint.split(':') + if (bounds.length != 2) { + log.error "BETWEEN constraint type found with values not seperated by ':'" + result += i.constraintByOmicsValue.constraint + } + else { + result += bounds.join(" and ") + } + } + else { + result += i.constraintByOmicsValue.constraint + } + } result += "
" } } diff --git a/grails-app/controllers/UserLandingController.groovy b/grails-app/controllers/UserLandingController.groovy old mode 100644 new mode 100755 index 6bba7072d..89d3289b3 --- a/grails-app/controllers/UserLandingController.groovy +++ b/grails-app/controllers/UserLandingController.groovy @@ -1,5 +1,6 @@ import org.springframework.web.servlet.support.RequestContextUtils import org.transmart.searchapp.AccessLog +import org.transmart.searchapp.AuthUser class UserLandingController { /** @@ -15,19 +16,26 @@ class UserLandingController { } def index = { + def user = AuthUser.findByUsername(springSecurityService?.principal?.username) new AccessLog(username: springSecurityService?.principal?.username, event: "Login", eventmessage: request.getHeader("user-agent"), accesstime: new Date()).save() - def skip_disclaimer = grailsApplication.config.com.recomdata?.skipdisclaimer ?: false; - if (skip_disclaimer) { - if (springSecurityService?.currentUser?.changePassword) { - flash.message = messageSource.getMessage('changePassword', new Objects[0], RequestContextUtils.getLocale(request)) - redirect(controller: 'changeMyPassword') + def skip_data_attestation = grailsApplication.config.com.recomdata?.skipdataattestation?:false; + if ((!skip_data_attestation) && DataAttestation.needsDataAttestation(user)) { + redirect(uri: '/dataAttestation/index') + } + else { + def skip_disclaimer = grailsApplication.config.com.recomdata?.skipdisclaimer ?: false; + if (skip_disclaimer) { + if (springSecurityService?.currentUser?.changePassword) { + flash.message = messageSource.getMessage('changePassword', new Objects[0], RequestContextUtils.getLocale(request)) + redirect(controller: 'changeMyPassword') + } else { + redirect(uri: userLandingPath) + } } else { - redirect(uri: userLandingPath) + redirect(uri: '/userLanding/disclaimer.gsp') } - } else { - redirect(uri: '/userLanding/disclaimer.gsp') } } def agree = { diff --git a/grails-app/controllers/com/recomdata/transmart/asynchronous/job/AsyncJobController.groovy b/grails-app/controllers/com/recomdata/transmart/asynchronous/job/AsyncJobController.groovy index dd2e41fa6..bff682bff 100644 --- a/grails-app/controllers/com/recomdata/transmart/asynchronous/job/AsyncJobController.groovy +++ b/grails-app/controllers/com/recomdata/transmart/asynchronous/job/AsyncJobController.groovy @@ -181,19 +181,19 @@ class AsyncJobController { // for concept paths we have to make sure they start with \\top node // note the string is escaped so we are adding a double backslash at the start - if (params.independentVariable != null) { + if (params.independentVariable != null && params.independentVariable != "") { concept_key = params.independentVariable.split("\\|")[0] concept_table = concept_key.split("\\\\")[1] studyIds += studyIdService.getStudyIdForConceptKey('\\\\' + concept_table + concept_key) } - if (params.dependentVariable != null) { + if (params.dependentVariable != null && params.dependentVariable != "") { concept_key = params.dependentVariable.split("\\|")[0] concept_table = concept_key.split("\\\\")[1] studyIds += studyIdService.getStudyIdForConceptKey('\\\\' + concept_table + concept_key) } - if (params.variablesConceptPaths != null) { + if (params.variablesConceptPaths != null && params.variablesConceptPaths != "") { concept_key = params.variablesConceptPaths.split("\\|")[0] concept_table = concept_key.split("\\\\")[1] studyIds += studyIdService.getStudyIdForConceptKey('\\\\' + concept_table + concept_key) diff --git a/grails-app/controllers/org/transmart/ontology/ConceptsController.groovy b/grails-app/controllers/org/transmart/ontology/ConceptsController.groovy old mode 100644 new mode 100755 index f2a2a16c6..e74ba84d0 --- a/grails-app/controllers/org/transmart/ontology/ConceptsController.groovy +++ b/grails-app/controllers/org/transmart/ontology/ConceptsController.groovy @@ -1,23 +1,33 @@ package org.transmart.ontology - import grails.converters.JSON +import org.transmart.searchapp.AuthUser import org.transmartproject.core.exceptions.InvalidArgumentsException //import org.transmartproject.core.ontology.BoundModifier - -import javax.naming.OperationNotSupportedException - class ConceptsController { def conceptsResourceService + def i2b2HelperService + def springSecurityService def getCategories() { render conceptsResourceService.allCategories as JSON } def getChildren() { + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) def parentConceptKey = params.get('concept_key') def parent = conceptsResourceService.getByKey(parentConceptKey) - render parent.children as JSON + def childrenWithTokens = i2b2HelperService.getChildPathsWithTokensFromParentKey(parentConceptKey) + def childrenWithAuth = i2b2HelperService.getAccess(childrenWithTokens, user) + def authChildren = [] + + parent.children.each { child-> + if (childrenWithAuth[child.fullName] != 'Locked') { + authChildren.add(child) + } + } + + render authChildren as JSON } def getResource() { diff --git a/grails-app/domain/DataAttestation.groovy b/grails-app/domain/DataAttestation.groovy new file mode 100755 index 000000000..a272f1611 --- /dev/null +++ b/grails-app/domain/DataAttestation.groovy @@ -0,0 +1,77 @@ + +/************************************************************************* + * tranSMART - translational medicine data mart + * + * Copyright 2008-2012 Janssen Research & Development, LLC. + * + * This product includes software developed at Janssen Research & Development, LLC. + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version, along with the following terms: + * 1. You may convey a work based on this program in accordance with section 5, provided that you retain the above notices. + * 2. You may convey verbatim copies of this program code as you receive it, in any medium, provided that you retain the above notices. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * + ******************************************************************/ +class DataAttestation { + + Long id + Long authUserId + Date lastDateAgreed + + //static belongsTo = [people: AuthUser] + + static mapping = { + table 'DATA_ATTESTATION' + version false + id generator:'sequence', params:[sequence:'DATA_ATTESTATION_ID_SEQ'] + columns { + id column:'DATA_ATTESTATION_ID' + authUserId column:'AUTH_USER_ID' + lastDateAgreed column:'LAST_DATE_AGREED' + } + + } + + def beforeInsert ={ + if(lastDateAgreed == null) { + lastDateAgreed = new Date() + } + } + + static constraints = { + authUserId(nullable:false) + lastDateAgreed(nullable:false) + } + + def hasAgreed() { + def agreed = true + def today = new Date() + if (daysBetween(lastDateAgreed,today) > 90) + agreed = false + return agreed + } + + static needsDataAttestation(user) { + def da = DataAttestation.findByAuthUserId(user.id) + return (da == null || !da.hasAgreed()) + } + + static updateOrAddNewAgreementDate(user) { + def da = DataAttestation.findByAuthUserId(user.id) + if (da == null) { + new DataAttestation(authUserId: user.id, lastDateAgreed: new Date()).save() + } else { + da.lastDateAgreed = new Date() + da.save() + } + } + + int daysBetween(Date d1, Date d2){ + return (int)( (d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24)); + } +} diff --git a/grails-app/services/ChartService.groovy b/grails-app/services/ChartService.groovy old mode 100644 new mode 100755 index 7c1bf733a..8d419eb4c --- a/grails-app/services/ChartService.groovy +++ b/grails-app/services/ChartService.groovy @@ -2,12 +2,15 @@ import org.apache.commons.math.stat.inference.TestUtils import org.jfree.chart.ChartFactory import org.jfree.chart.ChartRenderingInfo import org.jfree.chart.JFreeChart +import org.jfree.chart.axis.AxisLocation import org.jfree.chart.axis.CategoryAxis import org.jfree.chart.axis.NumberAxis import org.jfree.chart.axis.ValueAxis import org.jfree.chart.entity.StandardEntityCollection import org.jfree.chart.labels.BoxAndWhiskerToolTipGenerator import org.jfree.chart.plot.CategoryPlot +import org.jfree.chart.plot.PiePlot +import org.jfree.chart.plot.PieLabelLinkStyle import org.jfree.chart.plot.PlotOrientation import org.jfree.chart.plot.XYPlot import org.jfree.chart.renderer.category.* @@ -19,9 +22,11 @@ import org.jfree.data.general.Dataset import org.jfree.data.general.DefaultPieDataset import org.jfree.data.statistics.* import org.jfree.graphics2d.svg.SVGGraphics2D -import org.jfree.ui.RectangleInsets +import org.jfree.chart.ui.RectangleInsets import org.jfree.util.ShapeUtilities +import org.transmart.searchapp.AuthUser import org.transmartproject.core.dataquery.highdim.projections.Projection +import org.transmartproject.core.exceptions.EmptySetException import org.transmartproject.core.querytool.ConstraintByOmicsValue import java.awt.* @@ -35,6 +40,7 @@ class ChartService { def i2b2HelperService def highDimensionQueryService def highDimensionResourceService + def springSecurityService def public keyCache = [] def getSubsetsFromRequest(params) { @@ -42,7 +48,7 @@ class ChartService { // We retrieve the result instance ids from the client def result_instance_id1 = params.result_instance_id1 ?: null; def result_instance_id2 = params.result_instance_id2 ?: null; - + // We create our subset reference Map [ 1: [ exists: !(result_instance_id1 == null || result_instance_id1 == ""), instance: result_instance_id1], @@ -82,11 +88,12 @@ class ChartService { // First we get the Query Definition i2b2HelperService.renderQueryDefinition(p.instance, "Query Summary for Subset ${n}", writer) p.query = output.toStringAndFlush() + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) // Let's fetch the patient count - p.patientCount = i2b2HelperService.getPatientSetSize(p.instance) + p.patientCount = i2b2HelperService.getPatientSetSize(p.instance, user) // Getting the age data - p.ageData = i2b2HelperService.getPatientDemographicValueDataForSubset("AGE_IN_YEARS_NUM", p.instance).toList() + p.ageData = i2b2HelperService.getPatientDemographicValueDataForSubset("AGE_IN_YEARS_NUM", p.instance, user).toList() if (p.ageData) { p.ageStats = BoxAndWhiskerCalculator.calculateBoxAndWhiskerStatistics(p.ageData) ageHistogramHandle["Subset $n"] = p.ageData @@ -96,20 +103,20 @@ class ChartService { def moveKeyToEndOfMap = { map,key -> if (map.containsKey(key)) {def v=map[key];map.remove(key);map[key]=v} } // Sex chart has to be generated for each subset - p.sexData = i2b2HelperService.getPatientDemographicDataForSubset("sex_cd", p.instance) + p.sexData = i2b2HelperService.getPatientDemographicDataForSubset("sex_cd", p.instance, user) moveKeyToEndOfMap(p.sexData,'') p.sexPie = getSVGChart(type: 'pie', data: p.sexData, title: "Sex") // Same thing for Race chart - p.raceData = i2b2HelperService.getPatientDemographicDataForSubset("race_cd", p.instance) + p.raceData = i2b2HelperService.getPatientDemographicDataForSubset("race_cd", p.instance, user) moveKeyToEndOfMap(p.raceData,'') p.racePie = getSVGChart(type: 'pie', data: p.raceData, title: "Race") } // Let's build our age diagrams now that we have all the points in - subsets.commons.ageHisto = getSVGChart(type: 'histogram', data: ageHistogramHandle, title: "Age") - subsets.commons.agePlot = getSVGChart(type: 'boxplot', data: agePlotHandle, title: "Age") + subsets.commons.ageHisto = getSVGChart(type: 'histogram', data: ageHistogramHandle, title: "Age Histogram", xlabel: "Age", ylabel: "Count") + subsets.commons.agePlot = getSVGChart(type: 'boxplot', data: agePlotHandle, title: "Age Comparison", ylabel: "Age") subsets } @@ -134,15 +141,19 @@ class ChartService { def getHighDimensionalConceptsForSubsets(subsets) { // We also retrieve all concepts involved in the query - def concepts = [:] - highDimensionQueryService.getHighDimensionalConceptSet(subsets[1].instance, subsets[2].instance).findAll() { + def theseConcepts = [:] + def highDimensionalConceptSet = highDimensionQueryService.getHighDimensionalConceptSet(subsets[1].instance, subsets[2].instance) + highDimensionalConceptSet.findAll() { it.concept_key.indexOf("SECURITY") <= -1 }.each { def key = it.concept_key + it.omics_selector + " - " + it.omics_projection_type - if (!concepts.containsKey(key)) - concepts[key] = getConceptAnalysis(concept: it.concept_key, subsets: subsets, omics_params: it) + if (!theseConcepts.containsKey(key) ) + if (i2b2HelperService.isHighDimensionalConceptKey(it.concept_key)) { + theseConcepts[key] = getConceptAnalysis(concept: it.concept_key, subsets: subsets, omics_params: it) + } } - concepts + + theseConcepts } def getConceptAnalysis (Map args) { @@ -151,6 +162,8 @@ class ChartService { def concept = args.concept ?: null def chartSize = args.chartSize ?: null + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) + // We create our result holder and initiate it from subsets def result = [:] subsets.each { k, v -> @@ -164,6 +177,7 @@ class ChartService { result.commons.conceptKey = concept.substring(concept.substring(3).indexOf('\\') + 3) result.commons.conceptName = i2b2HelperService.getShortNameFromKey(concept); result.commons.conceptPath = concept + result.commons.conceptShortName = i2b2HelperService.getColumnNameFromKey(concept) result.commons.omics_params = args.omics_params ?: null if (i2b2HelperService.isValueConceptCode(result.commons.conceptCode)) { @@ -179,7 +193,7 @@ class ChartService { }.each { n, p -> if (p.instance != "") - p.patientCount = i2b2HelperService.getPatientSetSize(p.instance) + p.patientCount = i2b2HelperService.getPatientSetSize(p.instance, user) else p.patientCount = i2b2HelperService.getPatientCountForConcept(concept) @@ -191,33 +205,33 @@ class ChartService { } // Let's build our concept diagrams now that we have all the points in - result.commons.conceptHisto = getSVGChart(type: 'histogram', data: conceptHistogramHandle, size: chartSize) - result.commons.conceptPlot = getSVGChart(type: 'boxplot', data: conceptPlotHandle, size: chartSize) + result.commons.conceptHisto = getSVGChart(type: 'histogram', data: conceptHistogramHandle, size: chartSize, ylabel: "Count", xlabel: result.commons.conceptShortName) + result.commons.conceptPlot = getSVGChart(type: 'boxplot', data: conceptPlotHandle, size: chartSize, ylabel: result.commons.conceptShortName) // Lets calculate the T test if possible if (result[1].exists && result[2].exists) { if (result[1].conceptData.toArray() == result[2].conceptData.toArray()) - result.commons.testmessage = 'No T-test calculated: these are the same subsets' + result.commons.testmessage = 'No t-test calculated: these are the same subsets' else if (result[1].conceptData.size() < 2 || result[2].conceptData.size() < 2) - result.commons.testmessage = 'No T-test calculated: not enough data' + result.commons.testmessage = 'No t-test calculated: not enough data' else { def double [] o = (double[])result[1].conceptData.toArray() def double [] t = (double[])result[2].conceptData.toArray() result.commons.tstat = TestUtils.t(o, t).round(5) - result.commons.pvalue = TestUtils.tTest(o, t).round(5) + result.commons.pvalue = String.format("%1.3e", TestUtils.tTest(o, t)) result.commons.significance = TestUtils.tTest(o, t, 0.05) if (result.commons.significance) - result.commons.testmessage = 'T-test demonstrated results are significant at a 95% confidence level' + result.commons.testmessage = 't-test demonstrated results are significant at a 95% confidence level' else - result.commons.testmessage = 'T-test demonstrated results are not significant at a 95% confidence level' + result.commons.testmessage = 't-test demonstrated results are not significant at a 95% confidence level' } } - } else if (i2b2HelperService.isHighDimensionalConceptCode(result.commons.conceptCode) && result.commons.omics_params) { + } else if (i2b2HelperService.isHighDimensionalConceptCode(result.commons.conceptCode) && i2b2HelperService.isValidOmicsParams(result.commons.omics_params)) { result.commons.type = 'value' result.commons.conceptName = result.commons.omics_params.omics_selector + " in " + result.commons.conceptName @@ -233,16 +247,22 @@ class ChartService { }.each { n, p -> // Getting the concept data - p.conceptData = - resource.getDistribution( - new ConstraintByOmicsValue(projectionType: result.commons.omics_params.omics_projection_type, - property : result.commons.omics_params.omics_property, - selector : result.commons.omics_params.omics_selector), - concept, - (p.instance == "" ? null : p.instance as Long)).collect {k, v -> v} + try { + p.conceptData = + resource.getDistribution( + new ConstraintByOmicsValue(projectionType: result.commons.omics_params.omics_projection_type, + property: result.commons.omics_params.omics_property, + selector: result.commons.omics_params.omics_selector), + concept, + (p.instance == "" ? null : p.instance as Long)).collect { k, v -> v } + } + catch (EmptySetException ese) { + log.warn("No assays satisfy the provided criteria in result_instance_id " + p.instance) + p.conceptData = [] + } if (p.instance != "") - p.patientCount = i2b2HelperService.getPatientSetSize(p.instance) + p.patientCount = i2b2HelperService.getPatientSetSize(p.instance, user) else p.patientCount = i2b2HelperService.getPatientCountForConcept(concept) @@ -258,25 +278,25 @@ class ChartService { result.commons.conceptPlot = getSVGChart(type: 'boxplot-and-points', data: conceptHistogramHandle, boxplotdata: conceptPlotHandle, size: chartSize) // Lets calculate the T test if possible - if (result[2].exists) { + if (result[1].exists && result[2].exists) { if (result[1].conceptData.toArray() == result[2].conceptData.toArray()) - result.commons.testmessage = 'No T-test calculated: these are the same subsets' + result.commons.testmessage = 'No t-test calculated: these are the same subsets' else if (result[1].conceptData.size() < 2 || result[2].conceptData.size() < 2) - result.commons.testmessage = 'No T-test calculated: not enough data' + result.commons.testmessage = 'No t-test calculated: not enough data' else { def double [] o = (double[])result[1].conceptData.toArray() def double [] t = (double[])result[2].conceptData.toArray() result.commons.tstat = TestUtils.t(o, t).round(5) - result.commons.pvalue = TestUtils.tTest(o, t).round(5) + result.commons.pvalue = String.format("%1.3e", TestUtils.tTest(o, t)) result.commons.significance = TestUtils.tTest(o, t, 0.05) if (result.commons.significance) - result.commons.testmessage = 'T-test demonstrated results are significant at a 95% confidence level' + result.commons.testmessage = 't-test demonstrated results are significant at a 95% confidence level' else - result.commons.testmessage = 'T-test demonstrated results are not significant at a 95% confidence level' + result.commons.testmessage = 't-test demonstrated results are not significant at a 95% confidence level' } } @@ -291,17 +311,17 @@ class ChartService { }.each { n, p -> if (p.instance != "") - p.patientCount = i2b2HelperService.getPatientSetSize(p.instance) + p.patientCount = i2b2HelperService.getPatientSetSize(p.instance, user) else p.patientCount = i2b2HelperService.getPatientCountForConcept(concept) // Getting the concept data - p.conceptData = i2b2HelperService.getConceptDistributionDataForConcept(concept, p.instance) - p.conceptBar = getSVGChart(type: 'bar', data: p.conceptData, size: [width: 400, height: p.conceptData.size() * 15 + 80]) + p.conceptData = i2b2HelperService.getConceptDistributionDataForConcept(concept, p.instance, user) + p.conceptBar = getSVGChart(type: 'bar', data: p.conceptData, size: [width: 500, height: p.conceptData.size() * 22 + 90], ylabel: "Count", xlabel: "Concept") } // Let's calculate the χ² test if possible - if (result[2].exists) { + if (result[1].exists && result[2].exists) { def junction = false @@ -322,7 +342,7 @@ class ChartService { def long [][] counts = [result[1].conceptData.values(), result[2].conceptData.values()] result.commons.chisquare = TestUtils.chiSquare(counts).round(5) - result.commons.pvalue = TestUtils.chiSquareTest(counts).round(5) + result.commons.pvalue = String.format("%1.3e", TestUtils.chiSquareTest(counts)) result.commons.significance = TestUtils.chiSquareTest(counts, 0.05) if (result.commons.significance) @@ -359,6 +379,11 @@ class ChartService { // We retrieve the dimension if provided def width = size?.width ?: 300 def height = size?.height ?: 300 + + if (type == 'pie') { + width = 400; + height = 400; + } // If no data is being sent we return an empty string if (data.isEmpty()) return '' @@ -370,9 +395,9 @@ class ChartService { JFreeChart chart = null Color transparent = new Color(255, 255, 255, 0) - Color subset1SeriesColor = new Color(254, 220, 119, 150) + Color subset1SeriesColor = new Color(110, 155, 73, 150) Color subset2SeriesColor = new Color(110, 158, 200, 150) - Color subset1SeriesOutlineColor = new Color(214, 152, 13) + Color subset1SeriesOutlineColor = new Color(110, 155, 73) Color subset2SeriesOutlineColor = new Color(17, 86, 146) SVGGraphics2D renderer = new SVGGraphics2D(width, height) @@ -387,9 +412,11 @@ class ChartService { plot?.backgroundPaint = transparent if (plot instanceof CategoryPlot || plot instanceof XYPlot) { - + float[] dashArray = [2.0F, 2.0F] as float[] plot?.domainGridlinePaint = Color.LIGHT_GRAY + plot?.domainGridlineStroke = new BasicStroke(1F, 0, 2, 0.0F, dashArray, 0.0F); plot?.rangeGridlinePaint = Color.LIGHT_GRAY + plot?.rangeGridlineStroke = new BasicStroke(1F, 0, 2, 0.0F, dashArray, 0.0F); plot?.renderer?.setSeriesPaint(0, subset1SeriesColor) plot?.renderer?.setSeriesPaint(1, subset2SeriesColor) plot?.renderer?.setSeriesOutlinePaint(0, subset1SeriesOutlineColor) @@ -443,6 +470,7 @@ class ChartService { chart = ChartFactory.createHistogram(title, xlabel, ylabel, set, PlotOrientation.VERTICAL, true, true, false) chart.setChartParameters() + // If the first series (index 0) is related to 'Subset 2' i.s.o. 'Subset 1' // (e.g. because 'Subset 1' is empty or if no data is avaialable for the given concept) // adjust the default coloring scheme @@ -450,7 +478,6 @@ class ChartService { chart.plot.renderer.setSeriesPaint(0, subset2SeriesColor) chart.plot.renderer.setSeriesOutlinePaint(0, subset2SeriesOutlineColor) } - chart.legend.visible = false break; @@ -482,9 +509,13 @@ class ChartService { // adjust the default coloring scheme if (set.getRowCount()>0 && set.getRowKey(0) ==~ /.* 2/) { chart.plot.renderer.setSeriesPaint(0, subset2SeriesColor) - chart.plot.renderer.setSeriesOutlinePaint(0, subset2SeriesOutlineColor) } chart.plot.renderer.maximumBarWidth = 0.09 + chart.plot.renderer.setSeriesOutlinePaint(0, subset1SeriesOutlineColor) + chart.plot.renderer.setSeriesOutlinePaint(1, subset2SeriesOutlineColor) + chart.plot.renderer.setUseOutlinePaintForWhiskers(true) + chart.plot.renderer.setSeriesPaint(0, new Color(110, 155, 73)) + chart.plot.renderer.setSeriesPaint(1, new Color(110, 158, 200)) break; @@ -504,7 +535,7 @@ class ChartService { final NumberAxis yAxis = new NumberAxis(ylabel); yAxis.setAutoRangeIncludesZero(false); final BoxAndWhiskerRenderer boxAndWhiskerRenderer = new BoxAndWhiskerRenderer(); - boxAndWhiskerRenderer.setBaseToolTipGenerator(new BoxAndWhiskerToolTipGenerator()); + boxAndWhiskerRenderer.setDefaultToolTipGenerator(new BoxAndWhiskerToolTipGenerator()); final CategoryPlot catplot = new CategoryPlot(set, xAxis, yAxis, boxAndWhiskerRenderer); // add the points @@ -517,6 +548,7 @@ class ChartService { ChartFactory.chartTheme.apply(chart); chart.setChartParameters(); + chart.plot.renderer.maximumBarWidth = 0.09; break; @@ -540,15 +572,16 @@ class ChartService { chart.title.font.size = 13 chart.title.padding = new RectangleInsets(30, 0, 0, 0) - chart.plot.labelBackgroundPaint = new Color(230, 230, 230) - chart.plot.labelOutlinePaint = new Color(130, 130, 130) - chart.plot.labelShadowPaint = transparent - chart.plot.labelPadding = new RectangleInsets(5, 5, 5, 5) - chart.plot.maximumLabelWidth = 0.2 - chart.plot.shadowPaint = transparent - chart.plot.interiorGap = 0 - chart.plot.baseSectionOutlinePaint = new Color(213, 18, 42) - + + PiePlot plot = (PiePlot) chart.getPlot(); + plot.setLabelBackgroundPaint(null); + plot.setLabelOutlinePaint(null); + plot.setLabelShadowPaint(null); + plot.setMaximumLabelWidth(0.25); + plot.setShadowPaint(transparent); + plot.setInteriorGap(0.25); + plot.setLabelLinkStyle(PieLabelLinkStyle.STANDARD); + data.eachWithIndex { o, i -> if(o.key){ chart.plot.setSectionPaint(o.key, new Color(213, 18, 42, (255 / (data.size() + 1) * (data.size() - i)).toInteger())) @@ -572,10 +605,16 @@ class ChartService { } chart = ChartFactory.createBarChart(title, xlabel, ylabel, set, PlotOrientation.HORIZONTAL, false, true, false) - chart.setChartParameters() + chart.setChartParameters(); - chart.plot.renderer.setSeriesPaint(0, new Color(128, 193, 119)) - chart.plot.renderer.setSeriesOutlinePaint(0, new Color(84, 151, 12)) + def categoryPlot = chart.getCategoryPlot() + categoryPlot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT) + categoryPlot.renderer.setSeriesPaint(0, new Color(128, 193, 119)) + categoryPlot.renderer.setSeriesOutlinePaint(0, new Color(84, 151, 12)) + CategoryAxis axis = new CategoryAxis(); + axis.setMaximumCategoryLabelLines(2); + axis.configure(); + categoryPlot.setDomainAxis(axis); break; } @@ -670,7 +709,7 @@ class ChartService { g2.setPaint(getItemFillPaint(row, column)); } else { - g2.setPaint(getItemPaint(row, column)); + g2.setPaint(Color.GRAY); } g2.fill(shape); } diff --git a/grails-app/services/GeneSignatureService.groovy b/grails-app/services/GeneSignatureService.groovy index ccd756f75..6213fc901 100644 --- a/grails-app/services/GeneSignatureService.groovy +++ b/grails-app/services/GeneSignatureService.groovy @@ -78,7 +78,7 @@ public class GeneSignatureService { while (br.ready() && i < 3) { i++; record = br.readLine().trim(); - println("Line " + i + ": " + record) + log.info("Line " + i + ": " + record) if (record == "") continue; List items = new ArrayList(); @@ -145,7 +145,7 @@ public class GeneSignatureService { gsItems.add(new GeneSignatureItem(bioDataUniqueId: snpUid)) continue; } - println("WARN: invalid gene symbol: "+ geneSymbol) + log.warn("WARN: invalid gene symbol: "+ geneSymbol) invalidSymbols.add(geneSymbol); continue; } @@ -153,7 +153,7 @@ public class GeneSignatureService { { def bioMarkerId = marker[j].getAt(0); def uniqueId = marker[j].getAt(1) - println(">> Gene lookup: 1) marker id: "+bioMarkerId+"; 2) unique id: "+uniqueId) + log.info(">> Gene lookup: 1) marker id: "+bioMarkerId+"; 2) unique id: "+uniqueId) // create item instance GeneSignatureItem item = new GeneSignatureItem(bioMarker: BioMarker.read(bioMarkerId), bioDataUniqueId: uniqueId, foldChgMetric: foldChg); gsItems.add(item); @@ -192,7 +192,7 @@ public class GeneSignatureService { //while(br.ready() && i < 5) { i++; record = br.readLine().trim(); - println("Line " + i + ": " + record) + log.info("Line " + i + ": " + record) if (record == "") continue; List items = new ArrayList(); @@ -225,14 +225,14 @@ public class GeneSignatureService { if (fileSchemaName.toUpperCase() =~ /GENE /) { marker = lookupBioAssociations(geneSymbol, organism) if (marker == null || marker.size() == 0) { - println("WARN: invalid gene symbol: " + geneSymbol) + log.warn("WARN: invalid gene symbol: " + geneSymbol) invalidSymbols.add(geneSymbol); continue; } def bioMarkerId = marker.getAt(0); def uniqueId = marker.getAt(1) - println(">> Gene lookup: 1) marker id: " + bioMarkerId + "; 2) uniqued id: " + uniqueId) + log.info(">> Gene lookup: 1) marker id: " + bioMarkerId + "; 2) uniqued id: " + uniqueId) // create item instance GeneSignatureItem item = new GeneSignatureItem(bioMarker: BioMarker.read(bioMarkerId), bioDataUniqueId: uniqueId, foldChgMetric: foldChg); @@ -251,13 +251,13 @@ public class GeneSignatureService { } //def probesetId = marker.getAt(0); - // fixes -- 2014_10_31 + // fixes -- 2014_10_31 def probesetId = marker['probesetId'] def geneId = marker['geneId'] - String bioDataUniqueId = "GENE:" + String.valueOf(geneId) + String bioDataUniqueId = "GENE:" + String.valueOf(geneId) def bioMarkerId = marker['bioMarkerId'] - println(">> Probeset lookup: 1) probeset id: " + probesetId) + log.info(">> Probeset lookup: 1) probeset id: " + probesetId) // fixes -- 2014_10_31 //GeneSignatureItem item = new GeneSignatureItem(probesetId: probesetId, foldChgMetric: foldChg); @@ -265,10 +265,8 @@ public class GeneSignatureService { probesetId: probesetId, foldChgMetric: foldChg, bioDataUniqueId: bioDataUniqueId, - bioMarker: bioMarkerId, + bioMarker: bioMarkerId ); - - gsItems.add(item); } else { marker = null @@ -310,7 +308,7 @@ public class GeneSignatureService { while(br.ready()) { i++; record = br.readLine().trim(); - println("Line " + i +": " + record) + log.info("Line " + i +": " + record) if(record=="") continue; List items = new ArrayList(); @@ -349,7 +347,7 @@ public class GeneSignatureService { gsItems.add(new GeneSignatureItem(bioDataUniqueId: snpUid)) continue; } - println("WARN: invalid gene symbol: "+ geneSymbol) + log.warn("WARN: invalid gene symbol: "+ geneSymbol) invalidSymbols.add(geneSymbol); continue; } @@ -358,7 +356,7 @@ public class GeneSignatureService { { def bioMarkerId = marker[j].getAt(0); def uniqueId = marker[j].getAt(1) - println(">> Gene lookup: 1) marker id: "+bioMarkerId+"; 2) unique id: "+uniqueId) + log.info(">> Gene lookup: 1) marker id: "+bioMarkerId+"; 2) unique id: "+uniqueId) // create item instance GeneSignatureItem item = new GeneSignatureItem(bioMarker: BioMarker.read(bioMarkerId), bioDataUniqueId: uniqueId, foldChgMetric: foldChg); @@ -374,14 +372,14 @@ public class GeneSignatureService { gsItems.add(new GeneSignatureItem(bioDataUniqueId: snpUid)) continue; } - println("WARN: invalid gene symbol: "+ geneSymbol) + log.warn("WARN: invalid gene symbol: "+ geneSymbol) invalidSymbols.add(geneSymbol); continue; } def bioMarkerId = marker.getAt(0); def uniqueId = marker.getAt(1) - println(">> Gene lookup: 1) marker id: "+bioMarkerId+"; 2) unique id: "+uniqueId) + log.info(">> Gene lookup: 1) marker id: "+bioMarkerId+"; 2) unique id: "+uniqueId) // create item instance GeneSignatureItem item = new GeneSignatureItem(bioMarker: BioMarker.read(bioMarkerId), bioDataUniqueId: uniqueId, foldChgMetric: foldChg); @@ -397,7 +395,7 @@ public class GeneSignatureService { gsItems.add(new GeneSignatureItem(bioDataUniqueId: snpUid)) continue; } - println("WARN: invalid probe set id: "+ geneSymbol) + log.warn("WARN: invalid probe set id: "+ geneSymbol) invalidSymbols.add(geneSymbol); continue; } @@ -405,7 +403,7 @@ public class GeneSignatureService { //def probesetId = marker.getAt(0); def probesetId = marker.getAt(0); // def bioMarkerId = marker.getAt(1); - println(">> Probeset lookup: 1) probeset id: "+probesetId ) + log.info(">> Probeset lookup: 1) probeset id: "+probesetId ) // create item instance if this probeset exists in bio_assay_feature_group table, otherwise do nothing def ba = bio.BioAssayFeatureGroup.read(probesetId); @@ -517,7 +515,7 @@ public class GeneSignatureService { if (fileSchemaId == 3) marker = lookupProbesetBioAssociations(symbol, gs.techPlatform.accession) if (marker == null || marker.size() == 0) { - println("WARN: invalid gene symbol: " + symbol) + log.warn("WARN: invalid gene symbol: " + symbol) invalidSymbols.add(symbol) continue } @@ -525,7 +523,7 @@ public class GeneSignatureService { if (fileSchemaId != 3) { def bioMarkerId = marker.getAt(0); def uniqueId = marker.getAt(1) - println(">> Gene lookup: 1) marker id: " + bioMarkerId + "; 2) uniqued id: " + uniqueId + "; 3) FoldChgMetric: " + foldChgMetric) + log.info(">> Gene lookup: 1) marker id: " + bioMarkerId + "; 2) uniqued id: " + uniqueId + "; 3) FoldChgMetric: " + foldChgMetric) // create item instance GeneSignatureItem item = new GeneSignatureItem(bioMarker: BioMarker.get(bioMarkerId), bioDataUniqueId: uniqueId, foldChgMetric: foldChgMetric); @@ -561,12 +559,12 @@ public class GeneSignatureService { } /** - * create new GeneSignature and all dependendant objects from wizard + * create new GeneSignature and all dependant objects from wizard */ def saveWizard(GeneSignature gs, MultipartFile file) { def metricType = gs.foldChgMetricConceptCode?.bioConceptCode - println "metrictype " + metricType + log.info("metrictype " + metricType) def organism = gs.techPlatform?.organism def fileSchemaName = gs.fileSchema?.name @@ -591,7 +589,9 @@ public class GeneSignatureService { def nsave = savedInst; if (savedInst.uniqueId == null || savedInst.uniqueId == "") { // need to refresh this object - savedInst.updateUniqueId(); + if(gs.properties.uploadFile == 'Manual Item Entry') {savedInst.updateUniqueIdList()} + else{savedInst.updateUniqueId()} + //nsave = savedInst.save(flush:true) } @@ -599,8 +599,8 @@ public class GeneSignatureService { //if(nsave) updateGenSigItems(nsave) // link objects to search - searchKeywordService.updateGeneSignatureLink(nsave, GeneSignature.DOMAIN_KEY_GL, true) searchKeywordService.updateGeneSignatureLink(nsave, GeneSignature.DOMAIN_KEY, true) + searchKeywordService.updateGeneSignatureLink(nsave, GeneSignature.DOMAIN_KEY_GL, true) return nsave; } @@ -692,7 +692,7 @@ public class GeneSignatureService { def markers = BioData.executeQuery(qBuf); // try ext code lookup if necessary - //println(markers) + //log.info(markers) if (markers == null || markers.size() == 0 || markers.size() > 1) { query = new Query(mainTableAlias: "bm"); @@ -739,7 +739,7 @@ public class GeneSignatureService { def markers = BioData.executeQuery(qBuf); // try ext code lookup if necessary - // println(markers) + // log.info(markers) if(markers==null || markers.size()==0) { query = new Query(mainTableAlias:"bm"); @@ -814,12 +814,16 @@ public class GeneSignatureService { //"\tCAST(a.gene_id as string) = b.primary_external_id" + "\ta.probeset_id = '" + probeset.replace(" ", "") +"' "*/ ; + def qBuf = query.generateSQL(); - def marker = de.DeMrnaAnnotation.executeQuery(qBuf).asList(); + def markers = de.DeMrnaAnnotation.executeQuery(qBuf).asList(); + + // check for none or ambiguity + if (markers == null || markers.size() > 1) return null; /////return marker; - def mm = marker[0] + def mm = markers[0] return ['gplId':mm[0], 'probeId':mm[1], geneId:mm[2], probesetId:mm[3], bioMarkerId:mm[4]] } diff --git a/grails-app/services/HighDimensionQueryService.groovy b/grails-app/services/HighDimensionQueryService.groovy old mode 100644 new mode 100755 index 53a3cb256..ece730e15 --- a/grails-app/services/HighDimensionQueryService.groovy +++ b/grails-app/services/HighDimensionQueryService.groovy @@ -2,6 +2,7 @@ import com.recomdata.export.ExportColumn import com.recomdata.export.ExportRowNew import com.recomdata.export.ExportTableNew import groovy.sql.Sql +import org.transmart.searchapp.AuthUser import org.transmartproject.core.exceptions.InvalidRequestException import org.transmartproject.core.querytool.ConstraintByOmicsValue @@ -14,6 +15,7 @@ class HighDimensionQueryService { def dataSource def i2b2HelperService def highDimensionResourceService + def springSecurityService def getHighDimensionalConceptSet(String result_instance_id1, String result_instance_id2) { def result = [] @@ -40,15 +42,15 @@ class HighDimensionQueryService { sql.eachRow(sqlt, [resultInstanceId], { row -> def xml try { - xml = new XmlSlurper().parse(new StringReader(row.request_xml)) + xml = new XmlSlurper().parse(new StringReader(clobToString(row.request_xml))) } catch (exception) { throw new InvalidRequestException('Malformed XML document: ' + exception.message, exception) } xml.panel.each { p -> p.item.each { i -> - if (i.constrain_by_omics_value.size()) { - def constraint_params = [:] + def constraint_params = [:] + if (i.constrain_by_omics_value?.size()) { constraint_params.concept_key = i.item_key.toString() constraint_params.omics_selector = i.constrain_by_omics_value.omics_selector.toString() constraint_params.omics_value_type = i.constrain_by_omics_value.omics_value_type.toString() @@ -56,8 +58,12 @@ class HighDimensionQueryService { constraint_params.omics_value_constraint = i.constrain_by_omics_value.omics_value_constraint.toString() constraint_params.omics_projection_type = i.constrain_by_omics_value.omics_projection_type.toString() constraint_params.omics_property = i.constrain_by_omics_value.omics_property.toString() - concepts.add(constraint_params) } + else if (i.item_key) { + // high dimensional concept but no value filter + constraint_params.concept_key = i.item_key.toString() + } + concepts.add(constraint_params) } } }) @@ -70,6 +76,11 @@ class HighDimensionQueryService { */ def ExportTableNew addHighDimConceptDataToTable(ExportTableNew tablein, omics_constraint, String result_instance_id) { checkQueryResultAccess result_instance_id + def user = AuthUser.findByUsername(springSecurityService.getPrincipal().username) + + if (!i2b2HelperService.isValidOmicsParams(omics_constraint)) { + return i2b2HelperService.addConceptDataToTable(tablein, omics_constraint.concept_key, result_instance_id, user) + } def concept_key = omics_constraint.concept_key def selector = omics_constraint.omics_selector @@ -109,15 +120,30 @@ class HighDimensionQueryService { tablein.putRow(subject, newrow); } } - - //pad all the empty values for this column - for (ExportRowNew row : tablein.getRows()) { - if (!row.containsColumn(columnid)) { - row.put(columnid, "NULL"); - } - } } return tablein; } + + /** + * Converts a clob to a string for retuirned Oracle columns + */ + def String clobToString(clob) { + if (clob == null) { + return "" + }; + if (clob instanceof String) { + // postgres schema uses strings in some places oracle uses clobs + return clob + } + def buffer = new byte[1000]; + def num = 0; + def inStream = clob.asciiStream; + def out = new ByteArrayOutputStream(); + while ((num = inStream.read(buffer)) > 0) { + out.write(buffer, 0, num); + } + return new String(out.toByteArray()); + } + } diff --git a/grails-app/services/I2b2HelperService.groovy b/grails-app/services/I2b2HelperService.groovy old mode 100644 new mode 100755 index c2c012da2..60bc2e7ef --- a/grails-app/services/I2b2HelperService.groovy +++ b/grails-app/services/I2b2HelperService.groovy @@ -50,24 +50,30 @@ class I2b2HelperService { def sampleInfoService /** - * Gets a distribution of information from the patient dimention table for value columns + * Gets a distribution of information from the patient dimension table for value columns */ - def double[] getPatientDemographicValueDataForSubset(String col, String result_instance_id) { + def double[] getPatientDemographicValueDataForSubset(String col, String result_instance_id, AuthUser user) { checkQueryResultAccess result_instance_id + def authStudies = getAuthorizedStudies(user) + def authStudiesString = getSqlInString(authStudies) + // NOTE: UGLY, UGLY CODE - The sourcesystem_cd field, in the case that across trials data // exists, will be TrialId:SubjectId, where SubjectId is a unique subject id across trials. ArrayList values = new ArrayList() Set idSet = new HashSet() Sql sql = new Sql(dataSource) - String sqlt = """SELECT """ + col + """, sourcesystem_cd, patient_num + String sqlt = """SELECT """ + col + """, sourcesystem_cd, f.patient_num FROM patient_dimension f - WHERE patient_num IN ( + JOIN patient_trial pt ON pt.patient_num = f.patient_num + WHERE + pt.trial IN (""" + authStudiesString + """) AND + f.patient_num IN ( select distinct patient_num - from qt_patient_set_collection - where result_instance_id = ?)"""; + from qt_patient_set_collection + where result_instance_id = ?)"""; sql.eachRow(sqlt, [result_instance_id], { row -> -// log.trace("row: " + row[0] + "," + row[1] + "," + row[2]) + log.trace("row: " + row[0] + "," + row[1] + "," + row[2]) def id = row[2]; if (row[1]) { def holder = [] @@ -76,10 +82,10 @@ class I2b2HelperService { id = holder[1]; } } -// log.trace ("id = " + id) + log.trace ("id = " + id) if (!idSet.contains(id)) { idSet.add(id) - values.add(row[0]) + values.add(row[0]) } }); int count = 0; @@ -131,8 +137,16 @@ class I2b2HelperService { */ def String getShortNameFromKey(String concept_key) { String[] splits = concept_key.split("\\\\"); - - return splits[splits.length - 1] + + String concept_name = ""; + if (splits.length > 2) { + concept_name = "...\\" + splits[splits.length - 3] + "\\" + splits[splits.length - 2] + "\\" + splits[splits.length - 1]; + } else if (splits.length > 1) { + concept_name = "...\\" + splits[splits.length - 2] + "\\" + splits[splits.length - 1]; + } else { + concept_name = splits[splits.length - 1] + }; + return concept_name; } /** @@ -318,29 +332,30 @@ class I2b2HelperService { /** * Gets the distinct patient counts for the children of a parent concept key */ - def getChildrenWithPatientCountsForConcept(String concept_key) { + def getChildrenWithPatientCountsForConcept(String concept_key, AuthUser user) { log.debug "----------------- getChildrenWithPatientCountsForConcept" log.debug "concept_key = " + concept_key - def xTrialsTopNode = "\\\\" + ACROSS_TRIALS_TABLE_CODE + "\\" + ACROSS_TRIALS_TOP_TERM_NAME + "\\" - def xTrialsCaseFlag = isXTrialsConcept(concept_key) || (concept_key == xTrialsTopNode) + def xTrailsTopNode = "\\\\" + ACROSS_TRIALS_TABLE_CODE + "\\" + ACROSS_TRIALS_TOP_TERM_NAME + "\\" + def xTrialsCaseFlag = isXTrialsConcept(concept_key) || (concept_key == xTrailsTopNode) def counts = [:]; - if (xTrialsCaseFlag) { + if (xTrialsCaseFlag) { log.trace("XTrials for getConceptDistributionDataForConcept") def node = conceptsResourceService.getByKey(concept_key) def List childNodes = node.children for (OntologyTerm term: childNodes) { - counts.put(term.fullName,getObservationCountForXTrialsNode(term)) + counts.put(term.fullName,getObservationCountForXTrialsNode(term, user)) } } else { - Sql sql = new Sql(dataSource); - log.trace("Trying to get counts for parent_concept_path=" + keyToPath(concept_key)); - sql.eachRow("select * from CONCEPT_COUNTS where parent_concept_path = ?", [keyToPath(concept_key)], { row -> - log.trace "Found " << row.concept_path - counts.put(row.concept_path, row.patient_count) - }); + Sql sql = new Sql(dataSource); + log.trace("Trying to get counts for parent_concept_path=" + keyToPath(concept_key)); + sql.eachRow("select * from CONCEPT_COUNTS where parent_concept_path = ?", [keyToPath(concept_key)], { row -> + log.trace "Found " << row.concept_path + counts.put(row.concept_path, row.patient_count) + }); + } return counts; } @@ -371,7 +386,7 @@ class I2b2HelperService { * Gets the data associated with a value type concept from observation fact table * for display in a distribution histogram for a given subset */ - def getConceptDistributionDataForValueConcept(String concept_key, String result_instance_id) { + def getConceptDistributionDataForValueConcept(String concept_key, String result_instance_id, AuthUser user) { log.debug "----------------- getConceptDistributionDataForValueConcept" log.debug("Getting concept distribution data for value concept_key, " + concept_key + ", with results_instance_id = " + result_instance_id); @@ -379,38 +394,37 @@ class I2b2HelperService { log.trace("Access assured") def xTrialsCaseFlag = isXTrialsConcept(concept_key) - log.trace("Check for xTrials case = " + xTrialsCaseFlag) + log.trace("Check for xTrails case = " + xTrialsCaseFlag) ArrayList values = new ArrayList(); if (xTrialsCaseFlag) { - def data = fetchAcrossTiralsData(concept_key,result_instance_id) + def data = fetchAcrossTrialsData(concept_key,result_instance_id, user) data.each { def subject = it.subject def value = it.value values.add(value) } - } else { Sql sql = new Sql(dataSource); String concept_cd = getConceptCodeFromKey(concept_key); + String sqlt = "SELECT NVAL_NUM FROM OBSERVATION_FACT f WHERE CONCEPT_CD = '" + + concept_cd + "' AND PATIENT_NUM IN (select distinct patient_num " + + "from qt_patient_set_collection where result_instance_id = " + result_instance_id + ")"; - String sqlt = "SELECT NVAL_NUM FROM OBSERVATION_FACT f WHERE CONCEPT_CD = '" + - concept_cd + "' AND PATIENT_NUM IN (select distinct patient_num " + - "from qt_patient_set_collection where result_instance_id = " + result_instance_id + ")"; + log.trace("executing query: sqlt=" + sqlt); + try { + //sql.eachRow(sqlt, [concept_cd, result_instance_id], {row -> + sql.eachRow(sqlt, { row -> + if (row.NVAL_NUM != null) { + values.add(row.NVAL_NUM); + } + }); + } catch (Exception e) { + log.error("exception in getConceptDistributionDataForValueConcept: " + e.getMessage()) + } - log.trace("executing query: sqlt=" + sqlt); - try { - //sql.eachRow(sqlt, [concept_cd, result_instance_id], {row -> - sql.eachRow(sqlt, { row -> - if (row.NVAL_NUM != null) { - values.add(row.NVAL_NUM); - } - }); - } catch (Exception e) { - log.error("exception in getConceptDistributionDataForValueConcept: " + e.getMessage()) - } } log.debug("getConceptDistributionDataForValueConcept now finished: returning values n = " + values.size()); @@ -456,23 +470,24 @@ class I2b2HelperService { /** * Gets the count of a patient set from the result instance id */ - def Integer getPatientSetSize(String result_instance_id) { + def Integer getPatientSetSize(String result_instance_id, AuthUser user) { checkQueryResultAccess result_instance_id - + def authStudies = getAuthorizedStudies(user) + def authStudiesString = getSqlInString(authStudies) + log.debug("authorized patient set studies: " + authStudiesString) log.debug("getPatientSetSize(): result_instance_id = " + result_instance_id); - Integer i = 0; Sql sql = new Sql(dataSource); - // original code counted split_part(pd.sourcesystem_cd , ':', 2) - // but this is a postgres-only built-in function String sqlt = """select count(*) as patcount FROM ( - SELECT DISTINCT pd.sourcesystem_cd AS subject_id + SELECT DISTINCT split_part(pd.sourcesystem_cd , ':', 2) AS subject_id FROM qt_patient_set_collection ps JOIN patient_dimension pd ON ps.patient_num=pd.patient_num + JOIN patient_trial pt ON pt.patient_num = ps.patient_num WHERE ps.result_instance_id = CAST(? AS numeric) - ) patient_set""" + AND pt.trial IN (""" + authStudiesString + """) + ) pateint_set""" // String sqlt = """select count(distinct(patient_num)) as patcount // FROM qt_patient_set_collection // WHERE result_instance_id = CAST(? AS numeric)"""; @@ -603,6 +618,15 @@ class I2b2HelperService { return res; } + /** + * Check if a map contains all the keys an omics_params map should contain + * @param params the map to check + * @return True if the map contains all necessary keys, false otherwise + */ + def Boolean isValidOmicsParams(Map params) { + ['omics_selector', 'omics_projection_type', 'omics_property', 'omics_selector'].every {params?.containsKey(it)} + } + def Boolean nodeXmlRepresentsValueConcept(String xml) { Boolean res = false; @@ -630,17 +654,18 @@ class I2b2HelperService { return res; } - def HashMap getConceptDistributionDataForConcept(String concept_key, String result_instance_id) throws SQLException { + def HashMap getConceptDistributionDataForConcept(String concept_key, String result_instance_id, AuthUser user) throws SQLException { log.debug "----------------- start getConceptDistributionDataForConcept" checkQueryResultAccess result_instance_id def xTrialsCaseFlag = isXTrialsConcept(concept_key) def leafNodeFlag = isLeafConceptKey(concept_key) + def highDimNodeFlag = isHighDimensionalConceptKey(concept_key) def HashMap results = new LinkedHashMap() log.trace "input concept_key = " + concept_key - if (leafNodeFlag) { + if (leafNodeFlag && !highDimNodeFlag) { concept_key = getParentConceptKey(concept_key) } log.trace "lookup concept_key = " + concept_key @@ -656,7 +681,7 @@ class I2b2HelperService { } else { String fullname = concept_key.substring(concept_key.indexOf("\\", 2), concept_key.length()); - int i = getLevelFromKey(concept_key) + 1; + int i = getLevelFromKey(concept_key) + (highDimNodeFlag ? 0 : 1); Sql sql = new Sql(dataSource); String sqlt = """ SELECT DISTINCT c_name, c_fullname @@ -674,7 +699,7 @@ class I2b2HelperService { return results; } - def SortedMap> getConceptDistributionDataForConceptByTrial(String concept_key, String result_instance_id) throws SQLException { + def SortedMap> getConceptDistributionDataForConceptByTrial(String concept_key, String result_instance_id, AuthUser user) throws SQLException { log.debug "----------------- start getConceptDistributionDataForConceptByTrial" checkQueryResultAccess result_instance_id @@ -692,7 +717,7 @@ class I2b2HelperService { def baseNode = conceptsResourceService.getByKey(concept_key) log.trace(baseNode.class.name) - def List trials = trialsForResultSet(result_instance_id) + def List trials = trialsForResultSet(result_instance_id, user) log.trace("trials = " + trials) if (xTrialsCaseFlag) { @@ -709,7 +734,7 @@ class I2b2HelperService { // if not across trials; all parients in same trial/study def study = "Study" if (!trials.isEmpty()) study = trials[0] - results.put(study,getConceptDistributionDataForConcept(concept_key, result_instance_id)) + results.put(study,getConceptDistributionDataForConcept(concept_key, result_instance_id, user)) } log.trace("results.size() = " + results.size()) @@ -965,9 +990,12 @@ class I2b2HelperService { return count } - def Integer getObservationCountForXTrialsNode(AcrossTrialsOntologyTerm term_node) { - log.debug "-------- start getObservationCountForXTrialsNode" - log.debug "--------------------------- case: term_node only" + def Integer getObservationCountForXTrialsNode(AcrossTrialsOntologyTerm term_node, AuthUser user) { + log.debug "-------- start getObservationCountForXTrailsNode" + log.debug "--------------------------- case: term_nade only" + + def authStudies = getAuthorizedStudies(user) + def authStudiesString = getSqlInString(authStudies) def modifierList = [] def leafNodes = getAllXTrialsLeafNodes(term_node) @@ -990,10 +1018,12 @@ class I2b2HelperService { FROM observation_fact f JOIN patient_dimension pd ON f.patient_num=pd.patient_num + JOIN patient_trial pt ON pt.patient_num = f.patient_num WHERE + pt.trial IN (""" + authStudiesString + """) AND f.modifier_cd in ( """ + listToIN(modifierList.asList()) + """ ) AND f.concept_cd != 'SECURITY' - ) subjectList + ) as subjectList """ log.trace "sql text =" @@ -1037,7 +1067,7 @@ class I2b2HelperService { log.trace("\tresult_instance_id = " + result_instance_id) Sql sql = new Sql(dataSource); String sqlt = """ - select count(*) from ( + select count(*) as subjectCount from ( select distinct patient_num FROM i2b2demodata.observation_fact WHERE concept_cd IN ( @@ -1048,7 +1078,7 @@ class I2b2HelperService { select distinct patient_num from qt_patient_set_collection where result_instance_id = ?) - ) as subjectList + ) subjectList """ log.trace(sqlt); sql.eachRow(sqlt, [ @@ -1064,15 +1094,18 @@ class I2b2HelperService { /** * Fills the main demographic data in an export table for the grid */ - def ExportTableNew addAllPatientDemographicDataForSubsetToTable(ExportTableNew tablein, String result_instance_id, String subset) { + def ExportTableNew addAllPatientDemographicDataForSubsetToTable(ExportTableNew tablein, String result_instance_id, String subset, AuthUser user) { checkQueryResultAccess result_instance_id log.trace("Getting sampleCD's for patient number") def mapOfSampleCdsByPatientNum = buildMapOfSampleCdsByPatientNum(result_instance_id) + def authStudies = getAuthorizedStudies(user) + def authStudiesString = getSqlInString(authStudies) + log.trace("Adding patient demographic data to grid with result instance id:" + result_instance_id + " and subset: " + subset) Sql sql = new Sql(dataSource) - String sqlt = ''' + String sqlt = """ SELECT I.* FROM ( @@ -1085,14 +1118,16 @@ class I2b2HelperService { WHERE p.PATIENT_NUM IN ( SELECT - DISTINCT patient_num + DISTINCT ps.patient_num FROM - qt_patient_set_collection + qt_patient_set_collection ps + JOIN patient_trial pt ON pt.patient_num = ps.patient_num WHERE + pt.trial IN (""" + authStudiesString + """) AND result_instance_id = ? ) ) I ORDER BY - I.PATIENT_NUM'''; + I.PATIENT_NUM"""; log.trace "Initial grid query: $sqlt, riid: $result_instance_id" @@ -1202,7 +1237,7 @@ class I2b2HelperService { /** * Adds a column of data to the grid export table */ - def ExportTableNew addConceptDataToTable(ExportTableNew tablein, String concept_key, String result_instance_id) { + def ExportTableNew addConceptDataToTable(ExportTableNew tablein, String concept_key, String result_instance_id, AuthUser user) { checkQueryResultAccess result_instance_id log.debug "----------------- start addConceptDataToTable <<<<<< <<<<<< <<<<<<" @@ -1216,41 +1251,46 @@ class I2b2HelperService { /* As the column headers only show the (in many cases ambiguous) leaf part of the concept path, * showing the full concept path in the tooltip is much more informative. - * As no tooltip text is passed on to the GridView code, the value of the string columnid is used - * and shown as the tooltip text when hoovering over the column header in GridView. - * Explicitly passing a tooltip text to the GridView code removes the necessity to use this columnid value. + * As no tooltip text is passed on to the GridView code, the value of the string concept_key is used + * and shown as the tooltip text when hovering over the column header in GridView. + * Explicitly passing a tooltip text to the GridView code removes the necessity to use this concept_key value. * Removal of some undesired non-alpha-numeric characters from tooltip string * prevents display errors in GridView (drop down menu, columns not showing or cells not being filled). */ String columnid = concept_key.encodeAsSHA1() String columnname = getColumnNameFromKey(concept_key).replace(" ", "_") - String columntooltip = keyToPath(concept_key).replaceAll('[^a-zA-Z0-9_\\-\\\\]+','_') + String columntooltip = keyToPath(concept_key).replaceAll('[^a-zA-Z0-9_/\\-\\\\()\\[\\]]+','_') if (leafConceptFlag) { log.debug "----------------- this is a Leaf Node" - + def ExportColumn hascol; def valueLeafNodeFlag = isValueConceptKey(concept_key) def columnType = "string" - if (valueLeafNodeFlag){ + if (valueLeafNodeFlag) { columnType = "number" - } + } - // add the subject and columnid column to the table if its not there + // add the subject and columnid column to the table if it's not there if (tablein.getColumn("subject") == null) { tablein.putColumn("subject", new ExportColumn("subject", "Subject", "", "string")); } - if (tablein.getColumn(columnid) == null) { + + hascol = tablein.getColumnByBasename(columnname); // check existing column with basename + + if (tablein.getColumn(columnid) == null) { tablein.putColumn(columnid, new ExportColumn(columnid, columnname, "", columnType,columntooltip)); - } + if(hascol) + tablein.setColumnUnique(columnid); // make labels unique + } if (xTrialsCaseFlag) { - insertAcrossTrialsConceptDataIntoTable(columnid,concept_key,result_instance_id,valueLeafNodeFlag,tablein) - } + insertAcrossTrialsConceptDataIntoTable(columnid,concept_key,result_instance_id,valueLeafNodeFlag,tablein,user) + } else { insertConceptDataIntoTable(columnid, concept_key, result_instance_id, valueLeafNodeFlag, tablein) - } + } } else { // If a folder is dragged in, we want the contents of the folder to be added to the data @@ -1270,10 +1310,10 @@ class I2b2HelperService { // All children should be leaf categorical values if (item.children.any { - if (xTrialsCaseFlag) { - return !isLeafConceptKey(it) - } - return !isLeafConceptKey(it) || nodeXmlRepresentsValueConcept(it.metadataxml) + if (xTrialsCaseFlag) { + return !isLeafConceptKey(it) + } + return !isLeafConceptKey(it) || nodeXmlRepresentsValueConcept(it.metadataxml) }) { log.trace("Can not show data in gridview for folder nodes with mixed type of children") return tablein @@ -1283,7 +1323,7 @@ class I2b2HelperService { def columnType = "string" - // add the subject and columnid column to the table if its not there + // add the subject and columnid column to the table if it's not there if (tablein.getColumn("subject") == null) { tablein.putColumn("subject", new ExportColumn("subject", "Subject", "", "string")); } @@ -1297,27 +1337,28 @@ class I2b2HelperService { log.trace "Child key code: " + child.key def valueLeafNodeFlag = false concept_key = child.key - insertAcrossTrialsConceptDataIntoTable(columnid,concept_key,result_instance_id,valueLeafNodeFlag,tablein) + insertAcrossTrialsConceptDataIntoTable(columnid,concept_key,result_instance_id,valueLeafNodeFlag,tablein,user) } } else { log.debug "----------------- this is Folder Node - single study case" - // Store the concept paths to query - def paths = item.children*.fullName + // Store the concept paths to query + def paths = item.children*.fullName log.trace "Children Paths: " + paths - // Find the concept codes for the given children - def conceptCriteria = ConceptDimension.createCriteria() - def concepts = conceptCriteria.list { - 'in'("conceptPath", paths) - } + // Find the concept codes for the given children + def conceptCriteria = ConceptDimension.createCriteria() + def concepts = conceptCriteria.list { + 'in'("conceptPath", paths) + } log.trace "Children concepts: " + concepts*.conceptCode // Determine the patients to query - def patientIds = QtPatientSetCollection.executeQuery("SELECT q.patient.id FROM QtPatientSetCollection q WHERE q.resultInstance.id = ?", result_instance_id.toLong()) + def patientIds = QtPatientSetCollection.executeQuery( + "SELECT q.patient.id FROM QtPatientSetCollection q WHERE q.resultInstance.id = ?", result_instance_id.toLong()) patientIds = patientIds.collect { BigDecimal.valueOf(it) } // If nothing is found, return @@ -1327,20 +1368,25 @@ class I2b2HelperService { } // After that, retrieve all data entries for the children - def results = ObservationFact.executeQuery("SELECT o.patient.id, o.textValue FROM ObservationFact o WHERE conceptCode IN (:conceptCodes) AND o.patient.id in (:patientNums)", [conceptCodes: concepts*.conceptCode, patientNums: patientIds.collect { - it?.toLong() - }]) + def results = ObservationFact.executeQuery( + "SELECT o.patient.id, o.textValue FROM ObservationFact o WHERE conceptCode IN (:conceptCodes) AND o.patient.id in (select distinct q.patient.id from QtPatientSetCollection q where q.resultInstance.id = :resultInstanceId)", + [conceptCodes: concepts*.conceptCode, resultInstanceId: result_instance_id.toLong()]) +// def results = ObservationFact.executeQuery( +// "SELECT o.patient.id, o.textValue FROM ObservationFact o WHERE conceptCode IN (:conceptCodes) AND o.patient.id in (:patientNums)", +// [conceptCodes: concepts*.conceptCode, patientNums: patientIds.collect { +// it?.toLong() +// }]) log.trace "results length: " + results.length - results.each { row -> + results.each { row -> - /*If I already have this subject mark it in the subset column as belonging to both subsets*/ - String subject = row[0] - String value = row[1] - if (value == null) { - value = "Y"; - } + /*If I already have this subject mark it in the subset column as belonging to both subsets*/ + String subject = row[0] + String value = row[1] + if (value == null) { + value = "Y"; + } if (tablein.containsRow(subject)) /*should contain all subjects already if I ran the demographics first*/ { tablein.getRow(subject).put(columnid, value.toString()); } else /*fill the row*/ { @@ -1404,9 +1450,11 @@ class I2b2HelperService { return dataList } - def insertConceptDataIntoTable(columnid,concept_key,result_instance_id,valueLeafNodeFlag,tablein) { + def insertConceptDataIntoTable(String columnid, String concept_key, String result_instance_id, + Boolean valueLeafNodeFlag, ExportTableNew tablein) { log.debug "----------------- insertConceptDataIntoTable" - log.debug "for columnid = " + columnid + log.debug "for columnid " + columnid + log.debug "and concept_key " + concept_key def data = fetchConceptData(concept_key,result_instance_id) data.each{ def subject = it.subject @@ -1422,12 +1470,15 @@ class I2b2HelperService { } } - def fetchAcrossTiralsData(concept_key,result_instance_id){ - log.debug "----------------- fetchAcrossTiralsData" + def fetchAcrossTrialsData(concept_key,result_instance_id, user){ + log.debug "----------------- fetchAcrossTrialsData" def valueLeafNodeFlag = isValueConceptKey(concept_key) def dataList = [] + def authStudies = getAuthorizedStudies(user) + def authStudiesString = getSqlInString(authStudies) + def itemProbe = conceptsResourceService.getByKey(concept_key) String modifier_cd = itemProbe.modifierDimension.code @@ -1446,9 +1497,11 @@ class I2b2HelperService { WHERE modifier_cd = ? AND concept_cd != 'SECURITY' - AND PATIENT_NUM IN (select distinct patient_num - from qt_patient_set_collection - where result_instance_id = ?) + AND PATIENT_NUM IN (select distinct ps.patient_num + from qt_patient_set_collection ps + JOIN patient_trial pt ON pt.patient_num = ps.patient_num + WHERE pt.trial IN (""" + authStudiesString + """) + AND result_instance_id = ?) """ sql.eachRow(sqlt, [modifier_cd, result_instance_id], { row -> @@ -1467,9 +1520,11 @@ class I2b2HelperService { WHERE modifier_cd = ? AND concept_cd != 'SECURITY' - AND PATIENT_NUM IN (select distinct patient_num - from qt_patient_set_collection - where result_instance_id = ?) + AND PATIENT_NUM IN (select distinct ps.patient_num + from qt_patient_set_collection ps + JOIN patient_trial pt ON pt.patient_num = ps.patient_num + WHERE pt.trial IN (""" + authStudiesString + """) + AND result_instance_id = ?) """ sql.eachRow(sqlt, [modifier_cd, result_instance_id], { row -> @@ -1481,10 +1536,10 @@ class I2b2HelperService { dataList } - def insertAcrossTrialsConceptDataIntoTable(columnid,concept_key,result_instance_id,valueLeafNodeFlag,tablein) { + def insertAcrossTrialsConceptDataIntoTable(columnid,concept_key,result_instance_id,valueLeafNodeFlag,tablein, user) { log.debug "----------------- insertAcrossTrialsConceptDataIntoTable <<<< ---- <<<<<" - def data = fetchAcrossTiralsData(concept_key,result_instance_id) + def data = fetchAcrossTrialsData(concept_key,result_instance_id,user) data.each{ def subject = it.subject def value = it.value @@ -1504,11 +1559,13 @@ class I2b2HelperService { /** * Gets a distribution of information from the patient dimension table * */ - def HashMap getPatientDemographicDataForSubset(String col, String result_instance_id) { + def HashMap getPatientDemographicDataForSubset(String col, String result_instance_id, AuthUser user) { log.trace("in getPatientDemographicDataForSubset ...") log.trace("args: col = " + col + ", result_instance_id = " + result_instance_id) checkQueryResultAccess result_instance_id + def authStudies = getAuthorizedStudies(user) + def authStudiesString = getSqlInString(authStudies) HashMap results = new LinkedHashMap(); Sql sql = new Sql(dataSource) @@ -1521,8 +1578,11 @@ class I2b2HelperService { FROM qt_patient_set_collection ps JOIN patient_dimension pd ON ps.patient_num=pd.patient_num AND result_instance_id = ? + JOIN patient_trial pt ON pt.patient_num = ps.patient_num + WHERE pt.trial IN (""" + authStudiesString + """) ) base GROUP BY cat + ORDER BY cat """; // String sqlt = """SELECT a.cat as demcategory, COALESCE(b.demcount,0) as demcount FROM @@ -1632,7 +1692,7 @@ class I2b2HelperService { // In the current ETL, deapp.de_xtrial_child_map, is not populated! /*get all distinct concepts for analysis from both subsets into hashmap*/ - log.debug("lookupParentConcept" + conceptPath); + log.debug("lookupParentConcept " + conceptPath); try { Sql sql = new Sql(dataSource); String sqlQuery = """select parent_cd from deapp.de_xtrial_child_map xcm @@ -5491,7 +5551,7 @@ class I2b2HelperService { * Gets the data associated with a value type concept from observation fact table * for display in a distribution histogram for a given subset */ - def getConceptDistributionDataForValueConceptByTrial(String concept_key, String result_instance_id) { + def getConceptDistributionDataForValueConceptByTrial(String concept_key, String result_instance_id, AuthUser user) { log.debug "----------------- getConceptDistributionDataForValueConceptByTrial" checkQueryResultAccess result_instance_id @@ -5502,6 +5562,9 @@ class I2b2HelperService { def trialdata = [:]; + def authStudies = getAuthorizedStudies(user) + def authStudiesString = getSqlInString(authStudies) + if (result_instance_id != null && result_instance_id != "") { log.debug("Getting concept distribution data for value concept:" + concept_key); log.trace "concept_key = " + concept_key @@ -5521,7 +5584,8 @@ class I2b2HelperService { SELECT TRIAL, NVAL_NUM FROM OBSERVATION_FACT f INNER JOIN PATIENT_TRIAL t ON f.PATIENT_NUM=t.PATIENT_NUM WHERE modifier_cd = ? - AND concept_cd != 'SECURITY' + AND concept_cd != 'SECURITY' AND + t.trial IN (""" + authStudiesString + """) AND f.PATIENT_NUM IN (select distinct patient_num from qt_patient_set_collection where result_instance_id = ?) @@ -5653,6 +5717,47 @@ class I2b2HelperService { return admin; } + /** + * Gets a list of studies the user is authorized to view + */ + def getAuthorizedStudies(AuthUser user) { + def admin = isAdmin(user) + def allStudies = getAllStudiesWithTokens() + def tokenmap = getSecureTokensWithAccessForUser(user) + def authStudies = [] + + if (admin) { + authStudies = allStudies.keySet() + } + else { + allStudies.each { key, value -> + if (value == "EXP:PUBLIC") { + authStudies.add(key) + } else if (tokenmap.containsKey("EXP:" + key)) { + authStudies.add(key) + } + } + } + return authStudies + } + + def getSqlInString(inList) { + if (inList.getAt(0).isNumber()) + return inList.join(",") + else + return inList.collect{"'$it'"}.join(",") + } + + def getAllStudiesWithTokens() { + def studies = [:] + Sql sql = new Sql(dataSource) + String sqlt = "SELECT sourcesystem_cd, secure_obj_token FROM i2b2metadata.i2b2_secure WHERE c_hlevel = 1" + sql.eachRow(sqlt, [], { row -> + studies.put(row.sourcesystem_cd, row.secure_obj_token); + }) + return studies; + } + /** * Gets the children paths concepts of a parent key */ @@ -5681,7 +5786,7 @@ class I2b2HelperService { def tokens = [:] try { sql.eachRow(""" - SELECT sourcesystem_cd, secure_obj_token FROM i2b2metadata.i2b2_SECURE WHERE sourcesystem_cd IN (""" + listToIN(studyIds.asList()) + """) AND c_hlevel = 1 + SELECT sourcesystem_cd, secure_obj_token FROM i2b2metadata.i2b2_SECURE WHERE sourcesystem_cd IN (""" + listToIN(studyIds.asList()) + """) AND c_visualattributes = 'FAS' """, { row -> tokens += [(row.sourcesystem_cd): row.secure_obj_token] }) @@ -6399,26 +6504,32 @@ class I2b2HelperService { return conceptsResourceService.getByKey(key).getStudy().getId() } - def List trialsForResultSet (String result_instance_id) { - checkQueryResultAccess result_instance_id + def trialsForResultSet (String result_instance_id, AuthUser user) { + //checkQueryResultAccess result_instance_id + def trials = [:] + def authTrials = [] - List trials = new ArrayList(); Sql sql = new Sql(dataSource) String sqlt = """ - SELECT distinct trial + SELECT distinct trial, SECURE_OBJ_TOKEN FROM patient_trial pt JOIN qt_patient_set_collection psc ON pt.patient_num=psc.patient_num WHERE psc.result_instance_id = ? ORDER BY trial """ - log.trace(sqlt); + log.trace(sqlt) sql.eachRow(sqlt, [result_instance_id], {row -> - trials.add(row.trial) + trials.put(row.trial, row.secure_obj_token) }) - return trials -} + def trialsAccess = getAccess(trials, user) + trialsAccess.each { trial, access -> + if (access != 'Locked') + authTrials.add(trial) + } + return authTrials + } } diff --git a/grails-app/services/MonitorService.groovy b/grails-app/services/MonitorService.groovy new file mode 100755 index 000000000..b8bbea7dc --- /dev/null +++ b/grails-app/services/MonitorService.groovy @@ -0,0 +1,66 @@ +import groovy.sql.Sql + +class MonitorService { + + def grailsApplication + def dataSource + def rserveStatusService + def solrStatusService + + def getObservationCount() { + Sql sql = new Sql(dataSource) + String sqlText = "SELECT COUNT(*) FROM i2b2demodata.observation_fact" + def result = sql.firstRow(sqlText) + return result.values()[0] + } + + def getNephDataAttestationExists() { + Sql sql = new Sql(dataSource) + String sqlText = """ + SELECT EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = 'searchapp' + AND table_name = 'data_attestation' + ) + """ + def result = sql.firstRow(sqlText) + return result.values()[0] + } + + def getLoadedStudies() { + Sql sql = new Sql(dataSource) + String sqlText = "SELECT c_name FROM i2b2metadata.i2b2 WHERE c_visualattributes = 'FAS'" + return sql.rows(sqlText).collect({ it.getAt(0)}).join(", ") + } + + def getAppVersion() { + return grailsApplication.metadata['app.version'] + } + + def getDatabaseStatusText(isDBError, errorMessage) { + if (!isDBError) { + return "Database Connection Status: " + "OK
" + + "Observation Count: " + observationCount + "
" + + "Loaded Studies: " + loadedStudies + "
" + + "Data Attestation Exists: " + nephDataAttestationExists + "
" + } + else { + return "Database Connection Status: " + "ERROR
" + + "Last Database Error Message: " + errorMessage + "
" + } + } + + def getOtherStatusText() { + return "App Version: " + appVersion + "

" + + "RServe Status
" + + "--------------------
" + + rserveStatusService.status.toHTMLString() + "
" + + "Solr Status
" + + "--------------------
" + + solrStatusService.status.toHTMLString() + } + + + +} diff --git a/grails-app/services/com/recomdata/asynchronous/GenePatternService.groovy b/grails-app/services/com/recomdata/asynchronous/GenePatternService.groovy index 348bfea58..8b3fcb850 100644 --- a/grails-app/services/com/recomdata/asynchronous/GenePatternService.groovy +++ b/grails-app/services/com/recomdata/asynchronous/GenePatternService.groovy @@ -52,11 +52,11 @@ class GenePatternService implements Job { log.info("${jobName} has been triggered to run ") def jobDataMap = jobDetail.getJobDataMap() - if (log.isDebugEnabled()) { - jobDataMap.getKeys().each { _key -> - log.debug("\t${_key} -> ${jobDataMap[_key]}") - } - } +// if (log.isDebugEnabled()) { +// jobDataMap.getKeys().each { _key -> +// log.debug("\t${_key} -> ${jobDataMap[_key]}") +// } +// } // Note: this is a superset of all parameters for all of the different analysis types. // Some will be present, others will not depending on the type of job diff --git a/grails-app/services/org/transmart/SearchKeywordService.groovy b/grails-app/services/org/transmart/SearchKeywordService.groovy index 5d3070db3..fa6dd4dd2 100644 --- a/grails-app/services/org/transmart/SearchKeywordService.groovy +++ b/grails-app/services/org/transmart/SearchKeywordService.groovy @@ -440,33 +440,36 @@ public class SearchKeywordService { def updateGeneSignatureLink(GeneSignature gs, String domainKey, boolean bFlush) { // find keyword record SearchKeyword keyword = SearchKeyword.findByBioDataIdAndDataCategory(gs.id, domainKey) - println("INFO: retrieved " + keyword) + log.info("updateGeneSignatureLink: domainKey ${domainKey} concept ${gs.foldChgMetricConceptCode.bioConceptCode} retrieved ${keyword}") // delete search keywords if (gs.deletedFlag || (domainKey == GeneSignature.DOMAIN_KEY_GL && gs.foldChgMetricConceptCode.bioConceptCode != 'NOT_USED') || (domainKey == GeneSignature.DOMAIN_KEY && gs.foldChgMetricConceptCode.bioConceptCode == 'NOT_USED')) { + log.info("updateGeneSignatureLink delete keyword") if (keyword != null) keyword.delete(flush: bFlush) } else { // add if does not exist if (keyword == null) { + log.info("updateGeneSignatureLink create keyword") keyword = createSearchKeywordFromGeneSig(gs, domainKey) } else { // update keyword + log.info("updateGeneSignatureLink update keyword ${gs.name} ") keyword.keyword = gs.name keyword.ownerAuthUserId = gs.publicFlag ? null : gs.createdByAuthUser.id keyword.terms.each { - println("INFO: " + it) + log.info("INFO: " + it) it.keywordTerm = gs.name.toUpperCase() it.ownerAuthUserId = gs.publicFlag ? null : gs.createdByAuthUser.id - //println("INFO: setting owner to: "+it.ownerAuthUserId) + //log.info("INFO: setting owner to: "+it.ownerAuthUserId) } } keyword.validate() if (keyword.hasErrors()) { - println("WARN: SearchKeyword validation error!") - keyword.errors.each { println it } + log.info("WARN: SearchKeyword validation error!") + keyword.errors.each {log.info("keyword error: ${it}")} } - println("INFO: trying to save SearchKeyword") + log.info("INFO: trying to save SearchKeyword") keyword.save(flush: bFlush) } } diff --git a/grails-app/services/transmartapp/OntologyService.groovy b/grails-app/services/transmartapp/OntologyService.groovy old mode 100644 new mode 100755 index 318b732b0..5dd533f4a --- a/grails-app/services/transmartapp/OntologyService.groovy +++ b/grails-app/services/transmartapp/OntologyService.groovy @@ -56,7 +56,7 @@ class OntologyService { log.debug(nodeQuery) myCount = i2b2.OntNode.executeQuery(countQuery)[0] - myNodes = i2b2.OntNode.executeQuery(nodeQuery, [max: 100]) + myNodes = i2b2.OntNode.executeQuery(nodeQuery) } else { @@ -69,7 +69,7 @@ class OntologyService { def nodeQuery = "SELECT o from i2b2.OntNode o WHERE o.sourcesystemcd IN (:scdArg) AND (_searchterms_) AND o.visualattributes NOT like '" + visualAttrHiddenWild + "'" nodeQuery = nodeQuery.replace("_searchterms_", searchtermstring) - myNodes = i2b2.OntNode.executeQuery(nodeQuery, [scdArg: allSystemCds], [max: 100]) + myNodes = i2b2.OntNode.executeQuery(nodeQuery, [scdArg: allSystemCds]) } //} diff --git a/grails-app/services/transmartapp/SolrFacetService.groovy b/grails-app/services/transmartapp/SolrFacetService.groovy index c8fd06cf9..38318dcc8 100644 --- a/grails-app/services/transmartapp/SolrFacetService.groovy +++ b/grails-app/services/transmartapp/SolrFacetService.groovy @@ -9,6 +9,7 @@ import groovy.xml.StreamingMarkupBuilder import org.json.JSONObject import org.transmart.biomart.BioMarker import org.transmart.biomart.BioMarkerExpAnalysisMV +import org.transmartproject.db.support.InQuery class SolrFacetService { @@ -172,9 +173,7 @@ class SolrFacetService { for (uid in geneUids) { bioMarkers.push(BioMarker.findByUniqueId(uid)) } - def result = BioMarkerExpAnalysisMV.createCriteria().list { - 'in'('marker', bioMarkers) - } + def result = InQuery.addIn(BioMarkerExpAnalysisMV.createCriteria(), 'marker', bioMarkers).list() searchLog += "Found " + result.size() + " analysis matches" diff --git a/grails-app/views/RWG/_boxSearch.gsp b/grails-app/views/RWG/_boxSearch.gsp old mode 100644 new mode 100755 index 90340a7ec..1e22b99a9 --- a/grails-app/views/RWG/_boxSearch.gsp +++ b/grails-app/views/RWG/_boxSearch.gsp @@ -13,9 +13,6 @@ -
- Filter -
diff --git a/grails-app/views/chart/_conceptsAnalysis.gsp b/grails-app/views/chart/_conceptsAnalysis.gsp old mode 100644 new mode 100755 index 97b5a4175..e0f0d0a41 --- a/grails-app/views/chart/_conceptsAnalysis.gsp +++ b/grails-app/views/chart/_conceptsAnalysis.gsp @@ -9,14 +9,20 @@
${concept.value?.commons?.conceptKey ?: ""}
-
+
${concept.value?.commons?.testmessage}
- With a p-value of ${concept.value?.commons.pvalue} for a T-stat at ${concept.value?.commons.tstat} + + + +
p-value${concept.value?.commons.pvalue}
t-statistic${concept.value?.commons.tstat}
- With a p-value of ${concept.value?.commons.pvalue} for a χ² at ${concept.value?.commons.chisquare} + + + +
p-value${concept.value?.commons.pvalue}
χ²${concept.value?.commons.chisquare}
Variable arithmetically undefined (NaN) diff --git a/grails-app/views/chart/_valueComparison.gsp b/grails-app/views/chart/_valueComparison.gsp index 0c05cb939..4934dbe14 100644 --- a/grails-app/views/chart/_valueComparison.gsp +++ b/grails-app/views/chart/_valueComparison.gsp @@ -7,35 +7,39 @@ ${subsets?.commons?."${prefix}Histo" ?: ''} - - - - -
- - Subset ${s.key} - -
- - - - - - - - - - - - - - - - -
Mean: ${(stats.mean?.round(2) =~ /NaN/).replaceAll("-")}
Median: ${(stats.median?.round(2) =~ /NaN/).replaceAll("-")}
IQR: ${((stats.q3 - stats.q1).round(2) =~ /NaN/).replaceAll("-")}
SD: ${(Statistics.getStdDev((Number [])p?."${prefix}Data".toArray()).round(2) =~ /NaN/).replaceAll("-")}
SD: -
Data Points: ${p?."${prefix}Data"?.size()}
- -
-
+ +
+ + + +
+
+ + Subset ${s.key} + +
+ + + + + + + + + + + + + + + + +
Mean: ${(stats.mean?.round(2) =~ /NaN/).replaceAll("-")}
Median: ${(stats.median?.round(2) =~ /NaN/).replaceAll("-")}
IQR: ${((stats.q3 - stats.q1).round(2) =~ /NaN/).replaceAll("-")}
SD: ${(Statistics.getStdDev((Number [])p?."${prefix}Data".toArray()).round(2) =~ /NaN/).replaceAll("-")}
SD: -
Data Points: ${p?."${prefix}Data"?.size()}
+
+
+
+
+ ${subsets?.commons?."${prefix}Plot" ?: ''} diff --git a/grails-app/views/dataAttestation/attestation.gsp b/grails-app/views/dataAttestation/attestation.gsp new file mode 100755 index 000000000..157c48911 --- /dev/null +++ b/grails-app/views/dataAttestation/attestation.gsp @@ -0,0 +1,41 @@ + + + + + ${grailsApplication.config.com.recomdata.appTitle} Attestation Agreement + + + +
+
+ Transmart +

ATTENTION: Users of ${grailsApplication.config?.com?.recomdata?.appTitle}

+
+ ${grailsApplication.config?.dataAttestationText} +
+
+ + + + +
+
+
+ diff --git a/grails-app/views/datasetExplorer/datasetExplorer.gsp b/grails-app/views/datasetExplorer/datasetExplorer.gsp old mode 100644 new mode 100755 index 94824de51..cab0781db --- a/grails-app/views/datasetExplorer/datasetExplorer.gsp +++ b/grails-app/views/datasetExplorer/datasetExplorer.gsp @@ -42,7 +42,7 @@ NumOfSubsets: 2, NumOfQueryCriteriaGroups: 20, NumOfQueryCriteriaGroupsAtStart: 3, - MaxSearchResults: 100, + MaxSearchResults: 500, ONTUrl: '', usePMHost: '${grailsApplication.config.com.recomdata.datasetExplorer.usePMHost}', Config: 'jj', @@ -95,6 +95,7 @@ sampleExplorerEnabled: ${!!grailsApplication.config.ui.tabs.sampleExplorer.show}, metacoreAnalyticsEnabled: ${!!grailsApplication.config.com.thomsonreuters.transmart.metacoreAnalyticsEnable}, metacoreUrl: '${grailsApplication.config.com.thomsonreuters.transmart.metacoreURL}', + xnatViewerEnabled: ${!!grailsApplication.config.org.transmart.xnatViewerEnable}, AnalysisHasBeenRun: false, ResultSetRegionParams: {}, currentReportCodes: [], diff --git a/grails-app/views/geneSignature/_list_summary_record.gsp b/grails-app/views/geneSignature/_list_summary_record.gsp new file mode 100644 index 000000000..6d7956bb5 --- /dev/null +++ b/grails-app/views/geneSignature/_list_summary_record.gsp @@ -0,0 +1,46 @@ + + + + + + + + + detail ${gs.name?.replaceAll("'","\\\\'").encodeAsHTML()} + ${gs.createdByAuthUser.userRealName?.encodeAsHTML()} + + ${gs.publicFlag ? 'Yes' : 'No'} + + ${ctLkup?.getAt(1)} + + + 0 + + + + diff --git a/grails-app/views/geneSignature/gene_signiture_gene1.txt b/grails-app/views/geneSignature/gene_signature_gene1.txt similarity index 100% rename from grails-app/views/geneSignature/gene_signiture_gene1.txt rename to grails-app/views/geneSignature/gene_signature_gene1.txt diff --git a/grails-app/views/geneSignature/gene_signiture_gene2.txt b/grails-app/views/geneSignature/gene_signature_gene2.txt similarity index 100% rename from grails-app/views/geneSignature/gene_signiture_gene2.txt rename to grails-app/views/geneSignature/gene_signature_gene2.txt diff --git a/grails-app/views/geneSignature/gene_signiture_gene3.txt b/grails-app/views/geneSignature/gene_signature_gene3.txt similarity index 100% rename from grails-app/views/geneSignature/gene_signiture_gene3.txt rename to grails-app/views/geneSignature/gene_signature_gene3.txt diff --git a/grails-app/views/geneSignature/gene_signiture_probeset1.txt b/grails-app/views/geneSignature/gene_signature_probeset1.txt similarity index 100% rename from grails-app/views/geneSignature/gene_signiture_probeset1.txt rename to grails-app/views/geneSignature/gene_signature_probeset1.txt diff --git a/grails-app/views/geneSignature/gene_signiture_probeset2.txt b/grails-app/views/geneSignature/gene_signature_probeset2.txt similarity index 100% rename from grails-app/views/geneSignature/gene_signiture_probeset2.txt rename to grails-app/views/geneSignature/gene_signature_probeset2.txt diff --git a/grails-app/views/geneSignature/gene_signiture_probeset3.txt b/grails-app/views/geneSignature/gene_signature_probeset3.txt similarity index 100% rename from grails-app/views/geneSignature/gene_signiture_probeset3.txt rename to grails-app/views/geneSignature/gene_signature_probeset3.txt diff --git a/grails-app/views/geneSignature/list.gsp b/grails-app/views/geneSignature/list.gsp index 5b341b67b..52d3b336b 100644 --- a/grails-app/views/geneSignature/list.gsp +++ b/grails-app/views/geneSignature/list.gsp @@ -88,7 +88,7 @@
${flash.message}

-

Gene Signature List    +

Gene Signature Lists    Help

@@ -147,12 +147,12 @@
- @@ -162,6 +162,7 @@ + @@ -180,6 +181,7 @@ + diff --git a/grails-app/views/geneSignature/wizard_list.gsp b/grails-app/views/geneSignature/wizard_list.gsp new file mode 100644 index 000000000..d416f36b2 --- /dev/null +++ b/grails-app/views/geneSignature/wizard_list.gsp @@ -0,0 +1,294 @@ + + + + + + + + Gene List Edit + + + Gene List Create + + + $j = jQuery.noConflict(); + + + + jQuery(document).ready(function() { + var pasteContent; + + jQuery('#biomarkerList').on('change', '.biomarkerEntry', function(event) { + var name = jQuery(this).attr('name'); + var index = parseInt(name.substring(10)); + checkGene(index); + }); + + jQuery('#biomarkerList').on('keypress', '.biomarkerEntry', function(event) { + if (event.which == 13) { + event.preventDefault(); + + var name = jQuery(this).attr('name'); + var startingIndex = parseInt(name.substring(10)); + createNewInputIfRequired(startingIndex); + $j('#biomarker_' + (startingIndex+1)).focus(); + } + }); + + jQuery('#biomarkerList').on('paste', '.biomarkerEntry', function(event) { + pasteContent = null; + var startingInput = jQuery(this); + startingInput.val(''); + if (window.clipboardData) { + pasteContent = window.clipboardData.getData('Text'); + } + //Paste is ABOUT TO happen, so set timeout + setTimeout(function() { + if (pasteContent == null) { + pasteContent = startingInput.val(); + } + var pasteList = pasteContent.split(","); + if (pasteList.length < 2) { + pasteList = pasteContent.split("\n"); + } + var name = startingInput.attr('name'); + var startingIndex = parseInt(name.substring(10)); + for (var i = 0; i < pasteList.length; i++) { + var currentIndex = startingIndex + i; + $j('#biomarker_' + currentIndex).val(pasteList[i].trim()); + checkGene(currentIndex); + } + jQuery('#pasteSource').val(''); + }, 1); + }); + + jQuery('#biomarkerList').on('click', '.biomarkerDelete', function(event) { + var name = jQuery(this).attr('name'); + var index = parseInt(name.substring(10)); + jQuery('#biomarker_' + index).val(''); + jQuery('#geneCheckIcon' + index).removeClass('loading').removeClass('success').removeClass('failure').text(''); + }); + + <%-- Add checks for all fields if this is an edit --%> + + + + checkGene(${n}); + + + + }); + + function checkBiomarkerValues() { + var foundEntry = false; + var biomarkerFields = jQuery(".biomarkerEntry"); + + for (var n = 0; n < biomarkerFields.size(); n++) { + var textContent = jQuery(biomarkerFields[n]).val(); + if (textContent != null && textContent.trim() != "") { + foundEntry = true; + break; + } + } + return foundEntry; + } + + function validate() { + + var errorMsg = ""; + // list name required + if(document.geneSignatureFrm.name.value=="") { + errorMsg = "You must specify a list name"; + } + + if(document.geneSignatureFrm.uploadFile.value=="" && !checkBiomarkerValues()) + errorMsg = errorMsg + "\n- Please select a file, or manually enter a gene list"; + + if(document.geneSignatureFrm.uploadFile.value!="" && checkBiomarkerValues()) + errorMsg = errorMsg + "\n- You have both specified a file and manually entered a list"; + // if no errors, continue submission + if(errorMsg=="") return true; + + alert("Please correct the following errors:\n" + errorMsg); + return false; + } + + function createNewInputIfRequired(index) { + var removeImage = "${resource(dir:'images',file:'remove.png')}" + var newIndex = index+1; + var checkInputField = jQuery('#biomarker_' + newIndex); + if (checkInputField.length == 0) { + var newtr = jQuery('').attr('id', 'new_item_' + newIndex); + var newtdTextArea = jQuery('
Name Author Date CreatedPublic # Genes  
Name Author Date CreatedPublic # Genes  
').append(jQuery('