├── .gitignore
├── .jshintrc
├── .settings
└── .gitignore
├── .travis.yml
├── CHANGELOG
├── LICENSE
├── README.md
├── application.properties
├── doc
└── CONTRIBUTING.md
├── grails-app
├── conf
│ ├── BootStrap.groovy
│ ├── BuildConfig.groovy
│ ├── Config.groovy
│ ├── DataSource.groovy
│ ├── SmartRResources.groovy
│ ├── UrlMappings.groovy
│ └── spring
│ │ └── resources.groovy
├── controllers
│ └── smartR
│ │ └── plugin
│ │ ├── IpaConnectorController.groovy
│ │ ├── SmartRController.groovy
│ │ └── rest
│ │ ├── RSessionController.groovy
│ │ ├── ScriptExecutionController.groovy
│ │ └── SmartRTestController.groovy
├── i18n
│ └── messages.properties
└── views
│ ├── error.gsp
│ ├── layouts
│ ├── _boxplot.gsp
│ ├── _correlation.gsp
│ ├── _heatmap.gsp
│ ├── _ipaconnector.gsp
│ ├── _linegraph.gsp
│ ├── _patientmapper.gsp
│ ├── _volcanoplot.gsp
│ └── smartR.gsp
│ └── smartR
│ └── index.gsp
├── h2_init.sql
├── karma.conf.js
├── package.json
├── scripts
├── _Events.groovy
├── _Install.groovy
├── _Uninstall.groovy
└── _Upgrade.groovy
├── smartRGrailsPlugin.groovy
├── src
└── groovy
│ └── heim
│ ├── SmartRExecutorService.groovy
│ ├── SmartRRuntimeConstants.groovy
│ ├── jobs
│ ├── JobInstance.groovy
│ └── JobInstanceImpl.groovy
│ ├── rserve
│ ├── GenericJavaObjectAsJsonRFunctionArg.groovy
│ ├── RConnectionProvider.groovy
│ ├── RFunctionArg.groovy
│ ├── RScriptOutputManager.groovy
│ ├── RScriptsSynchronizer.groovy
│ ├── RServeSession.groovy
│ └── RUtil.java
│ ├── session
│ ├── SessionContext.groovy
│ ├── SessionFiles.groovy
│ ├── SessionService.groovy
│ ├── SmartRSessionScope.java
│ ├── SmartRSessionScopeInterfaced.java
│ └── SmartRSessionSpringScope.groovy
│ └── tasks
│ ├── AbstractTask.groovy
│ ├── DataFetchTask.groovy
│ ├── DataFetchTaskFactory.groovy
│ ├── JobTasksService.groovy
│ ├── RScriptExecutionTask.groovy
│ ├── RScriptExecutionTaskFactory.groovy
│ ├── Task.groovy
│ ├── TaskFactory.groovy
│ ├── TaskResult.groovy
│ ├── TaskState.groovy
│ └── TestTaskFactory.groovy
├── test
├── functional
│ └── heim
│ │ ├── BaseAPITestCase.groovy
│ │ ├── DataFetchingTaskTests.groovy
│ │ ├── RSessionControllerTests.groovy
│ │ └── ScriptExecutionControllerTests.groovy
├── integration
│ └── heim
│ │ └── session
│ │ └── SessionServiceTests.groovy
├── runit
│ ├── run_tests.R
│ └── tests
│ │ ├── boxplot_summary.R
│ │ ├── core_input.R
│ │ ├── heatmap_downloadData.R
│ │ ├── heatmap_preprocess.R
│ │ ├── heatmap_run.R
│ │ ├── heatmap_summary.R
│ │ └── volcanoplot_run.R
└── unit
│ ├── heim
│ └── RScriptOutputManagerSpec.groovy
│ └── javascript
│ ├── JavaScriptUnitTestKarmaSuite.groovy
│ ├── ajaxServicesTests.js
│ ├── fetchButtonDirectiveTests.js
│ ├── runButtonDirectiveTests.js
│ ├── smartRUtilsServiceTests.js
│ └── testSetup.js
└── web-app
├── HeimScripts
├── _core
│ ├── index.R
│ └── input.R
├── _func_test
│ ├── autosourcing.R
│ ├── sample.R
│ ├── sourced.R
│ └── sourcing.R
├── _shared_functions
│ ├── Clustering
│ │ └── heatmapUtils.R
│ ├── GEX
│ │ ├── DataFrameAndGEXmatrixUtils.R
│ │ └── limmaUtils.R
│ ├── Generic
│ │ └── utils.R
│ └── SummaryStatistics
│ │ └── summaryStatistics.R
├── boxplot
│ ├── run.R
│ └── summary.R
├── correlation
│ └── run.R
├── heatmap
│ ├── downloadData.R
│ ├── preprocess.R
│ ├── run.R
│ └── summary.R
├── ipaconnector
│ └── run.R
├── linegraph
│ ├── corrStats.R
│ ├── lineStats.R
│ └── run.R
├── patientmapper
│ └── run.R
└── volcanoplot
│ └── run.R
├── WEB-INF
├── applicationContext.xml
├── sitemesh.xml
└── tld
│ ├── c.tld
│ ├── fmt.tld
│ ├── grails.tld
│ └── spring.tld
├── css
├── boxplot.css
├── correlation.css
├── heatmap.css
├── ipaconnector.css
├── linegraph.css
├── ng-table.css
├── patientmappper.css
├── smartR.css
├── tooltip.css
└── volcanoplot.css
└── js
├── resource
├── angular-css.js
├── angular-mocks.js
├── angular-route.js
├── angular.js
├── crossfilter.js
├── d3-tip.js
├── d3.min.js
├── jquery-2.1.4.min.js
├── jquery-ui-1.11.4.min.js
├── jsrender.js
├── ng-table.js
├── plotly-latest.min.js
└── sha256.js
└── smartR
├── _angular
├── controllers
│ ├── boxplot.js
│ ├── correlation.js
│ ├── heatmap.js
│ ├── ipaconnector.js
│ ├── linegraph.js
│ ├── patientmapper.js
│ └── volcanoplot.js
├── directives
│ ├── biomarkerSelection.js
│ ├── capturePlotButton.js
│ ├── cohortSummaryInfo.js
│ ├── conceptBox.js
│ ├── downloadResultsButton.js
│ ├── fetchButton.js
│ ├── ipaApi.js
│ ├── ngTranscludeReplace.js
│ ├── preprocessButton.js
│ ├── runButton.js
│ ├── sortingCriteria.js
│ ├── summaryStatistics.js
│ ├── tabContainer.js
│ ├── workflowControls.js
│ ├── workflowTab.js
│ └── workflowWarnings.js
├── services
│ ├── commonWorkflowService.js
│ ├── rServeService.js
│ └── smartRUtils.js
├── smartRApp.js
├── templates
│ ├── biomarkerSelection.html
│ ├── boxplot.html
│ ├── conceptBox.html
│ ├── fetchButton.html
│ ├── heatmap.html
│ ├── ipaApi.html
│ ├── linegraph.html
│ ├── preprocessButton.html
│ ├── runButton.html
│ ├── sortingCriteria.html
│ ├── summaryStatistics.html
│ ├── tabContainer.html
│ ├── workflowControls.html
│ └── workflowWarnings.html
└── viz
│ ├── d3Correlation.js
│ ├── d3Heatmap.js
│ ├── d3Linegraph.js
│ ├── d3Volcanoplot.js
│ └── plotlyBoxplot.js
└── smartR.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # .gitignore for Grails 1.2 and 1.3
2 |
3 | # web application files
4 | /web-app/WEB-INF/classes
5 |
6 | # default HSQL database files for production mode
7 | /prodDb.*
8 |
9 | # general HSQL database files
10 | *Db.properties
11 | *Db.script
12 |
13 | # R generated files
14 | .RData
15 | .Rapp.history
16 | .Rhistory
17 |
18 | # InteliJ generated files
19 | *.iml
20 |
21 | # logs
22 | /stacktrace.log
23 | /test/reports
24 | /logs
25 |
26 | # project release file
27 | /*.war
28 |
29 | # plugin release files
30 | /*.zip
31 | /*.zip.sha1
32 | /plugin.xml
33 |
34 | # older plugin install locations
35 | /plugins
36 | /web-app/plugins
37 |
38 | # "temporary" build files
39 | /target
40 |
41 | .DS_Store
42 | .idea
43 | /production
44 | *.DS_Store
45 | /SmartR.todo
46 | .tern-port
47 | /node_modules
48 |
--------------------------------------------------------------------------------
/.settings/.gitignore:
--------------------------------------------------------------------------------
1 | *.json
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php # to have .travis installing the php version we indicate
2 | sudo: false
3 | php:
4 | - 5.6
5 | addons:
6 | apt:
7 | packages:
8 | - libreadline-dev
9 | - clang
10 | - gfortran
11 | - libcairo2-dev
12 | cache:
13 | directories:
14 | - $HOME/.m2
15 | - $HOME/rserve
16 | - node_modules
17 |
18 | jdk:
19 | - oraclejdk7
20 |
21 | before_install:
22 | - git clone --depth 1 git://github.com/transmart/transmart-travis.git ~/ts-travis
23 | - git clone --depth 100 git://github.com/transmart/transmart-data.git ~/transmart-data
24 | - source ~/ts-travis/init.sh
25 | - source ~/ts-travis/grails_inline.sh
26 | - source ~/ts-travis/maven_dep.sh
27 | - source ~/ts-travis/rserve_install.sh
28 |
29 | install:
30 | - maybe_build_maven_dep $(travis_get_owner)/transmart-core-api core-api
31 | - maybe_make_inline_grails_dep $(travis_get_owner)/transmart-core-db core-db . transmart-core-db-tests
32 | - make -C ~/transmart-data/env ../vars
33 | - install_rserve ~/transmart-data
34 | - npm install
35 |
36 | before_script:
37 | - start_rserve
38 |
39 | script:
40 | - grails refresh-dependencies --non-interactive
41 | - grails test-app --non-interactive --stacktrace
42 | - "\"$RSERVE_LOCATION\"/bin/Rscript test/runit/run_tests.R"
43 |
44 | notifications:
45 | hipchat:
46 | rooms:
47 | secure: UKvpmZGc7nf/OGOY4e+mmvgaax/ezCTUNehuxhPqXSYwkec7OTY64znG6QN1rAP1YuWky1bdvTXiTXbr6STss4NlgMfmtK85GtxGWTFx6Xrq3L13YP9iG4ZaYQHGAzWat6CfKUmmkwhFgZCcg6n4msPW+YBpeGcFEglcOHRKcAxqx9S6vLzzIa+lsC2030bOL994IJrDgE48tyZtu8XhTpds737DsTgR0UW2WwH4PmyfdaxCvi4OmM/ZBrXBQgdK48R6RXmcP6WyvtLbxBVZCiZaf7TF06DLZffAJgxMhjJgczUzu/5kQpikodvCGxzYaKZ2cnRCKEVbF1YfFhMdjkUFalTdSmNhMUZapYb0Yt32wCR7inQDAVShJa0RGm3TEhtTWuFMlKCTFHgOV7jYc3wxQjDEFMdWWVvf9T0lXA5ULaBd0fbmBrP2Nj+9gtGOkVeu4X0ukXe10+COUj2oBzMR295+856LXHz9jp220VnCVU1VOxWnlyUvVO8qLnYlMyX5rukJ3XoR3dsff0h4qKKsPgDzkZDVucQreGci9a4sLpVuvH+o6P14oweZGU6gX5DSRUWvGOm1asZBSuwvmDIfIIrqbK63zfFQI0ClM6OaJl9K6qTL27BXUHOnx6jvchZET6ph9B+3v1eSAbLvEnNrfexfgNTwc5W3XX+AWdU=
48 |
49 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 |
3 | Current Version 0.3
4 | - **Fix: Big datasets lead no longer to an unpextect error**
5 | - Fix: Feature ranking works correctly now
6 | - Fix: Added log2 to Heatmap tooltip
7 | - Fix: Cutoff in Heatmap will not work anymore with 2 rows or less
8 | - **Fix: Heatmap can now handle datasets containing duplicated probeset ids**
9 | - Improved Heatmap script performance
10 | - Fix: Correlation visualization behaves correctly now when resizing
11 | - Fix: The "Run Analysis" button will now enable again even if JS rendering fails
12 |
13 | Version 0.2
14 | - Fix: Session management should be more stable now
15 | - Fix: Large datasets should not cause SmartR to crash anymore
16 | - Fix: Heatmap - switched red and green color
17 | - Fix: Heatmap - use log2 values instead of raw values for ranking
18 | - Fix: Heatmap - improved contrast
19 |
20 | Version 0.1
21 | - Introduced versioning. No changelog available behind this point.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## If you use SmartR in your publication please cite it:
2 | https://doi.org/10.1093/bioinformatics/btx137
3 |
4 | ###SmartR is a grails plugin seeking to improve the visual analytics of the [tranSMART platform](https://github.com/transmart/transmartApp) by using recent web technologies such as [d3](http://d3js.org/).
5 |
6 | [](https://youtu.be/Gg0AdYt77Cs)
7 |
8 | #### Installation
9 | ##### tranSMART 16.2, eTRIKS v3 and younger builds
10 | SmartR will be included by default. No modification necessary.
11 |
12 | ##### tranSMART 1.2.5 and 16.1
13 | 1. Add ```runtime ':smart-r:1.0-STABLE-SNAPSHOT'``` to **BuildConfig.groovy** in the **transmartApp** source code.
14 | 2. Compile a WAR file via ```grails war``` for deployment.
15 |
16 | ##### tranSMART 1.2.4 and older
17 | 1. Add ```runtime ':smart-r:1.0-STABLE-SNAPSHOT'``` to **BuildConfig.groovy** in the **transmartApp** source code.
18 | 2. Add the following code to **transmartApp/web-app/js/datasetExplorer/datasetExplorer.js** near [>>this location<< :](https://github.com/transmart/transmartApp/blob/release-1.2.4/web-app/js/datasetExplorer/datasetExplorer.js#L782)
19 | ```loadPlugin('smartR', "/SmartR/loadScripts", function () {
20 | resultsTabPanel.insert(4, smartRPanel);
21 | })```
22 | 3. Compile a WAR file via ```grails war``` for deployment.
23 |
24 | #### Requirements
25 | - SmartR requires the following R packages:
26 | - [jsonlite](https://cran.r-project.org/web/packages/jsonlite/index.html)
27 | - [gplots](https://cran.r-project.org/web/packages/gplots/index.html)
28 | - [reshape2](https://cran.r-project.org/web/packages/reshape2/index.html)
29 | - [WGCNA](https://cran.r-project.org/web/packages/WGCNA/index.html)
30 | - [limma](https://bioconductor.org/packages/release/bioc/html/limma.html)
31 | - [zoo](https://cran.r-project.org/web/packages/zoo/index.html)
32 |
33 | #### Releases
34 | - https://github.com/transmart/SmartR/releases
35 |
36 | #### Wiki
37 | - https://wiki.transmartfoundation.org/display/transmartwiki/SmartR
38 |
39 |
--------------------------------------------------------------------------------
/application.properties:
--------------------------------------------------------------------------------
1 | #Grails Metadata file
2 | #Tue Mar 03 14:09:30 CET 2015
3 | app.grails.version=2.3.11
4 | app.name=smart-r
5 | app.servlet.version=2.5
6 |
--------------------------------------------------------------------------------
/grails-app/conf/BootStrap.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Janssen Research & Development, LLC.
3 | *
4 | * This file is part of REST API: transMART's plugin exposing tranSMART's
5 | * data via an HTTP-accessible RESTful API.
6 | *
7 | * This program is free software: you can redistribute it and/or modify it
8 | * under the terms of the GNU General Public License as published by the
9 | * Free Software Foundation, either version 3 of the License, or (at your
10 | * option) any later version, along with the following terms:
11 | *
12 | * 1. You may convey a work based on this program in accordance with
13 | * section 5, provided that you retain the above notices.
14 | * 2. You may convey verbatim copies of this program code as you receive
15 | * it, in any medium, provided that you retain the above notices.
16 | *
17 | * This program is distributed in the hope that it will be useful, but
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
20 | * Public License for more details.
21 | *
22 | * You should have received a copy of the GNU General Public License along
23 | * with this program. If not, see .
24 | */
25 |
26 | import com.google.common.collect.ImmutableList
27 | import grails.util.Environment
28 | import grails.util.Holders
29 | import groovy.util.logging.Log4j
30 |
31 | @Log4j
32 | class BootStrap {
33 |
34 | final static String TEST_PHASE_CONFIGURER_CLASS_NAME =
35 | 'org.codehaus.groovy.grails.test.runner.phase.IntegrationTestPhaseConfigurer'
36 |
37 | def init = { servletContext ->
38 | if (Environment.currentEnvironment == Environment.TEST) {
39 | if (Class.forName(TEST_PHASE_CONFIGURER_CLASS_NAME).currentApplicationContext) {
40 | /* don't load the test data bundle for integration tests */
41 | return
42 | }
43 | def testData = createTestData()
44 | log.info 'About to save test data'
45 | testData.saveAll()
46 | log.info 'Saved test data'
47 |
48 | def queryResults = testData.mrnaData.patients.collect { patient ->
49 | Class.forName('org.transmartproject.db.querytool.QueryResultData')
50 | .getMethod('createQueryResult', List)
51 | .invoke(null, [patient])
52 | }
53 |
54 | // hibernate is not saving QtQueryMaster before QtQueryInstance if
55 | // the id of QtQueryMaster is explicitly assigned, hence we add
56 | // an endpoint to retrieve these result instance ids
57 | // (see SmartRTestController)
58 |
59 | Holders.applicationContext.registerSingleton('mrnaPatientSetIds', ArrayList)
60 | Holders.applicationContext.getBean('mrnaPatientSetIds').addAll(
61 | ImmutableList.copyOf(queryResults*.save()*.id))
62 | log.info 'Created extra patient sets for testing'
63 | }
64 | }
65 |
66 | def createTestData() {
67 | Class clazz = Class.forName('org.transmartproject.db.TestData')
68 | clazz.getMethod('createDefault').invoke(null) //static method
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/grails-app/conf/BuildConfig.groovy:
--------------------------------------------------------------------------------
1 | def forkSettingsRun = [
2 | minMemory: 1536,
3 | maxMemory: 4096,
4 | maxPerm : 384,
5 | debug : false,
6 | ]
7 | def forkSettingsOther = [
8 | minMemory: 256,
9 | maxMemory: 1024,
10 | maxPerm : 384,
11 | debug : false,
12 | ]
13 |
14 | grails.project.fork = [
15 | test : false,
16 | run : forkSettingsRun,
17 | war : false,
18 | console: forkSettingsOther]
19 |
20 | grails.project.class.dir = "target/classes"
21 | grails.project.test.class.dir = "target/test-classes"
22 | grails.project.test.reports.dir = "target/test-reports"
23 |
24 | def dm, dmClass
25 | try {
26 | dmClass = new GroovyClassLoader().parseClass(
27 | new File('../transmart-dev/SmartRDependencyManagement.groovy'))
28 | } catch (Exception e) {
29 | }
30 | if (dmClass) {
31 | dm = dmClass.newInstance()
32 | }
33 |
34 | grails.project.dependency.resolver = 'maven'
35 | grails.project.dependency.resolution = {
36 | log "warn"
37 | legacyResolve false
38 | inherits('global') {}
39 | if (!dm) {
40 | repositories {
41 | grailsCentral()
42 | mavenLocal()
43 | mavenCentral()
44 | mavenRepo 'https://repo.transmartfoundation.org/content/repositories/public/'
45 | mavenRepo 'https://repo.thehyve.nl/content/repositories/public/'
46 | }
47 | } else {
48 | dm.configureRepositories delegate
49 | }
50 | dependencies {
51 | // compile 'org.apache.ant:ant:1.9.6'
52 | compile 'net.sf.opencsv:opencsv:2.3'
53 | compile 'org.rosuda:Rserve:1.7.3'
54 | compile 'org.mapdb:mapdb:0.9.10'
55 | compile 'org.apache.commons:commons-lang3:3.4'
56 |
57 | /* serializable ImmutableMap only on guava 16 */
58 | compile group: 'com.google.guava', name: 'guava', version: '16.0-dev-20140115-68c8348'
59 | compile 'org.transmartproject:transmart-core-api:16.2-SNAPSHOT'
60 | //test 'com.jayway.restassured:rest-assured:2.4.1'
61 |
62 | runtime 'org.javassist:javassist:3.16.1-GA'
63 | runtime 'com.ittm_solutions.ipacore:IpaApi:1.0.0'
64 |
65 | test('org.gmock:gmock:0.9.0-r435-hyve2') {
66 | transitive = false /* don't bring groovy-all */
67 | export = false
68 | }
69 | }
70 | plugins {
71 | // for release support (e.g. to 'publish' plugin to nexus)
72 | build "org.grails.plugins:release:3.1.2"
73 |
74 | // FIXME: Advanced workflows gets buggy when updating resources plugin to 1.2.14
75 | runtime ':resources:1.2.1'
76 |
77 | compile ":sendfile:0.2"
78 | build ':tomcat:7.0.47', {
79 | export = false
80 | }
81 | test ':functional-test:2.0.0'
82 | test ':karma-test-runner:0.2.4'
83 |
84 | // core-db doesn't export hibernate as dep as it was builtin in 2.2.4
85 | //runtime ':hibernate:3.6.10.16'
86 |
87 | if (!dm) {
88 | runtime ':transmart-core:16.2-SNAPSHOT'
89 |
90 | test ':transmart-core:16.2-SNAPSHOT'
91 | test ':transmart-core-db-tests:16.2-SNAPSHOT'
92 | } else {
93 | dm.internalDependencies delegate
94 | }
95 |
96 | }
97 | }
98 |
99 | dm?.with {
100 | configureInternalPlugin 'runtime', 'transmart-core'
101 | configureInternalPlugin 'test', 'transmart-core'
102 | configureInternalPlugin 'test', 'transmart-core-db-tests'
103 | }
104 |
105 | dm?.inlineInternalDependencies grails, grailsSettings
106 |
--------------------------------------------------------------------------------
/grails-app/conf/Config.groovy:
--------------------------------------------------------------------------------
1 | // configuration for plugin testing - will not be included in the plugin zip
2 | log4j = {
3 |
4 | error 'org.codehaus.groovy.grails.web.servlet', // controllers
5 | 'org.codehaus.groovy.grails.web.pages', // GSP
6 | 'org.codehaus.groovy.grails.web.sitemesh', // layouts
7 | 'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
8 | 'org.codehaus.groovy.grails.web.mapping', // URL mapping
9 | 'org.codehaus.groovy.grails.commons', // core / classloading
10 | 'org.codehaus.groovy.grails.plugins', // plugins
11 | 'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
12 | 'org.springframework',
13 | 'org.hibernate',
14 | 'net.sf.ehcache.hibernate'
15 | info 'org.transmartproject'
16 | debug 'heim'
17 |
18 | // uncomment to debug queries
19 | //trace 'org.hibernate.type'
20 | //debug 'org.hibernate.SQL'
21 | }
22 |
23 | grails.views.default.codec="none" // none, html, base64
24 | grails.views.gsp.encoding="UTF-8"
25 |
26 | grails.databinding.convertEmptyStringsToNull = false
27 | grails.databinding.trimStrings = false
28 |
--------------------------------------------------------------------------------
/grails-app/conf/DataSource.groovy:
--------------------------------------------------------------------------------
1 | dataSource {
2 | pooled = true
3 | driverClassName = "org.h2.Driver"
4 | username = "sa"
5 | password = ""
6 | }
7 | hibernate {
8 | cache.use_second_level_cache = true
9 | cache.use_query_cache = false
10 | cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory' // Hibernate 3
11 | // cache.region.factory_class = 'org.hibernate.cache.ehcache.EhCacheRegionFactory' // Hibernate 4
12 | }
13 | // environment specific settings
14 | environments {
15 | development {
16 | dataSource {
17 | dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
18 | url = "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM './h2_init.sql'"
19 | }
20 | }
21 | test {
22 | dataSource {
23 | dbCreate = "update"
24 | url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM './h2_init.sql'"
25 | }
26 | }
27 | production {
28 | dataSource {
29 | dbCreate = "update"
30 | url = "jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM './h2_init.sql'"
31 | properties {
32 | maxActive = -1
33 | minEvictableIdleTimeMillis = 1800000
34 | timeBetweenEvictionRunsMillis = 1800000
35 | numTestsPerEvictionRun = 3
36 | testOnBorrow = true
37 | testWhileIdle = true
38 | testOnReturn = false
39 | validationQuery = "SELECT 1"
40 | jdbcInterceptors = "ConnectionState"
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/grails-app/conf/SmartRResources.groovy:
--------------------------------------------------------------------------------
1 | modules = {
2 | smartR_core {
3 | resource url: [plugin: 'smart-r', dir: 'css', file: 'smartR.css']
4 | resource url: [plugin: 'smart-r', dir: 'css', file: 'tooltip.css']
5 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'jquery-2.1.4.min.js']
6 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'jquery-ui-1.11.4.min.js']
7 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'd3.min.js']
8 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'd3-tip.js']
9 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'crossfilter.js']
10 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'plotly-latest.min.js']
11 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'jsrender.js']
12 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'angular.js']
13 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'angular-route.js']
14 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'angular-css.js']
15 | // IPA connector
16 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'sha256.js' ]
17 | resource url: [plugin: 'smart-r', dir: 'js/resource', file: 'ng-table.js']
18 | resource url: [plugin: 'smart-r', dir: 'css', file: 'ng-table.css']
19 | }
20 |
21 | smartR_angular_components {
22 | dependsOn 'smartR_core'
23 | // app initializer
24 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular', file: 'smartRApp.js']
25 | // directives
26 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'conceptBox.js']
27 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'tabContainer.js']
28 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'workflowTab.js']
29 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'fetchButton.js']
30 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'preprocessButton.js']
31 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'runButton.js']
32 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'capturePlotButton.js']
33 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'downloadResultsButton.js']
34 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'ngTranscludeReplace.js']
35 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'summaryStatistics.js']
36 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'biomarkerSelection.js']
37 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'sortingCriteria.js']
38 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'cohortSummaryInfo.js']
39 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'workflowWarnings.js']
40 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'workflowControls.js']
41 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/directives', file: 'ipaApi.js']
42 | // services
43 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/services', file: 'rServeService.js']
44 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/services', file: 'smartRUtils.js']
45 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/services', file: 'commonWorkflowService.js']
46 | }
47 |
48 | smartR_all {
49 | dependsOn 'smartR_angular_components'
50 | // heatmap
51 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/controllers', file: 'heatmap.js']
52 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/viz', file: 'd3Heatmap.js']
53 | // correlation
54 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/controllers', file: 'correlation.js']
55 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/viz', file: 'd3Correlation.js']
56 | // boxplot
57 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/controllers', file: 'boxplot.js']
58 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/viz', file: 'plotlyBoxplot.js']
59 | // volcano
60 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/controllers', file: 'volcanoplot.js']
61 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/viz', file: 'd3Volcanoplot.js']
62 | // linegraph
63 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/controllers', file: 'linegraph.js']
64 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/viz', file: 'd3Linegraph.js']
65 | // patientmapper
66 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/controllers', file: 'patientmapper.js']
67 | // IPA connector
68 | resource url: [plugin: 'smart-r', dir: 'js/smartR/_angular/controllers', file: 'ipaconnector.js']
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/grails-app/conf/UrlMappings.groovy:
--------------------------------------------------------------------------------
1 | class UrlMappings {
2 | /**
3 | * This mappings file seems to be required when inlining core-db plugins
4 | * (as stated here https://github.com/transmart/transmart-rest-api/blob/v1.2.4/grails-app/conf/UrlMappings.groovy).
5 | * Without it, we get "Class not found loading Grails application: UrlMappings"
6 | * (https://travis-ci.org/transmart/SmartR/builds/163942859)
7 | */
8 |
9 | static mappings = {
10 | // Following block seems to be required by functional tests (BaseAPITestCase.groovy)
11 | "/$controller/$action?/$id?(.${format})?"{
12 | constraints {
13 | // apply constraints here
14 | }
15 | }
16 |
17 | // Removed the two following lines in order to not interfere with transmartApp url mappings - TRANSREL-134
18 | //"/"(view:"/index")
19 | //"500"(view:'/error')
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/grails-app/conf/spring/resources.groovy:
--------------------------------------------------------------------------------
1 | import heim.tasks.TestTaskFactory
2 |
3 | // only relevant for testing/running standalone
4 | beans = {
5 | testTaskFactory TestTaskFactory
6 | }
7 |
--------------------------------------------------------------------------------
/grails-app/controllers/smartR/plugin/rest/RSessionController.groovy:
--------------------------------------------------------------------------------
1 | package smartR.plugin.rest
2 |
3 | import heim.session.SessionService
4 | import org.transmartproject.core.exceptions.InvalidArgumentsException
5 |
6 | class RSessionController {
7 |
8 | static scope = 'prototype'
9 |
10 | static allowedMethods = [
11 | create: 'POST',
12 | delete: 'POST',
13 | ]
14 |
15 | SessionService sessionService
16 |
17 | /**
18 | * Creates a new R session
19 | */
20 | def create() {
21 | def json = request.JSON
22 | if (!json.workflow) {
23 | throw new InvalidArgumentsException('Workflow not given')
24 | }
25 |
26 | UUID sessionId = sessionService.createSession(
27 | null /* user not used now */,
28 | json.workflow)
29 |
30 | response.status = 201
31 | render(contentType: 'text/json') {
32 | [sessionId: sessionId.toString()]
33 | }
34 | }
35 |
36 | def touch() {
37 | sessionService.touchSession(sessionId)
38 | render status: 204 // no content
39 | }
40 |
41 | /**
42 | * Deletes an R session
43 | */
44 | def delete() {
45 | sessionService.destroySession(sessionId)
46 |
47 | response.status = 202 /* session shutdown runs asynchronously */
48 | render ''
49 | }
50 |
51 | /**
52 | * Delete files assoiated with this session
53 | */
54 | def deleteFiles() {
55 | sessionService.removeAllFiles(sessionId)
56 |
57 | response.status = 204
58 | render ''
59 | }
60 |
61 | private UUID getSessionId() {
62 | def json = request.JSON
63 | if (!json.sessionId || !(json.sessionId instanceof String)) {
64 | throw new InvalidArgumentsException(
65 | "No session id provided or not string: ${json.sessionId}")
66 | }
67 | try {
68 | UUID.fromString(json.sessionId)
69 | } catch (IllegalArgumentException iae) {
70 | throw new InvalidArgumentsException(
71 | "Invalid session id (not a UUID): ${json.sessionId}")
72 | }
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/grails-app/controllers/smartR/plugin/rest/ScriptExecutionController.groovy:
--------------------------------------------------------------------------------
1 | package smartR.plugin.rest
2 |
3 | import grails.validation.Validateable
4 | import heim.session.SessionService
5 | import heim.tasks.TaskResult
6 | import org.transmartproject.core.exceptions.InvalidArgumentsException
7 |
8 | class ScriptExecutionController {
9 |
10 | static scope = 'request'
11 |
12 | SessionService sessionService
13 | def sendFileService
14 |
15 | static allowedMethods = [
16 | init : 'POST',
17 | run : 'POST',
18 | status: 'GET',
19 | downloadFile: 'GET',
20 | ]
21 |
22 | def run(RunCommand runCommand) {
23 | throwIfInvalid runCommand
24 |
25 | UUID executionId =
26 | sessionService.createTask(
27 | runCommand.arguments,
28 | runCommand.sessionId,
29 | runCommand.taskType,)
30 |
31 | render(contentType: 'text/json') {
32 | [executionId: executionId.toString()]
33 | }
34 | }
35 |
36 | def status(StatusCommand statusCommand) {
37 | throwIfInvalid statusCommand
38 |
39 | Map status = sessionService.getTaskData(
40 | statusCommand.sessionId,
41 | statusCommand.executionId,
42 | statusCommand.waitForCompletion)
43 |
44 | TaskResult res = status.result
45 | def resultValue = null
46 | if (res != null) {
47 | String exceptionMessage
48 | if (res.exception) {
49 | exceptionMessage = res.exception.getClass().toString() +
50 | ': ' + res.exception.message
51 | }
52 | resultValue = [
53 | successful: res.successful,
54 | exception: exceptionMessage,
55 | artifacts: res.artifacts,
56 | ]
57 | }
58 |
59 | render(contentType: 'text/json') {
60 | [
61 | state : status.state.toString(),
62 | result: resultValue
63 | ]
64 | }
65 | }
66 |
67 | def downloadFile(DownloadCommand downloadCommand) {
68 | throwIfInvalid downloadCommand
69 |
70 | File selectedFile = sessionService.getFile(
71 | downloadCommand.sessionId,
72 | downloadCommand.executionId,
73 | downloadCommand.filename)
74 |
75 | sendFileService.sendFile servletContext, request, response, selectedFile
76 | }
77 |
78 | private void throwIfInvalid(command) {
79 | if (command.hasErrors()) {
80 | List errorStrings = command.errors.allErrors.collect {
81 | g.message(error:it, encodeAs: 'raw')
82 | }
83 | throw new InvalidArgumentsException("Invalid input: $errorStrings")
84 | }
85 | }
86 | }
87 |
88 | @Validateable
89 | class StatusCommand {
90 | UUID sessionId
91 | UUID executionId
92 | boolean waitForCompletion
93 |
94 | static constraints = {
95 | sessionId nullable: false
96 | executionId nullable: false
97 | }
98 | }
99 |
100 | @Validateable
101 | class RunCommand {
102 | UUID sessionId
103 | Map arguments = [:]
104 | String taskType
105 |
106 | static constraints = {
107 | sessionId nullable: false
108 | taskType blank: false
109 | }
110 | }
111 |
112 | @Validateable
113 | class DownloadCommand {
114 | UUID sessionId
115 | UUID executionId
116 | String filename
117 |
118 | static constraints = {
119 | sessionId nullable: false
120 | executionId nullable: false
121 | filename blank: false
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/grails-app/controllers/smartR/plugin/rest/SmartRTestController.groovy:
--------------------------------------------------------------------------------
1 | package smartR.plugin.rest
2 |
3 | import grails.converters.JSON
4 | import grails.util.Environment
5 | import grails.util.Holders
6 |
7 | /**
8 | * It's not possible to create result instances with well-known ids, so we
9 | * expose these to the client with this controller (see BootStrap).
10 | */
11 | class SmartRTestController {
12 |
13 | def resultInstanceIds() {
14 | if (Environment.currentEnvironment == Environment.TEST) {
15 | render([values: Holders.applicationContext.getBean('mrnaPatientSetIds')] as JSON)
16 | }
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/grails-app/i18n/messages.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/transmart/SmartR/6ce267606ff9b99bb63a5a2db0e1b5303d50d206/grails-app/i18n/messages.properties
--------------------------------------------------------------------------------
/grails-app/views/error.gsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Grails Runtime Exception Error
5 |
6 |
7 |
8 |
9 | SmartR An error has occurred
10 | exception '${exception}'
11 |
12 |
13 |
14 |
15 |
16 | SmartR
17 | An error has occurred
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/grails-app/views/layouts/_boxplot.gsp:
--------------------------------------------------------------------------------
1 |
2 |
86 |
--------------------------------------------------------------------------------
/grails-app/views/layouts/_correlation.gsp:
--------------------------------------------------------------------------------
1 |
2 |
94 |
--------------------------------------------------------------------------------
/grails-app/views/layouts/_linegraph.gsp:
--------------------------------------------------------------------------------
1 |
2 |
65 |
--------------------------------------------------------------------------------
/grails-app/views/layouts/_patientmapper.gsp:
--------------------------------------------------------------------------------
1 |
44 |
--------------------------------------------------------------------------------
/grails-app/views/layouts/_volcanoplot.gsp:
--------------------------------------------------------------------------------
1 |
2 |
46 |
--------------------------------------------------------------------------------
/grails-app/views/layouts/smartR.gsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/grails-app/views/smartR/index.gsp:
--------------------------------------------------------------------------------
1 |
2 |
3 | Loading...
4 |
5 |
SmartR - Dynamic Data Visualization and Interaction
6 |
7 |
8 |
9 |
15 | Reset SmartR
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/h2_init.sql:
--------------------------------------------------------------------------------
1 | CREATE SCHEMA IF NOT EXISTS I2B2METADATA;
2 | CREATE SCHEMA IF NOT EXISTS I2B2DEMODATA;
3 | CREATE SCHEMA IF NOT EXISTS DEAPP;
4 | CREATE SCHEMA IF NOT EXISTS BIOMART;
5 | CREATE SCHEMA IF NOT EXISTS SEARCHAPP;
6 | SET SCHEMA_SEARCH_PATH PUBLIC,I2B2METADATA,I2B2DEMODATA,BIOMART;
7 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(config) {
4 | config.set({
5 |
6 | // base path that will be used to resolve all patterns (eg. files, exclude)
7 | basePath: '',
8 |
9 | // frameworks to use
10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
11 | frameworks: ['jasmine'],
12 |
13 | plugins: [
14 | 'karma-jasmine',
15 | 'karma-phantomjs-launcher',
16 | 'karma-ng-html2js-preprocessor',
17 | 'karma-remote-reporter'
18 | ],
19 |
20 | // list of files / patterns to load in the browser
21 | files: [
22 | // libraries
23 | 'web-app/js/resource/jquery-2.1.4.min.js',
24 | 'web-app/js/resource/angular.js',
25 | 'web-app/js/resource/angular-route.js',
26 | 'web-app/js/resource/angular-css.js',
27 | 'web-app/js/resource/d3.min.js',
28 |
29 | // stuff for testing only
30 | 'web-app/js/resource/angular-mocks.js',
31 | 'test/unit/javascript/testSetup.js',
32 | // 'node_modules/karma-ng-html2js-preprocessor/lib/html2js.js',
33 |
34 | // application code
35 | 'web-app/js/smartR/_angular/services/smartRUtils.js',
36 | 'web-app/js/smartR/_angular/services/rServeService.js',
37 | 'web-app/js/smartR/_angular/directives/fetchButton.js',
38 | 'web-app/js/smartR/_angular/directives/runButton.js',
39 |
40 | // templates
41 | 'web-app/js/smartR/_angular/templates/*.html',
42 |
43 | // test files
44 | 'test/unit/javascript/**/*.js'
45 | // 'test/functional/javascript/**/*.js'
46 | ],
47 |
48 | // list of files to exclude
49 | exclude: [
50 | ],
51 |
52 | // preprocess matching files before serving them to the browser
53 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
54 | preprocessors: {
55 | 'web-app/js/smartR/_angular/templates/*.html': 'ng-html2js'
56 | },
57 |
58 | ngHtml2JsPreprocessor: {
59 | stripPrefix: 'web-app',
60 | moduleName: 'smartRTemplates'
61 | },
62 |
63 | // test results reporter to use
64 | // possible values: 'dots', 'progress'
65 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
66 | reporters: ['progress', 'remote'],
67 |
68 | remoteReporter: {
69 | host: 'localhost',
70 | port: '9889'
71 | },
72 |
73 |
74 | // web server port
75 | port: 9876,
76 |
77 |
78 | // enable / disable colors in the output (reporters and logs)
79 | colors: true,
80 |
81 |
82 | // level of logging
83 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
84 | logLevel: config.LOG_INFO,
85 |
86 |
87 | // enable / disable watching file and executing tests whenever any file changes
88 | autoWatch: true,
89 |
90 |
91 | // start these browsers
92 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
93 | browsers: ['PhantomJS'],
94 |
95 |
96 | // Continuous Integration mode
97 | // if true, Karma captures browsers, runs the tests and exits
98 | singleRun: true,
99 |
100 | // Concurrency level
101 | // how many browser should be started simultaneous
102 | concurrency: Infinity
103 | });
104 | };
105 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-r",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start karma.conf.js"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "repository": "https://github.com/sherzinger/SmartR",
15 | "devDependencies": {
16 | "jasmine-ajax": "^3.2.0",
17 | "jasmine-core": "^2.4.1",
18 | "jasmine-def": "^0.1.0",
19 | "jasmine-expect": "^2.0.0-beta2",
20 | "karma": "^0.13.19",
21 | "karma-cli": "^0.1.2",
22 | "karma-jasmine": "^0.3.6",
23 | "karma-phantomjs-launcher": "^0.2.3",
24 | "karma-remote-reporter": "^0.2.0",
25 | "phantomjs": "^2.0.0",
26 | "karma-ng-html2js-preprocessor": "^0.2.2"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/scripts/_Events.groovy:
--------------------------------------------------------------------------------
1 | eventCreatePluginArchiveStart = { stagingDir ->
2 | event("BuildInfoAddPropertiesStart", [stagingDir])
3 | writeProperties(getEnvProperties(), "${stagingDir}/application.properties")
4 | event("BuildInfoAddPropertiesEnd", [stagingDir])
5 | }
6 |
7 | private void writeProperties(Map properties, String propertyFile) {
8 | Ant.propertyfile(file: propertyFile) {
9 | properties.each { k,v->
10 | entry(key: k, value: v)
11 | }
12 | }
13 | }
14 |
15 | def getEnvProperties() {
16 | def environment = [:]
17 | Ant.antProject.properties.findAll({k,v-> k.startsWith('environment')}).each { k,v->
18 | environment[k] = v
19 | }
20 |
21 | def properties = [:]
22 | properties['scm.version'] = getRevision()?.trim() ?: '-'
23 | properties['build.date'] = new Date().format('dd/MMM/yyyy; kk:mm:ss')
24 | properties['build.timezone'] = Calendar.getInstance().getTimeZone().getID()
25 | properties['build.java'] = System.getProperty('java.version')
26 | properties['build.groovy'] = GroovySystem.version
27 |
28 | //Add a selection of useful environment variables
29 | properties['env.os'] = environment['environment.OS'] ?: environment['environment.OSTYPE'] ?: '-'
30 | properties['env.username'] = environment['environment.USERNAME'] ?: environment['environment.USER'] ?: '-'
31 | properties['env.computer'] = environment['environment.COMPUTERNAME'] ?: environment['environment.HOSTNAME'] ?: '-'
32 | properties['env.proc.type'] = environment['environment.PROCESSOR_ARCHITECTURE'] ?: environment['environment.HOSTTYPE'] ?: '-'
33 | properties['env.proc.cores'] = environment['environment.NUMBER_OF_PROCESSORS'] ?: '-'
34 |
35 | return properties
36 | }
37 |
38 | def getRevision() {
39 | // client provided closure to determine revision
40 | def determineRevisionClosure = buildConfig.buildinfo.determineRevision
41 | if (determineRevisionClosure instanceof Closure) {
42 | return determineRevisionClosure()
43 | }
44 |
45 | // try to get revision from Hudson
46 | def scmVersion = Ant.antProject.properties."environment.SVN_REVISION"
47 |
48 | if (!scmVersion) {
49 | scmVersion = Ant.antProject.properties."environment.GIT_COMMIT"
50 | }
51 |
52 | // maybe a local git?
53 | if (!scmVersion) {
54 | try {
55 | def command = """git rev-parse HEAD"""
56 | def proc = command.execute()
57 | proc.waitFor()
58 | if (proc.exitValue() == 0) {
59 | scmVersion = proc.in.text
60 | }
61 | } catch (IOException e) {
62 | // oh well
63 | }
64 | }
65 |
66 | if (!scmVersion) {
67 | scmVersion = getEstimateRevisionFromGitFolder()
68 | }
69 |
70 | if (!scmVersion) {
71 | scmVersion = getRevisionFromSvnCli()
72 | }
73 |
74 | // if Hudson/Jenkins env variable not found, try file system (for SVN)
75 | if (!scmVersion) {
76 | File entries = new File(basedir, '.svn/entries')
77 | if (entries.exists() && entries.text.split('\n').length>3) {
78 | scmVersion = entries.text.split('\n')[3].trim()
79 | }
80 | }
81 |
82 | return scmVersion ?: '-'
83 | }
84 |
85 | private String getRevisionFromSvnCli() {
86 | try {
87 | def command = 'svn info --xml'
88 | def proc = command.execute()
89 | def out = new ByteArrayOutputStream()
90 | proc.consumeProcessOutput(out, null) //prevent blocking in Windows due to a full output buffer
91 | int exitVal = proc.waitFor()
92 | if (exitVal == 0) {
93 | def slurper = new XmlSlurper().parseText(out.toString())
94 | return slurper.entry.@revision
95 | }
96 | } catch (ignore) {
97 | return null
98 | }
99 | }
100 |
101 | def getEstimateRevisionFromGitFolder() {
102 | try {
103 | //on system which do not have git in the PATH, try the file system
104 | //the head this might not always provide accurate commit hash
105 | def headFile = new File(".git/HEAD")
106 | def refsHeadPath = ''
107 | if (headFile.exists()) {
108 | def headContents = headFile.text.trim()
109 | refsHeadPath = headContents.split(':')[1].trim()
110 | def refsHeadFile = new File(".git/${refsHeadPath}")
111 | if (refsHeadFile.isFile()) {
112 | return refsHeadFile.text.trim()
113 | }
114 | }
115 | } catch (ignore) {
116 | return null
117 | }
118 | }
--------------------------------------------------------------------------------
/scripts/_Install.groovy:
--------------------------------------------------------------------------------
1 | //
2 | // This script is executed by Grails after plugin was installed to project.
3 | // This script is a Gant script so you can use all special variables provided
4 | // by Gant (such as 'baseDir' which points on project base dir). You can
5 | // use 'ant' to access a global instance of AntBuilder
6 | //
7 | // For example you can create directory under project tree:
8 | //
9 | // ant.mkdir(dir:"${basedir}/grails-app/jobs")
10 | //
11 |
--------------------------------------------------------------------------------
/scripts/_Uninstall.groovy:
--------------------------------------------------------------------------------
1 | //
2 | // This script is executed by Grails when the plugin is uninstalled from project.
3 | // Use this script if you intend to do any additional clean-up on uninstall, but
4 | // beware of messing up SVN directories!
5 | //
6 |
--------------------------------------------------------------------------------
/scripts/_Upgrade.groovy:
--------------------------------------------------------------------------------
1 | //
2 | // This script is executed by Grails during application upgrade ('grails upgrade'
3 | // command). This script is a Gant script so you can use all special variables
4 | // provided by Gant (such as 'baseDir' which points on project base dir). You can
5 | // use 'ant' to access a global instance of AntBuilder
6 | //
7 | // For example you can create directory under project tree:
8 | //
9 | // ant.mkdir(dir:"${basedir}/grails-app/jobs")
10 | //
11 |
--------------------------------------------------------------------------------
/smartRGrailsPlugin.groovy:
--------------------------------------------------------------------------------
1 | import grails.util.Environment
2 | import heim.rserve.RScriptsSynchronizer
3 | import org.codehaus.groovy.grails.plugins.GrailsPluginUtils
4 | import org.springframework.stereotype.Component
5 | import heim.SmartRRuntimeConstants
6 |
7 | class smartRGrailsPlugin {
8 |
9 | public static final String DEFAULT_REMOTE_RSCRIPTS_DIRECTORY = '/tmp/smart_r_scripts'
10 | public static final String TRANSMART_EXTENSIONS_REGISTRY_BEAN_NAME = 'transmartExtensionsRegistry'
11 |
12 | // the plugin version
13 | def version = "1.2-STABLE-SNAPSHOT"
14 | // the version or versions of Grails the plugin is designed for
15 | def grailsVersion = "2.3 > *"
16 | // resources that are excluded from plugin packaging
17 | def pluginExcludes = [
18 | "grails-app/views/error.gsp"
19 | ]
20 |
21 | // TODO Fill in these fields
22 | def title = "SmartR Plugin" // Headline display name of the plugin
23 | def author = ""
24 | def authorEmail = ""
25 | def description =
26 | '''
27 | Brief summary/description of the plugin.
28 | '''
29 |
30 | // URL to the plugin's documentation
31 | def documentation = ""
32 |
33 | // Extra (optional) plugin metadata
34 |
35 | // License: one of 'APACHE', 'GPL2', 'GPL3'
36 | // def license = "APACHE"
37 |
38 | // Details of company behind the plugin (if there is one)
39 | // def organization = [ name: "My Company", url: "http://www.my-company.com/" ]
40 |
41 |
42 | // Any additional developers beyond the author specified above.
43 | // def developers = [ [ name: "Joe Bloggs", email: "joe@bloggs.net" ]]
44 |
45 | // Location of the plugin's issue tracker.
46 | // def issueManagement = [ system: "JIRA", url: "http://jira.grails.org/browse/GPMYPLUGIN" ]
47 |
48 | // Online location of the plugin's browseable source code.
49 | // def scm = [ url: "http://svn.codehaus.org/grails-plugins/" ]
50 |
51 | def doWithWebDescriptor = { xml ->
52 | // TODO Implement additions to web.xml (optional), this event occurs before
53 | }
54 |
55 | def doWithSpring = {
56 | xmlns context:"http://www.springframework.org/schema/context"
57 |
58 | context.'component-scan'('base-package': 'heim') {
59 | context.'include-filter'(
60 | type: 'annotation',
61 | expression: Component.canonicalName)
62 | }
63 | }
64 |
65 | def doWithApplicationContext = { ctx ->
66 | def config = application.config
67 | SmartRRuntimeConstants constants = ctx.getBean(SmartRRuntimeConstants)
68 |
69 | File smartRDir = GrailsPluginUtils.getPluginDirForName('smart-r')?.file
70 | if (!smartRDir) {
71 | String pluginPath = ctx.pluginManager.allPlugins.find {
72 | it.name == 'smartR'
73 | }.pluginPath
74 |
75 | smartRDir = ctx.getResource(pluginPath).file
76 | } else {
77 | smartRDir = new File(smartRDir, 'web-app')
78 | }
79 | if (!smartRDir) {
80 | throw new RuntimeException('Could not determine directory for ' +
81 | 'smart-r plugin')
82 | }
83 |
84 | constants.pluginScriptDirectory = new File(smartRDir.canonicalPath, 'HeimScripts')
85 | log.info("Directory for heim scripts is ${constants.pluginScriptDirectory}")
86 |
87 | if (!skipRScriptsTransfer(config)) {
88 | def remoteScriptDirectory = config.smartR.remoteScriptDirectory
89 | if (!remoteScriptDirectory) {
90 | remoteScriptDirectory = DEFAULT_REMOTE_RSCRIPTS_DIRECTORY
91 | }
92 | constants.remoteScriptDirectoryDir = remoteScriptDirectory
93 | log.info("Location for R scripts in the Rserve server is ${constants.remoteScriptDirectoryDir}")
94 |
95 | ctx.getBean(RScriptsSynchronizer).start()
96 | } else {
97 | log.info('Skipping copying of R script in development mode with local Rserve')
98 | constants.remoteScriptDirectoryDir = constants.pluginScriptDirectory.absoluteFile
99 | ctx.getBean(RScriptsSynchronizer).skip()
100 | }
101 |
102 | if (ctx.containsBean(TRANSMART_EXTENSIONS_REGISTRY_BEAN_NAME)) {
103 | ctx.getBean(TRANSMART_EXTENSIONS_REGISTRY_BEAN_NAME)
104 | .registerAnalysisTabExtension('smartR', '/SmartR/loadScripts', 'addSmartRPanel')
105 | }
106 |
107 | }
108 |
109 | private boolean skipRScriptsTransfer(config) {
110 | (!config.RModules.host ||
111 | config.RModules.host in ['127.0.0.1', '::1', 'localhost']) &&
112 | Environment.currentEnvironment == Environment.DEVELOPMENT &&
113 | !config.smartR.alwaysCopyScripts
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/groovy/heim/SmartRExecutorService.groovy:
--------------------------------------------------------------------------------
1 | package heim
2 |
3 | import com.google.common.util.concurrent.ListeningExecutorService
4 | import com.google.common.util.concurrent.MoreExecutors
5 | import groovy.transform.TypeChecked
6 | import org.springframework.beans.factory.DisposableBean
7 | import org.springframework.stereotype.Component
8 |
9 | import java.util.concurrent.LinkedBlockingQueue
10 | import java.util.concurrent.ThreadFactory
11 | import java.util.concurrent.ThreadPoolExecutor
12 | import java.util.concurrent.TimeUnit
13 | import java.util.concurrent.atomic.AtomicInteger
14 |
15 | /**
16 | * Created by glopes on 09-10-2015.
17 | */
18 | @Component
19 | @TypeChecked
20 | class SmartRExecutorService implements ListeningExecutorService,
21 | DisposableBean {
22 |
23 | public static final int CORE_POOL_SIZE = 3
24 | public static final int MAXIMUM_POOL_SIZE = 20
25 | public static final String SMART_RTHREAD_POOL_NAME = 'smartRThreadPool'
26 | // TODO: make configurable
27 |
28 | @Delegate
29 | ListeningExecutorService executorService
30 |
31 | SmartRExecutorService() {
32 | def pool = new ThreadPoolExecutor(
33 | CORE_POOL_SIZE,
34 | MAXIMUM_POOL_SIZE,
35 | 1,
36 | TimeUnit.MINUTES,
37 | new LinkedBlockingQueue())
38 | pool.with {
39 | threadFactory = new SmartRThreadFactory()
40 | }
41 | executorService = MoreExecutors.listeningDecorator(pool)
42 | }
43 |
44 | void destroy() {
45 | executorService.shutdownNow()
46 | }
47 |
48 | static class SmartRThreadFactory implements ThreadFactory {
49 | private final ThreadGroup group
50 | private final AtomicInteger threadNumber = new AtomicInteger(1);
51 |
52 | SmartRThreadFactory() {
53 | SecurityManager s = System.securityManager
54 | group = s?.threadGroup ?: Thread.currentThread().threadGroup
55 | }
56 |
57 | public Thread newThread(Runnable r) {
58 | new Thread(group, r,
59 | SMART_RTHREAD_POOL_NAME + '-' +
60 | threadNumber.getAndIncrement(),
61 | 0).with {
62 | daemon = false
63 | priority = Thread.NORM_PRIORITY
64 | (Thread) it
65 | }
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/groovy/heim/SmartRRuntimeConstants.groovy:
--------------------------------------------------------------------------------
1 | package heim
2 |
3 | import grails.util.Holders
4 | import org.codehaus.groovy.grails.commons.GrailsApplication
5 | import org.springframework.beans.factory.annotation.Autowired
6 | import org.springframework.stereotype.Component
7 |
8 | @Component
9 | class SmartRRuntimeConstants {
10 | private static SmartRRuntimeConstants instance;
11 |
12 | public static SmartRRuntimeConstants getInstance() {
13 | if (instance == null) {
14 | synchronized (SmartRRuntimeConstants.class) {
15 | if (instance == null) {
16 | instance = new SmartRRuntimeConstants();
17 | instance.grailsApplication = Holders.grailsApplication; // thanks to http://stackoverflow.com/a/24501325/535203 (Holders.applicationContext.getBean("smartRRuntimeConstants") and Holders.applicationContext.getBean(SmartRRuntimeConstants) were failing with "org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'smartRRuntimeConstants' is defined"
18 | }
19 | }
20 | }
21 | instance;
22 | }
23 |
24 | @Autowired
25 | private GrailsApplication grailsApplication
26 |
27 | File pluginScriptDirectory
28 |
29 | void setPluginScriptDirectory(File dir) {
30 | assert dir.isDirectory()
31 | this.pluginScriptDirectory = dir.absoluteFile
32 | }
33 |
34 | void setRemoteScriptDirectoryDir(Object dir) {
35 | grailsApplication.config.smartR.remoteScriptDirectory = dir as File
36 | remoteScriptDirectoryDir // for the side effects
37 | }
38 |
39 | /**
40 | * Where to copy the R scripts to and execute them from
41 | * (in the machine where Rserve is running).
42 | */
43 | File getRemoteScriptDirectoryDir() {
44 | def dir = grailsApplication.config.smartR.remoteScriptDirectory as File
45 | if (!dir.absolute) {
46 | throw new RuntimeException("Invalid configuration: " +
47 | "smartR.remoteScriptDirectory should be an absolute path," +
48 | " got '$dir'")
49 | }
50 | dir
51 | }
52 |
53 | File getBaseDir() {
54 | def dir = grailsApplication.config.smartR.baseDir
55 | if (!dir) {
56 | dir = File.createTempDir("smartR", ".baseDir");
57 | setBaseDir(dir)
58 | }
59 | dir as File
60 | }
61 |
62 | void setBaseDir(Object dir) {
63 | grailsApplication.config.smartR.baseDir = dir as File
64 | baseDir // for the side effects
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/groovy/heim/jobs/JobInstance.groovy:
--------------------------------------------------------------------------------
1 | package heim.jobs
2 |
3 | import heim.tasks.Task
4 |
5 | /**
6 | * Created by glopes on 13-10-2015.
7 | */
8 | interface JobInstance {
9 |
10 | String getWorkflow()
11 |
12 | Task createTask(String name, Map arguments)
13 | }
14 |
--------------------------------------------------------------------------------
/src/groovy/heim/jobs/JobInstanceImpl.groovy:
--------------------------------------------------------------------------------
1 | package heim.jobs
2 |
3 | import heim.session.SmartRSessionScopeInterfaced
4 | import heim.tasks.Task
5 | import heim.tasks.TaskFactory
6 | import org.springframework.beans.factory.annotation.Autowired
7 | import org.springframework.beans.factory.annotation.Value
8 | import org.springframework.core.OrderComparator
9 | import org.springframework.stereotype.Component
10 | import org.transmartproject.core.exceptions.InvalidArgumentsException
11 |
12 | import javax.annotation.PostConstruct
13 |
14 | /**
15 | * Created by glopes on 09-10-2015.
16 | */
17 | @SmartRSessionScopeInterfaced
18 | @Component('jobInstance')
19 | class JobInstanceImpl implements JobInstance {
20 |
21 | @Autowired
22 | List taskFactories
23 |
24 | @Value('#{workflowType}')
25 | String workflow
26 |
27 | @PostConstruct
28 | void init() {
29 | // only Spring 4 orders the list automatically
30 | // Grails 2.3 still uses Spring 3
31 | taskFactories.sort(new OrderComparator())
32 | }
33 |
34 | Task createTask(String name, Map arguments) {
35 | TaskFactory selectedTaskFactory =
36 | taskFactories.find { it.handles(name, arguments) }
37 |
38 | if (!selectedTaskFactory) {
39 | throw new InvalidArgumentsException("No task factory found for " +
40 | "task name '$name' and arguments $arguments")
41 | }
42 |
43 | selectedTaskFactory.createTask(name, arguments)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/groovy/heim/rserve/GenericJavaObjectAsJsonRFunctionArg.groovy:
--------------------------------------------------------------------------------
1 | package heim.rserve
2 |
3 | import groovy.json.JsonBuilder
4 |
5 | /**
6 | * Created by glopes on 09-10-2015.
7 | */
8 | class GenericJavaObjectAsJsonRFunctionArg implements RFunctionArg {
9 | String name
10 | Object object
11 |
12 | @Override
13 | String asRExpression() {
14 | def string = new JsonBuilder(object).toString()
15 | "fromJSON('${RUtil.escapeRStringContent(string)}')"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/groovy/heim/rserve/RConnectionProvider.groovy:
--------------------------------------------------------------------------------
1 | package heim.rserve
2 |
3 | import org.codehaus.groovy.grails.commons.GrailsApplication
4 | import org.rosuda.REngine.Rserve.RConnection
5 | import org.springframework.beans.factory.annotation.Autowired
6 | import org.springframework.stereotype.Component
7 |
8 | @Component
9 | class RConnectionProvider {
10 |
11 | @Autowired
12 | GrailsApplication grailsApplication
13 |
14 | RConnection get() {
15 | def conn = new RConnection(grailsApplication.config.RModules.host ?: '127.0.0.1',
16 | grailsApplication.config.RModules.port ?: 6311)
17 | conn.eval("options(bitmapType='cairo')")
18 | conn
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/groovy/heim/rserve/RFunctionArg.groovy:
--------------------------------------------------------------------------------
1 | package heim.rserve
2 |
3 | /**
4 | * Created by glopes on 09-10-2015.
5 | */
6 | interface RFunctionArg {
7 | String getName()
8 | String asRExpression()
9 | }
10 |
--------------------------------------------------------------------------------
/src/groovy/heim/rserve/RScriptOutputManager.groovy:
--------------------------------------------------------------------------------
1 | package heim.rserve
2 |
3 | import heim.SmartRRuntimeConstants
4 | import org.rosuda.REngine.Rserve.RConnection
5 |
6 | /**
7 | * Created by piotrzakrzewski on 08/10/15.
8 | */
9 | class RScriptOutputManager {
10 |
11 | private final RConnection conn
12 | private final File baseDir
13 | private final File outputPath
14 |
15 | def RScriptOutputManager(RConnection conn,
16 | UUID sessionId,
17 | UUID taskId,
18 | SmartRRuntimeConstants constants) {
19 | this.conn = conn
20 | this.baseDir = constants.baseDir
21 | this.outputPath = generateOutputPath(sessionId, taskId)
22 | }
23 |
24 | List downloadFiles() {
25 | if (!outputPath.mkdirs()) {
26 | throw new IOException("Failed creating $outputPath")
27 | }
28 |
29 | List files = listFiles()
30 | files.collect {
31 | downloadFromRserve(it)
32 | }
33 | }
34 |
35 | private File generateOutputPath(UUID session, UUID taskId) {
36 |
37 | [session, taskId].inject(baseDir) { File accum, b ->
38 | new File(accum, b.toString())
39 | }
40 | }
41 |
42 | private List listFiles() {
43 | RUtil.runRCommand(conn, 'list.files()').asNativeJavaObject() as List
44 | }
45 |
46 | private File downloadFromRserve(String name) {
47 | File targetFile = new File(outputPath, name)
48 | if (targetFile.parentFile != outputPath) {
49 | throw new RuntimeException("$targetFile is a child of $outputPath")
50 | }
51 |
52 | conn.openFile(name).withStream { InputStream is ->
53 | targetFile.withOutputStream { OutputStream os ->
54 | os << is
55 | }
56 | }
57 |
58 | targetFile
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/groovy/heim/rserve/RServeSession.groovy:
--------------------------------------------------------------------------------
1 | package heim.rserve
2 |
3 | import groovy.util.logging.Log4j
4 | import heim.session.SmartRSessionScope
5 | import org.codehaus.groovy.grails.commons.GrailsApplication
6 | import org.rosuda.REngine.Rserve.RConnection
7 | import org.springframework.beans.factory.DisposableBean
8 | import org.springframework.beans.factory.annotation.Autowired
9 | import org.springframework.beans.factory.annotation.Value
10 | import org.springframework.stereotype.Component
11 |
12 | import java.util.concurrent.locks.Lock
13 | import java.util.concurrent.locks.ReentrantLock
14 |
15 | @Log4j
16 | @SmartRSessionScope
17 | @Component
18 | class RServeSession implements DisposableBean {
19 |
20 | @Value('#{sessionId}')
21 | String sessionId
22 |
23 | @Autowired
24 | RConnectionProvider rConnectionProvider
25 |
26 | private volatile Thread rConnectionHoldingThread
27 | private volatile boolean shuttingDown
28 | private final Lock rConnectionLock = new ReentrantLock()
29 |
30 | @Lazy
31 | private RConnection rConnection = {
32 | rConnectionProvider.get()
33 | }()
34 |
35 | /*
36 | * TODO: the implementation will block threads unnecessarily.
37 | * Explore implementation with GPars' Agents or Actors?
38 | */
39 | public R doWithRConnection(Closure callable) {
40 | if (shuttingDown) {
41 | log.warn("Rserve session already shutting down; " +
42 | "won't execute code")
43 | return null
44 | }
45 |
46 | rConnectionLock.lockInterruptibly()
47 | rConnectionHoldingThread = Thread.currentThread()
48 | log.debug "Thread ${Thread.currentThread().name} " +
49 | "got access to R connection in session $sessionId"
50 | try {
51 | return callable.call(rConnection)
52 | } finally {
53 | rConnectionLock.unlock()
54 | rConnectionHoldingThread = null
55 | log.debug "Thread ${Thread.currentThread().name} has released " +
56 | "access to R connection in session $sessionId"
57 | }
58 | }
59 |
60 | @Override
61 | void destroy() throws Exception {
62 | shuttingDown = true
63 | if (rConnection.isConnected()) {
64 | log.debug("Asked to disconnect R connection $rConnection")
65 | if (rConnectionHoldingThread) {
66 | log.info("Trying to interrupt thread holding the R connection")
67 | rConnectionHoldingThread.interrupt()
68 | }
69 |
70 | rConnectionLock.lock()
71 | try {
72 | boolean result = rConnection.close()
73 | if (!result) {
74 | log.warn(
75 | "Close() returned false on R connection $rConnection")
76 | } else {
77 | log.debug("Closed R connection $rConnection")
78 | }
79 | } finally {
80 | rConnection.unlock()
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/groovy/heim/rserve/RUtil.java:
--------------------------------------------------------------------------------
1 | package heim.rserve;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.rosuda.REngine.REXP;
5 | import org.rosuda.REngine.REXPMismatchException;
6 | import org.rosuda.REngine.REngineException;
7 | import org.rosuda.REngine.Rserve.RConnection;
8 | import org.rosuda.REngine.Rserve.RserveException;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | /**
14 | * Originally in RModules.
15 | */
16 | public class RUtil {
17 |
18 | private static final Logger LOG = Logger.getLogger(RUtil.class);
19 |
20 | private static Map ESCAPE_SEQUENCES = new HashMap();
21 |
22 | /* maximum part of command to take to show in error messages */
23 | private static final int MAX_COMMAND_PRINT_SIZE = 1024;
24 |
25 | static {
26 | ESCAPE_SEQUENCES.put(0x07, "\\a"); // Bell
27 | ESCAPE_SEQUENCES.put(0x08, "\\b"); // Backspace
28 | ESCAPE_SEQUENCES.put(0x0C, "\\f"); // Form feed
29 | ESCAPE_SEQUENCES.put(0x0A, "\\n"); // Line feed
30 | ESCAPE_SEQUENCES.put(0x0D, "\\r"); // Carriage return
31 | ESCAPE_SEQUENCES.put(0x09, "\\t"); // Horizontal tab
32 | ESCAPE_SEQUENCES.put(0x0B, "\\v"); // Vertical tab
33 | ESCAPE_SEQUENCES.put(0x5C, "\\\\"); // Backslash
34 | ESCAPE_SEQUENCES.put(0x22, "\\\""); // Quotation mark
35 | ESCAPE_SEQUENCES.put(0x27, "\\'"); // Apostrophe
36 | }
37 |
38 | /**
39 | * Escapes a string so that it's safe to include inside a single- or
40 | * double-quoted R string. The result will only contain ASCII
41 | * characters so the string can be serialized to any (reasonable) byte
42 | * stream.
43 | * @param s
44 | * @return
45 | */
46 | public static String escapeRStringContent(String s) {
47 | /* ok to overflow: will just give a NegativeArraySizeException */
48 | StringBuffer sb = new StringBuffer(s.length() * 2);
49 |
50 | /* See StringValue() in R's lexer file (gram.y)
51 | * Mixing \xXX and \\uXXXX escape sequences is forbidden. If a string
52 | * contains a \\u (or \U) sequence, it will be stored internally in
53 | * UTF-8. Functions that use such string can generally choose
54 | * having UTF-8 strings converted to the current locale or work
55 | * with the UTF-8 data directly. See:
56 | * http://cran.r-project.org/doc/manuals/R-ints.html#Encodings-for-CHARSXPs */
57 | final int length = s.length();
58 | for (int offset = 0; offset < length; ) {
59 | final int cp = s.codePointAt(offset);
60 |
61 | if (cp >= 0x80) { /* high characters */
62 | if (cp <= 0xFFFF) {
63 | sb.append(String.format("\\u%04X", cp));
64 | } else {
65 | sb.append(String.format("\\U%08X", cp));
66 | }
67 | } else if (ESCAPE_SEQUENCES.containsKey(cp)) {
68 | sb.append(ESCAPE_SEQUENCES.get(cp));
69 | } else if (cp < 0x20 || cp == 0x7F) {
70 | /* other control characters. This will force the string to
71 | * be in UTF-8, which could otherwise not be necessary, but
72 | * doing it this way keeps the code a little simpler */
73 | sb.append(String.format("\\u%04X", cp));
74 | } else {
75 | sb.appendCodePoint(cp);
76 | }
77 |
78 | offset += Character.charCount(cp);
79 | }
80 |
81 | return sb.toString();
82 | }
83 |
84 | public static REXP runRCommand(RConnection conn, String finalCommand)
85 | throws REXPMismatchException, REngineException {
86 | String wrappedCommand = "try({" + finalCommand + "}, silent=FALSE)";
87 | if (LOG.isDebugEnabled()) {
88 | LOG.debug("About to run: " + wrappedCommand);
89 | }
90 | REXP rObject = conn.parseAndEval(wrappedCommand);
91 |
92 | if (rObject != null && rObject.inherits("try-error")) {
93 | Object c = finalCommand.length() > MAX_COMMAND_PRINT_SIZE ?
94 | (finalCommand.substring(0, MAX_COMMAND_PRINT_SIZE) + "...") :
95 | finalCommand;
96 | throw new RserveException(conn,
97 | "R command failure for: " + c + ": " + rObject.asString());
98 | }
99 |
100 | return rObject;
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/groovy/heim/session/SessionContext.groovy:
--------------------------------------------------------------------------------
1 | package heim.session
2 |
3 | import com.google.common.collect.HashMultimap
4 | import com.google.common.collect.Multimap
5 | import com.google.common.collect.Multimaps
6 | import groovy.util.logging.Log4j
7 | import org.transmartproject.core.users.User
8 |
9 | import java.util.concurrent.atomic.AtomicReference
10 |
11 | /**
12 | * Holds the data for a specific session.
13 | */
14 | @Log4j
15 | class SessionContext {
16 |
17 | public final static String SMART_R_USER_BEAN = 'smartRBean'
18 |
19 | final UUID sessionId
20 |
21 | final String workflowType
22 |
23 | private final AtomicReference lastActive = new AtomicReference<>(new Date())
24 |
25 | private Map beans = [:].asSynchronized()
26 |
27 | private Multimap destructionCallbacks =
28 | Multimaps.synchronizedMultimap(HashMultimap.create())
29 |
30 |
31 | void updateLastModified() {
32 | lastActive.set(new Date())
33 | }
34 |
35 | Date getLastActive() {
36 | lastActive.get()
37 | }
38 |
39 | SessionContext(User user, String workflowType) {
40 | sessionId = UUID.randomUUID()
41 | this.workflowType = workflowType
42 | beans[SMART_R_USER_BEAN] = user
43 | }
44 |
45 | Object getBean(String beanName) {
46 | beans[beanName]
47 | }
48 |
49 | void addBean(String beanName, Object value) {
50 | beans[beanName] = value
51 | }
52 |
53 | Object removeBean(String beanName) {
54 | destructionCallbacks.removeAll(beanName)
55 | beans.remove(beanName)
56 | }
57 |
58 | void registerDestructionCallback(String name, Runnable callback) {
59 | destructionCallbacks.put(name, callback)
60 | }
61 |
62 | void destroy() {
63 | destructionCallbacks.asMap().each { String bean,
64 | Collection callbacks ->
65 | log.debug("Calling destruction callbacks for bean $bean")
66 | callbacks.each {
67 | it.run()
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/groovy/heim/session/SessionFiles.groovy:
--------------------------------------------------------------------------------
1 | package heim.session
2 |
3 | import com.google.common.collect.HashBasedTable
4 | import com.google.common.collect.Table
5 | import org.springframework.stereotype.Component
6 | import org.transmartproject.core.exceptions.NoSuchResourceException
7 |
8 | import java.nio.file.NoSuchFileException
9 |
10 | /**
11 | * A holder for the files that were created during the session,
12 | * mapping the logical task/filename combination to a file path.
13 | */
14 | @Component
15 | @SmartRSessionScope
16 | class SessionFiles {
17 |
18 | private Table files =
19 | HashBasedTable.create()
20 |
21 | void add(UUID task, String filename, File file) {
22 | files.put(task, filename, file)
23 | }
24 |
25 | File get(UUID taskId, String filename) throws NoSuchResourceException {
26 | files.get(taskId, filename)
27 | }
28 |
29 | void removeAll() {
30 | try {
31 | for (File f: files.values()) {
32 | f.delete()
33 | }
34 | }
35 | catch(NoSuchFileException e) {}
36 | finally {
37 | files = HashBasedTable.create()
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/groovy/heim/session/SmartRSessionScope.java:
--------------------------------------------------------------------------------
1 | package heim.session;
2 |
3 | import org.springframework.context.annotation.Scope;
4 | import org.springframework.context.annotation.ScopedProxyMode;
5 |
6 | import java.lang.annotation.Documented;
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 |
10 | /**
11 | * Needs to be written in Java, otherwise you get
12 | * Annotation @org.springframework.context.annotation.Scope is not allowed on
13 | * element ANNOTATION
14 | */
15 | @Scope(value = "smartRSession", proxyMode = ScopedProxyMode.TARGET_CLASS)
16 | @Retention(RetentionPolicy.RUNTIME)
17 | @Documented
18 | public @interface SmartRSessionScope {
19 | }
20 |
--------------------------------------------------------------------------------
/src/groovy/heim/session/SmartRSessionScopeInterfaced.java:
--------------------------------------------------------------------------------
1 | package heim.session;
2 |
3 | import org.springframework.context.annotation.Scope;
4 | import org.springframework.context.annotation.ScopedProxyMode;
5 |
6 | import java.lang.annotation.Documented;
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 |
10 | /**
11 | * Variant of {@link SmartRSessionScope} that creates interface proxies.
12 | */
13 | @Scope(value = "smartRSession", proxyMode = ScopedProxyMode.INTERFACES)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | @Documented
16 | public @interface SmartRSessionScopeInterfaced {
17 | }
18 |
--------------------------------------------------------------------------------
/src/groovy/heim/session/SmartRSessionSpringScope.groovy:
--------------------------------------------------------------------------------
1 | package heim.session
2 |
3 | import groovy.util.logging.Log4j
4 | import org.springframework.beans.BeansException
5 | import org.springframework.beans.factory.ObjectFactory
6 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor
7 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
8 | import org.springframework.beans.factory.config.Scope
9 | import org.springframework.core.NamedThreadLocal
10 | import org.springframework.core.Ordered
11 | import org.springframework.stereotype.Component
12 |
13 | /**
14 | * Managed the @SessionScope scope. Object storage is the session itself.
15 | */
16 | @Log4j
17 | @Component
18 | class SmartRSessionSpringScope implements Scope, BeanFactoryPostProcessor, Ordered {
19 |
20 | private static final String SCOPE_NAME = 'smartRSession'
21 |
22 | private static final boolean PROXY_CLASSES = true // rather than interfaces
23 |
24 | final int order = Ordered.LOWEST_PRECEDENCE;
25 |
26 |
27 | private static ThreadLocal ACTIVE_SESSION =
28 | new NamedThreadLocal("ActiveSmartRSession") {
29 | @Override
30 | protected SessionContext initialValue() {
31 | null
32 | }
33 | };
34 |
35 | static T withActiveSession(SessionContext ctx, Closure closure) {
36 | if (ACTIVE_SESSION.get()) {
37 | if (ACTIVE_SESSION.get() != ctx) {
38 | throw new IllegalStateException(
39 | 'Attempt to set session in context with another ' +
40 | 'one already in place')
41 | } else {
42 | return
43 | }
44 | }
45 | try {
46 | ACTIVE_SESSION.set(ctx)
47 | closure.call()
48 | } finally {
49 | ACTIVE_SESSION.set(null)
50 | }
51 | }
52 |
53 |
54 | private SessionContext getSessionContext() {
55 | def res = ACTIVE_SESSION.get()
56 | if (!res) {
57 | throw new IllegalStateException('No active smartR session set')
58 | }
59 | res
60 | }
61 |
62 | @Override
63 | Object get(String name, ObjectFactory> objectFactory) {
64 | def object = sessionContext.getBean(name)
65 | if (object == null) {
66 | object = objectFactory.getObject()
67 | sessionContext.addBean(name, object)
68 | }
69 | object
70 | }
71 |
72 | @Override
73 | Object remove(String name) {
74 | sessionContext.removeBean(name)
75 | }
76 |
77 | @Override
78 | void registerDestructionCallback(String name, Runnable callback) {
79 | sessionContext.registerDestructionCallback(name, callback)
80 | }
81 |
82 | @Override
83 | Object resolveContextualObject(String key) {
84 | if (sessionContext.hasProperty(key)) {
85 | sessionContext."$key"
86 | }
87 | }
88 |
89 | @Override
90 | String getConversationId() {
91 | sessionContext.sessionId.toString()
92 | }
93 |
94 | @Override
95 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
96 | beanFactory.registerScope('smartRSession', this)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/AbstractTask.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | import com.google.common.base.Objects
4 |
5 | /**
6 | * Created by glopes on 09-10-2015.
7 | */
8 | abstract class AbstractTask implements Task {
9 |
10 | final UUID uuid
11 |
12 | AbstractTask() {
13 | this.uuid = UUID.randomUUID()
14 | }
15 |
16 | @Override
17 | public String toString() {
18 | return Objects.toStringHelper(this)
19 | .add("class", getClass().simpleName)
20 | .add("uuid", uuid)
21 | .toString();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/RScriptExecutionTask.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | import com.google.common.collect.ImmutableList
4 | import com.google.common.collect.ImmutableMap
5 | import groovy.transform.TypeChecked
6 | import groovy.util.logging.Log4j
7 | import heim.SmartRRuntimeConstants
8 | import heim.rserve.RFunctionArg
9 | import heim.rserve.RScriptOutputManager
10 | import heim.rserve.RServeSession
11 | import heim.rserve.RUtil
12 | import heim.session.SessionFiles
13 | import org.rosuda.REngine.REXP
14 | import org.rosuda.REngine.Rserve.RConnection
15 |
16 | import static heim.rserve.RUtil.runRCommand
17 |
18 | @Log4j
19 | @TypeChecked
20 | // TODO: convert to prototype bean
21 | class RScriptExecutionTask extends AbstractTask {
22 |
23 | UUID sessionId
24 | RServeSession rServeSession
25 | SessionFiles sessionFiles
26 | File fileToLoad // absolute
27 | String function = 'main'
28 | List arguments
29 | SmartRRuntimeConstants constants
30 |
31 | @Override
32 | TaskResult call() throws Exception {
33 | rServeSession.doWithRConnection { RConnection conn ->
34 | def outputManager = new RScriptOutputManager(conn, sessionId, uuid, constants)
35 |
36 | REXP res = callR(conn)
37 | List fileNames = outputManager.downloadFiles()
38 |
39 | convertSuccessfulResult(res, fileNames)
40 | }
41 | }
42 |
43 | private void injectScriptDir(RConnection conn) {
44 | def remoteScriptDir = fileToLoad.getParentFile().getParentFile() // We need the HeimScripts root
45 | String path = RUtil.escapeRStringContent(remoteScriptDir.absolutePath)
46 | runRCommand(conn, "remoteScriptDir <- \"$path\"")
47 | }
48 |
49 | private void sourceCoreUtils(RConnection conn) {
50 | def remoteScriptDirRoot = fileToLoad.getParentFile().getParentFile()
51 | File remoteCoreUtilsDir = new File(remoteScriptDirRoot, '_core')
52 | File coreUtilsIndex = new File(remoteCoreUtilsDir, 'index.R')
53 | runRCommand(conn, "source('" +
54 | "${RUtil.escapeRStringContent(coreUtilsIndex.absolutePath)}')")
55 | }
56 |
57 | private REXP callR(RConnection conn) {
58 | runRCommand(conn, 'library(jsonlite)')
59 | injectScriptDir(conn)
60 | sourceCoreUtils(conn)
61 | runRCommand(conn, "source('" +
62 | "${RUtil.escapeRStringContent(fileToLoad.toString())}')")
63 | def namedArguments = arguments.collect { RFunctionArg arg ->
64 | "${arg.name}=${arg.asRExpression()}"
65 | }
66 | runRCommand(conn, "main(${namedArguments.join(', ')})")
67 | }
68 |
69 | private TaskResult convertSuccessfulResult(REXP result,
70 | List files) {
71 | def builder = ImmutableMap.builder()
72 | builder.putAll(massage(result))
73 | builder.put('files', ImmutableList.copyOf(files*.name))
74 |
75 | files.each { File f ->
76 | sessionFiles.add(uuid, f.name, f)
77 | }
78 | new TaskResult(
79 | successful: true,
80 | artifacts: builder.build(),
81 | )
82 | }
83 |
84 | private ImmutableMap massage(REXP result) {
85 | def asNative = result.asNativeJavaObject()
86 | if (!(asNative instanceof Map)) {
87 | asNative = [(asNative): 'value']
88 | }
89 |
90 | /* list(a=1, b='a') results in a map where the keys (!) are arrays
91 | * with one element with value 1.0 and 'a' and the values are the names
92 | * of the list. Invert this and unwrap the values
93 | */
94 | def builder = ImmutableMap.builder()
95 | asNative.each { k, v ->
96 | if (k == []) {
97 | return
98 | }
99 | assert k.getClass().isArray() && ((Object[])k).length == 1
100 | assert v instanceof String
101 | builder.put(v, ((Object[])k)[0])
102 | }
103 |
104 | builder.build()
105 | }
106 |
107 | @Override
108 | void close() throws Exception {
109 | // nothing to do
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/RScriptExecutionTaskFactory.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | import heim.SmartRRuntimeConstants
4 | import heim.jobs.JobInstance
5 | import heim.rserve.GenericJavaObjectAsJsonRFunctionArg
6 | import heim.rserve.RFunctionArg
7 | import heim.rserve.RScriptsSynchronizer
8 | import heim.rserve.RServeSession
9 | import heim.session.SessionFiles
10 | import heim.session.SmartRSessionScope
11 | import org.springframework.beans.factory.annotation.Autowired
12 | import org.springframework.beans.factory.annotation.Value
13 | import org.springframework.core.Ordered
14 | import org.springframework.stereotype.Component
15 | import org.transmartproject.core.exceptions.InvalidArgumentsException
16 |
17 | /**
18 | * Created by glopes on 09-10-2015.
19 | */
20 | @Component
21 | @SmartRSessionScope
22 | class RScriptExecutionTaskFactory implements TaskFactory {
23 |
24 | @Autowired
25 | private RServeSession rServeSession
26 |
27 | @Autowired
28 | private JobInstance jobInstance
29 |
30 | @Autowired
31 | private RScriptsSynchronizer rScriptsSynchronizer
32 |
33 | @Autowired
34 | private SessionFiles sessionFiles
35 |
36 | @Autowired
37 | private SmartRRuntimeConstants constants
38 |
39 | @Value('#{sessionId}')
40 | private UUID sessionId
41 |
42 | final int order = Ordered.LOWEST_PRECEDENCE
43 |
44 | @Override
45 | boolean handles(String taskName, Map argument) {
46 | true // fallback factory, since it has the lowest precedence
47 | }
48 |
49 | private File calculateRemoteScriptPath(String taskType) {
50 | File dir = constants.remoteScriptDirectoryDir
51 | assert dir != null
52 | File workflowDir = new File(dir, jobInstance.workflow)
53 | new File(workflowDir, taskType + '.R')
54 | }
55 |
56 | @Override
57 | Task createTask(String taskName, Map arguments) {
58 | rScriptsSynchronizer.ensureScriptsAreCopied()
59 | try {
60 | File fileToLoad = calculateRemoteScriptPath(taskName)
61 |
62 | new RScriptExecutionTask(
63 | sessionFiles: sessionFiles,
64 | sessionId: sessionId,
65 | rServeSession: rServeSession,
66 | fileToLoad: fileToLoad,
67 | arguments: convertArguments(arguments),
68 | constants: constants,
69 | )
70 | } catch (IOException ioe) {
71 | throw new InvalidArgumentsException("Bad script '$taskName' for " +
72 | "workflow '${jobInstance.workflow}'", ioe)
73 | }
74 | }
75 |
76 | List convertArguments(Map arguments) {
77 | arguments.collect { k, v ->
78 | new GenericJavaObjectAsJsonRFunctionArg(
79 | name: k,
80 | object: v)
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/Task.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | import java.util.concurrent.Callable
4 |
5 | /**
6 | * Created by glopes on 09-10-2015.
7 | */
8 | interface Task extends Callable, AutoCloseable {
9 |
10 | UUID getUuid()
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/TaskFactory.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | import org.springframework.core.Ordered
4 |
5 | /**
6 | * Created by glopes on 09-10-2015.
7 | */
8 | interface TaskFactory extends Ordered {
9 |
10 | boolean handles(String taskName,
11 | Map argument)
12 |
13 | Task createTask(String name,
14 | Map arguments)
15 | }
16 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/TaskResult.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | import com.google.common.collect.ImmutableMap
4 | import groovy.transform.ToString
5 |
6 | /**
7 | * Created by glopes on 09-10-2015.
8 | */
9 | @ToString(includePackage = false, includeNames = true)
10 | final class TaskResult {
11 | final boolean successful
12 | final Exception exception
13 | final ImmutableMap artifacts
14 |
15 | TaskResult(Map args) {
16 | this.successful = args.successful
17 | this.exception = args.exception
18 | this.artifacts = args.artifacts
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/TaskState.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | /**
4 | * Created by glopes on 09-10-2015.
5 | */
6 | enum TaskState {
7 | QUEUED,
8 | RUNNING,
9 | FINISHED,
10 | FAILED
11 | }
12 |
--------------------------------------------------------------------------------
/src/groovy/heim/tasks/TestTaskFactory.groovy:
--------------------------------------------------------------------------------
1 | package heim.tasks
2 |
3 | import heim.tasks.AbstractTask
4 | import heim.tasks.Task
5 | import heim.tasks.TaskFactory
6 | import heim.tasks.TaskResult
7 | import org.springframework.core.Ordered
8 |
9 | import java.util.concurrent.atomic.AtomicBoolean
10 |
11 | /**
12 | * This class is here for technical reasons (test classpath is not available
13 | * when setting up the Spring context).
14 | */
15 | class TestTaskFactory implements TaskFactory {
16 |
17 | public final static String TEST_TASK_NAME = 'test'
18 |
19 | final int order = Ordered.HIGHEST_PRECEDENCE
20 |
21 | @Override
22 | boolean handles(String taskName, Map argument) {
23 | taskName == TEST_TASK_NAME
24 | }
25 |
26 | @Override
27 | Task createTask(String name, Map arguments) {
28 | assert arguments['closure'] instanceof Closure
29 | new AbstractTask() {
30 | AtomicBoolean isClosed = arguments.closed
31 | final Object monitor = arguments.monitor
32 |
33 | @Override
34 | void close() throws Exception {
35 | synchronized (monitor) {
36 | isClosed?.set(true)
37 | monitor.notify()
38 | }
39 | }
40 |
41 | @Override
42 | TaskResult call() throws Exception {
43 | arguments['closure'].call()
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/functional/heim/BaseAPITestCase.groovy:
--------------------------------------------------------------------------------
1 | package heim
2 |
3 | import com.grailsrocks.functionaltest.APITestCase
4 |
5 | import static org.hamcrest.Matchers.is
6 | import static org.junit.Assume.assumeThat
7 |
8 | abstract class BaseAPITestCase extends APITestCase {
9 |
10 | protected void setUp() {
11 | super.setUp()
12 | //To fix "org.apache.http.conn.HttpHostConnectException: Connection to http://localhost:8080 refused"
13 | baseURL = baseURL.replace('localhost', '127.0.0.1')
14 | }
15 |
16 | protected static String buildQueryParameters(Map map) {
17 | // you were supposes to be able to do:
18 | // get url, { foo = 'bar' }
19 | // but it seems to be broken
20 | // this is a workaround
21 | map.collect { k, v ->
22 | "$k=${URLEncoder.encode(v.toString(), 'UTF-8')}"
23 | }.join('&')
24 | }
25 |
26 | protected String /* session id */ createSession(String workflowName) {
27 | post('/RSession/create') {
28 | body json: [
29 | workflow: workflowName
30 | ]
31 | }
32 | assumeThat client.responseStatus, is(201)
33 | JSON.sessionId
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/functional/heim/RSessionControllerTests.groovy:
--------------------------------------------------------------------------------
1 | package heim
2 |
3 | import org.springframework.http.HttpStatus
4 |
5 | import static org.hamcrest.MatcherAssert.assertThat
6 | import static org.hamcrest.Matchers.*
7 | import static org.junit.Assume.assumeThat
8 |
9 | class RSessionControllerTests extends BaseAPITestCase {
10 |
11 | void testCreate() {
12 | post('/RSession/create') {
13 | body json: [
14 | workflow: 'heatmap'
15 | ]
16 | }
17 | assertStatus HttpStatus.CREATED.value()
18 |
19 | assertThat JSON, hasEntry(equalTo('sessionId'), isA(String))
20 | }
21 |
22 | void testDestroy() {
23 | post('/RSession/create') {
24 | body json: [
25 | workflow: 'heatmap',
26 | ]
27 | }
28 | assumeThat client.responseStatus, is(201)
29 | def sessionId = JSON.sessionId
30 |
31 | post('/RSession/delete') {
32 | body json: [
33 | sessionId: sessionId,
34 | ]
35 | }
36 |
37 | assertStatus HttpStatus.ACCEPTED.value()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/runit/run_tests.R:
--------------------------------------------------------------------------------
1 | library(RUnit)
2 |
3 | test.suite <- defineTestSuite("Heim",
4 | dirs = file.path("test/runit/tests"),
5 | testFileRegexp = ".*R$") # any R file
6 |
7 | test.result <- runTestSuite(test.suite)
8 |
9 | printTextProtocol(test.result)
10 | if (test.result$Heim$nFail > 0) {
11 | stop("Some Unit Tests failed - check the log for details.")
12 | } else {
13 | print("All R tests passed.")
14 | }
15 |
--------------------------------------------------------------------------------
/test/runit/tests/boxplot_summary.R:
--------------------------------------------------------------------------------
1 | source("web-app/HeimScripts/boxplot/summary.R")
2 |
3 |
4 | ### main test data ###
5 | test_set <-
6 | data.frame(
7 | Row.Label = c("1007_s_at", "1053_at", "117_at", "121_at", "1255_g_at"),
8 | clinical_numeric = c(179.26, NA, 0, 0.00734,-23.0234),
9 | stringsAsFactors = F
10 | )
11 |
12 | test_set_measurements <-
13 | test_set["clinical_numeric"]
14 |
15 | test_data <- list("n0_s1" = test_set)
16 | test_data_measurements <- list("n0_s1" = test_set_measurements)
17 |
18 | test_set_preprocessed <- test_set
19 | colnames(test_set_preprocessed) <-
20 | c("Row.Label" ,"clinical_numeric_n0_s1")
21 |
22 | .setUp <- function() {
23 | # A .png file is created by produce_boxplot. Should be written to temporary directory
24 | assign("origDirectory", getwd(), envir = .GlobalEnv)
25 | dir <- tempdir()
26 | dir.create(dir)
27 | setwd(dir)
28 |
29 | #loaded_variables and preprocessed are sometimes removed by a test function
30 | assign("loaded_variables", test_data, envir = .GlobalEnv)
31 | }
32 |
33 | .tearDown <- function() {
34 | dir <- getwd()
35 | setwd(origDirectory)
36 | unlink(dir, recursive = T, force = T)
37 | }
38 |
39 |
40 |
41 | ### unit tests for function extract_measurements ###
42 |
43 | #test if function works if data has a "Row.Label" and a "Bio.marker" column
44 | test.extract_measurements.simplecase1 <- function() {
45 | checkEquals(test_data_measurements, extract_measurements(test_data))
46 | }
47 |
48 | #test if it works if data has "Row.Label" column but no "Bio.marker" column
49 | test.extract_measurements.simplecase2 <- function() {
50 | test_data_tmp <-
51 | list("n0_s1" = test_set[, c("Row.Label", "clinical_numeric")])
52 | checkEquals(test_data_measurements, extract_measurements(test_data_tmp))
53 | }
54 |
--------------------------------------------------------------------------------
/test/runit/tests/core_input.R:
--------------------------------------------------------------------------------
1 | source("web-app/HeimScripts/_core/input.R")
2 |
3 | loaded_variables_with_a_vector <- list(should_be_df = c(1,2,3)) # a Vector instead of a df
4 | loaded_variables_df <- data.frame(a = c(1,2,3)) # data.frame instead of a list
5 | loaded_variables_proper1 <- list(box1_n1_s1 = data.frame(a = c(1,2,3) ) )
6 |
7 | HDDproper <- data.frame(Row.Label = letters[1:5], Bio.marker = letters[1:5], NumVect = 1:5 )
8 | ClinicalNumericProper <- data.frame(Row.Label = letters[1:5], NumVect = 1:5 )
9 |
10 |
11 | # Case of loaded_variables containing smt else than a dataframe
12 | test.validateLoadedVariables1 <- function() {
13 | checkException( validateLoadedVariables(loaded_variables_with_a_vector) )
14 | }
15 |
16 | # Case of loaded_variables of type data.frame instead of list
17 | test.validateLoadedVariables2 <- function() {
18 | checkException( validateLoadedVariables(loaded_variables_df ) )
19 | }
20 |
21 | # Case of a proper loaded_variable
22 | test.validateLoadedVariables3 <- function() {
23 | # the inner checkException will throw an exception because there is no exception thrown
24 | # the outer checkException catches it and validates to true
25 | checkException(checkException( validateLoadedVariables(loaded_variables_proper1 ) ))
26 | }
27 |
28 | test.isHDD1 <- function() {
29 | checkException( isHDD(ClinicalNumericProper) )
30 | }
31 |
32 | test.isHDD2 <- function() {
33 | checkTrue( isHDD(HDDproper) )
34 | }
35 |
--------------------------------------------------------------------------------
/test/runit/tests/heatmap_downloadData.R:
--------------------------------------------------------------------------------
1 | source("web-app/HeimScripts/heatmap/downloadData.R")
2 |
3 | .setUp <- function() {
4 | assign("origDirectory", getwd(), envir = .GlobalEnv)
5 | dir <- tempdir()
6 | dir.create(dir)
7 | setwd(dir)
8 | }
9 |
10 | .tearDown <- function() {
11 | dir <- getwd()
12 | setwd(origDirectory)
13 | unlink(dir, recursive = T, force = T)
14 | }
15 |
16 |
17 | test.zip.success <- function() {
18 | write("foobar1", "params.json")
19 | write("foobar2", "heatmap_data.tsv")
20 | write("foobar3", "heatmap_orig_values.tsv")
21 |
22 | main()
23 |
24 | checkTrue(file.exists('analysis_data.zip'))
25 |
26 | zipFiles <- unzip(zipfile = 'analysis_data.zip', list = T)$Name
27 | checkTrue(identical(sort(c("params.json",
28 | "heatmap_data.tsv",
29 | "heatmap_orig_values.tsv")),
30 | sort(zipFiles)))
31 | }
32 |
33 | test.zip.misingfile <- function() {
34 | checkException(main())
35 | }
36 |
--------------------------------------------------------------------------------
/test/runit/tests/heatmap_preprocess.R:
--------------------------------------------------------------------------------
1 | source("web-app/HeimScripts/heatmap/preprocess.R")
2 |
3 | test_data_aggregate <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay1 = c(4,1,4,1), Assay2 = c(4,1,4,1) )
4 |
5 | test_data_drop_empty <- data.frame(Bio.marker=c("","a",NA),b=c(1,2,3) )
6 |
7 | #Simple case
8 | test.aggregate <- function()
9 | {
10 | result <- aggregate.probes(test_data_aggregate)
11 | expected <- data.frame(Row.Label=c("a1","b1"), Bio.marker = c("a","b"), Assay1=c(4,4), Assay2 = c(4,4), stringsAsFactors = F)
12 | checkEquals(result, expected)
13 | }
14 |
15 | test.drop.empty <- function()
16 | {
17 | result <- dropEmptyGene(test_data_drop_empty)
18 | expected <- test_data_drop_empty[2,]
19 | checkEquals(result, expected)
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/test/runit/tests/heatmap_run.R:
--------------------------------------------------------------------------------
1 | source("web-app/HeimScripts/heatmap/run.R")
2 |
3 | ##SE: Fct not used anymore
4 | # # Test slash being removed - issue that used to broke sorting
5 | # test.fixString.slash.case <- function()
6 | # {
7 | # testString <- "a/b"
8 | # result <- fixString(testString)
9 | # expected <- "ab"
10 | # checkEquals(result,expected)
11 | # }
12 | #
13 | # #Dashes are allowed in probe_ids
14 | # test.fixString.dash.case <- function()
15 | # {
16 | # testString <- "a-b"
17 | # result <- fixString(testString)
18 | # expected <- "a-b"
19 | # checkEquals(result,expected)
20 | # }
21 |
22 | test.mergeFetchedData.singledf <- function()
23 | {
24 | df1 <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay1 = 1:4, Assay2 = 4:1 )
25 | test_loaded_variables <- list(d1=df1)
26 | result <- mergeFetchedData(test_loaded_variables)
27 | expected <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay1_d1 = 1:4, Assay2_d1 = 4:1 )
28 | checkEquals(result,expected)
29 | }
30 |
31 | test.mergeFetchedData.twodfs <- function()
32 | {
33 | df1 <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay1 = 1:4, Assay2 = 4:1 )
34 | df2 <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay3 = 1:4, Assay4 = 4:1 )
35 | test_loaded_variables <- list(d1=df1, d2=df2)
36 | merged <- mergeFetchedData(test_loaded_variables)
37 | checkTrue(all(colnames(merged) == c("Row.Label","Bio.marker","Assay1_d1","Assay2_d1","Assay3_d2","Assay4_d2")))
38 | checkTrue(nrow(merged) == 4)
39 | }
40 |
41 | test.get.subset <- function()
42 | {
43 | test_patient_ids <- c("Assay1_blabla_s1","Assay2_bleblee_s2")
44 | result <- getSubset(test_patient_ids)
45 | expected <- c(1,2)
46 | checkEquals(result, expected)
47 | }
48 |
49 | test.get.subject <- function()
50 | {
51 | test_patient_ids <- c("120_Breast_s2", "121_Breast_s1", "122_Breast_s1", "123_Breast_s2")
52 | result <- getSubject(test_patient_ids)
53 | expected <- c("120", "121", "122", "123")
54 | checkEquals(result, expected)
55 | }
56 |
57 | test.getSubset1Length <- function()
58 | {
59 | testMeasurements <- data.frame("a_n0_s1" = 1:3,
60 | "b_n0_s1" = 1:3, "a_n1_s1" = 1:3, "b_n1_s1" = 1:3,
61 | "a_n0_s2" = 1:3, "b_n0_s2" = 1:3 )
62 | expected <- 4
63 | result <- getSubset1Length(testMeasurements)
64 | checkEquals(result,expected)
65 | }
66 |
67 | ## SE: Adapted to fit modified getDesign() fct
68 | test.getDesign <- function()
69 | {
70 | testMeasurements <- data.frame("a_n0_s1" = 1:3,
71 | "b_n0_s1" = 1:3, "a_n1_s1" = 1:3, "b_n1_s1" = 1:3,
72 | "a_n0_s2" = 1:3, "b_n0_s2" = 1:3 )
73 | expected <- matrix(
74 | c(1, 1, 1, 1, 0, 0,
75 | 0, 0, 0, 0, 1, 1),
76 | ncol = 2
77 | )
78 | colnames(expected) <- c("S1", "S2")
79 | result <- getDesign(testMeasurements)
80 | checkEquals(result,expected)
81 | }
82 |
83 | test.getDEgenes <- function()
84 | {
85 | test_set<- data.frame( Row.Label = c("1007_s_at", "1053_at", "117_at"),
86 | Bio.marker = c("a", "b", "c"),
87 | GSM210004_n0_s1 = c(6.5, 4, 5),
88 | GSM210006_n1_s1 = c(8.1, 2, 4),
89 | GSM210007_n1_s1 = c(6.2, 3, 5),
90 | GSM210008_n1_s1 = c(8.34, 2, 5),
91 | GSM210006_n1_s2 = c(8.4, -2, 9),
92 | GSM210007_n1_s2 = c(6.4,2, 12),
93 | stringsAsFactors = F)
94 | result <- getDEgenes(test_set)
95 | result[,-c(1,2)]<-round(result[,-c(1,2)],4)
96 |
97 | expected <- data.frame( Row.Label = c("1007_s_at", "1053_at", "117_at"),
98 | Bio.marker = c("a", "b", "c"),
99 | logfold = c(0.115, -2.75, 5.75),
100 | ttest = c(0.089, -2.1275, 4.4485),
101 | pval = c(0.9306, 0.0548, 0.0008),
102 | adjpval = c(0.9306, 0.0822, 0.0024),
103 | bval = c(-5.7709, -3.7252, 3.1846),
104 | stringsAsFactors = F)
105 | checkEquals(result, expected)
106 | }
107 |
--------------------------------------------------------------------------------
/test/runit/tests/volcanoplot_run.R:
--------------------------------------------------------------------------------
1 | source("web-app/HeimScripts/volcanoplot/run.R")
2 |
3 | ## SE: Fct not used anymore
4 | # # Test slash being removed - issue that used to broke sorting
5 | # test.fixString.slash.case <- function()
6 | # {
7 | # testString <- "a/b"
8 | # result <- fixString(testString)
9 | # expected <- "ab"
10 | # checkEquals(result,expected)
11 | # }
12 | #
13 | # #Dashes are allowed in probe_ids
14 | # test.fixString.dash.case <- function()
15 | # {
16 | # testString <- "a-b"
17 | # result <- fixString(testString)
18 | # expected <- "a-b"
19 | # checkEquals(result,expected)
20 | # }
21 |
22 | test.mergeFetchedData.singledf <- function()
23 | {
24 | df1 <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay1 = 1:4, Assay2 = 4:1 )
25 | test_loaded_variables <- list(d1=df1)
26 | result <- mergeFetchedData(test_loaded_variables)
27 | expected <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay1_d1 = 1:4, Assay2_d1 = 4:1 )
28 | checkEquals(result,expected)
29 | }
30 |
31 | test.mergeFetchedData.twodfs <- function()
32 | {
33 | df1 <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay1 = 1:4, Assay2 = 4:1 )
34 | df2 <- data.frame(Row.Label=c("a1","a2","b1","b2") , Bio.marker= c("a","a","b","b"), Assay3 = 1:4, Assay4 = 4:1 )
35 | test_loaded_variables <- list(d1=df1, d2=df2)
36 | merged <- mergeFetchedData(test_loaded_variables)
37 | checkTrue(all(colnames(merged) == c("Row.Label","Bio.marker","Assay1_d1","Assay2_d1","Assay3_d2","Assay4_d2")))
38 | checkTrue(nrow(merged) == 4)
39 | }
40 |
41 | test.get.subset <- function()
42 | {
43 | test_patient_ids <- c("Assay1_blabla_s1","Assay2_bleblee_s2")
44 | result <- getSubset(test_patient_ids)
45 | expected <- c(1,2)
46 | checkEquals(result, expected)
47 | }
48 |
49 | test.get.subject <- function()
50 | {
51 | test_patient_ids <- c("120_Breast_s2", "121_Breast_s1", "122_Breast_s1", "123_Breast_s2")
52 | result <- getSubject(test_patient_ids)
53 | expected <- c("120", "121", "122", "123")
54 | checkEquals(result, expected)
55 | }
56 |
57 | test.getSubset1Length <- function()
58 | {
59 | testMeasurements <- data.frame("a_n0_s1" = 1:3,
60 | "b_n0_s1" = 1:3, "a_n1_s1" = 1:3, "b_n1_s1" = 1:3,
61 | "a_n0_s2" = 1:3, "b_n0_s2" = 1:3 )
62 | expected <- 4
63 | result <- getSubset1Length(testMeasurements)
64 | checkEquals(result,expected)
65 | }
66 |
67 | ## SE: Adapted to fit modified getDesign() fct
68 | test.getDesign <- function()
69 | {
70 | testMeasurements <- data.frame("a_n0_s1" = 1:3,
71 | "b_n0_s1" = 1:3, "a_n1_s1" = 1:3, "b_n1_s1" = 1:3,
72 | "a_n0_s2" = 1:3, "b_n0_s2" = 1:3 )
73 | expected <- matrix(
74 | c(1, 1, 1, 1, 0, 0,
75 | 0, 0, 0, 0, 1, 1),
76 | ncol = 2
77 | )
78 | colnames(expected) <- c("S1", "S2")
79 | result <- getDesign(testMeasurements)
80 | checkEquals(result,expected)
81 | }
82 |
83 | test.getDEgenes <- function()
84 | {
85 | test_set<- data.frame( Row.Label = c("1007_s_at", "1053_at", "117_at"),
86 | Bio.marker = c("a", "b", "c"),
87 | GSM210004_n0_s1 = c(6.5, 4, 5),
88 | GSM210006_n1_s1 = c(8.1, 2, 4),
89 | GSM210007_n1_s1 = c(6.2, 3, 5),
90 | GSM210008_n1_s1 = c(8.34, 2, 5),
91 | GSM210006_n1_s2 = c(8.4, -2, 9),
92 | GSM210007_n1_s2 = c(6.4,2, 12),
93 | stringsAsFactors = F)
94 | result <- getDEgenes(test_set)
95 | result[,-c(1,2)]<-round(result[,-c(1,2)],4)
96 |
97 | expected <- data.frame( Row.Label = c("1007_s_at", "1053_at", "117_at"),
98 | Bio.marker = c("a", "b", "c"),
99 | logfold = c(0.115, -2.75, 5.75),
100 | ttest = c(0.089, -2.1275, 4.4485),
101 | pval = c(0.9306, 0.0548, 0.0008),
102 | adjpval = c(0.9306, 0.0822, 0.0024),
103 | bval = c(-5.7709, -3.7252, 3.1846),
104 | stringsAsFactors = F)
105 | checkEquals(result, expected)
106 | }
107 |
--------------------------------------------------------------------------------
/test/unit/heim/RScriptOutputManagerSpec.groovy:
--------------------------------------------------------------------------------
1 | package heim
2 |
3 | import grails.test.mixin.TestMixin
4 | import grails.test.mixin.support.GrailsUnitTestMixin
5 | import heim.rserve.RScriptOutputManager
6 | import org.rosuda.REngine.Rserve.RConnection
7 | import spock.lang.Specification
8 |
9 | /**
10 | * See the API for {@link grails.test.mixin.support.GrailsUnitTestMixin} for usage instructions
11 | */
12 | @TestMixin(GrailsUnitTestMixin)
13 | class RScriptOutputManagerSpec extends Specification {
14 |
15 | def setup() {
16 |
17 | }
18 |
19 | def cleanup() {
20 | new File(SmartRRuntimeConstants.instance.baseDir, "bogusfolder/init/plot.png").delete() // Access to a property of SmartRRuntimeConstants and "instance" mechanism inspired by http://stackoverflow.com/a/13846141/535203
21 | }
22 |
23 | void "test getScriptOutput() returning single plot"() {
24 | given:
25 | def conn = new RConnection()
26 | conn.eval('png("plot.png")')
27 | conn.eval('plot(c(0,1),c(1,1))')
28 | conn.eval('dev.off()')
29 | def sessionId = UUID.randomUUID()
30 | def taskId = UUID.randomUUID()
31 | def mngr = new RScriptOutputManager(conn, sessionId, taskId, SmartRRuntimeConstants.instance)
32 |
33 | when: ""
34 | def result = mngr.downloadFiles()
35 | then: "Result is a list containing plot.png and only that one file."
36 | result
37 | result.size() == 1
38 | result[0] instanceof File
39 | result[0].getAbsolutePath() == SmartRRuntimeConstants.instance.baseDir.toString()+"/$sessionId/$taskId/plot.png"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/test/unit/javascript/JavaScriptUnitTestKarmaSuite.groovy:
--------------------------------------------------------------------------------
1 | package javascript
2 |
3 | import de.is24.util.karmatestrunner.junit.KarmaTestSuiteRunner
4 | import org.junit.runner.RunWith
5 |
6 | @RunWith(KarmaTestSuiteRunner)
7 | @KarmaTestSuiteRunner.KarmaProcessName('./node_modules/.bin/karma')
8 | @KarmaTestSuiteRunner.KarmaConfigPath('karma.conf.js')
9 | class JavaScriptUnitTestKarmaSuite {
10 | }
11 |
--------------------------------------------------------------------------------
/test/unit/javascript/runButtonDirectiveTests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('runButton', function() {
4 | var $compile,
5 | $rootScope,
6 | $httpBackend,
7 | $q,
8 | element,
9 | smartRUtils,
10 | rServeService;
11 |
12 | beforeEach(module('smartRApp'));
13 | beforeEach(module('smartRTemplates'));
14 |
15 | beforeEach(inject(function(_$compile_, _$rootScope_, _$httpBackend_, _$q_, _smartRUtils_, _rServeService_) {
16 | $compile = _$compile_;
17 | $rootScope = _$rootScope_;
18 | $httpBackend = _$httpBackend_;
19 | $q = _$q_;
20 | smartRUtils = _smartRUtils_;
21 | rServeService = _rServeService_;
22 |
23 | $rootScope.scriptResults = {};
24 | }));
25 |
26 | it('should show "Creating plot, please wait"', function() {
27 | var html = ' ';
28 | element = $compile(angular.element(html))($rootScope);
29 | $rootScope.$digest();
30 | expect(element.isolateScope().waitMessage).toEqual("Creating plot, please wait");
31 | });
32 |
33 | it('should show "Running R-script, please wait"', function() {
34 | var html = ' ';
35 | element = $compile(angular.element(html))($rootScope);
36 | $rootScope.$digest();
37 | expect(element.isolateScope().waitMessage).toEqual("Running R-script, please wait");
38 | });
39 | });
40 |
41 |
--------------------------------------------------------------------------------
/test/unit/javascript/smartRUtilsServiceTests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('smartRUtils', function() {
4 |
5 | var smartRUtils;
6 |
7 | beforeEach(module('smartRApp'));
8 |
9 | beforeEach(inject(function(_smartRUtils_) {
10 | smartRUtils = _smartRUtils_;
11 | }));
12 |
13 | it('must be defined', function() {
14 | expect(smartRUtils).toBeDefined();
15 | });
16 |
17 | it('has working conceptBoxMapToConceptKeys()', function() {
18 | var params = {a: {concepts: ['c1', 'c2'], valid: true},
19 | 'foo bar __-!*()123 abc': {concepts: ['c3'], valid: true},
20 | b: {concepts: [], valid: true},
21 | c: {concepts: ['12--_- c31/??/*&^/foobar'], valid: true}
22 | };
23 | var expected = {a_n0: 'c1', a_n1: 'c2', 'foo bar __-!*()123 abc_n0': 'c3', c_n0: '12--_- c31/??/*&^/foobar'};
24 | var result = smartRUtils.conceptBoxMapToConceptKeys(params);
25 | expect(result).toEqual(expected);
26 | });
27 |
28 | it('has working makeSafeForCSS()', function() {
29 | var regex = /[_a-zA-Z]+[_a-zA-Z0-9-]*/;
30 | var testStr1 = '!@#$%^&*asdfghj _-;:,<.>/|\\"+=0987654321';
31 | var testStr2 = '__foo-bar-123';
32 | expect(testStr1.replace(regex, '')).not.toEqual('');
33 | expect(smartRUtils.makeSafeForCSS(testStr1).replace(regex, '')).toEqual('');
34 | expect(testStr2.replace(regex, '')).toEqual('');
35 | expect(smartRUtils.makeSafeForCSS(testStr2).replace(regex, '')).toEqual('');
36 | });
37 |
38 | it('has working shortenConcept()', function() {
39 | var testStr1 = '\\foo bar\\ab c d';
40 | var testStr2 = 'foo bar\\abcd';
41 | var testStr3 = '\\a\\b\\c\\d\\e\\f\\g\\h';
42 | var testStr4 = 'foo bar\\abcd\\';
43 | expect(smartRUtils.shortenConcept(testStr1)).toEqual('foo bar/ab c d');
44 | expect(smartRUtils.shortenConcept(testStr2)).toEqual('foo bar/abcd');
45 | expect(smartRUtils.shortenConcept(testStr3)).toEqual('g/h');
46 | expect(smartRUtils.shortenConcept(testStr4)).toEqual('foo bar/abcd');
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/unit/javascript/testSetup.js:
--------------------------------------------------------------------------------
1 | var pageInfo = {};
2 | pageInfo.basePath = 'http://localhost';
3 |
4 | // mockup of smartRApp to avoid GET request before it can be handled by $httpBackend
5 | window.smartRApp = angular.module('smartRApp', ['ngRoute', 'door3.css'])
6 | .run(function($rootScope) {
7 | 'use strict';
8 | $rootScope.smartRPath = '';
9 | });
10 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/_core/index.R:
--------------------------------------------------------------------------------
1 | if (!exists("remoteScriptDir")) { # Needed for unit-tests
2 | remoteScriptDir <- "web-app/HeimScripts/_core"
3 | }
4 |
5 | inputUtils <- paste(remoteScriptDir, "/_core/input.R", sep="")
6 | source(inputUtils)
7 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/_func_test/autosourcing.R:
--------------------------------------------------------------------------------
1 |
2 | main <- function() {
3 |
4 | if (!exists('parseInput')) {
5 | stop("_core/input.R is not properly sourced")
6 | }
7 | return(list(success=TRUE))
8 | }
9 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/_func_test/sample.R:
--------------------------------------------------------------------------------
1 | main <- function(data_to_pass, data_to_write) {
2 | write(data_to_write, file='test_file')
3 | list(a=1,b='foobar',passed=data_to_pass)
4 | }
5 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/_func_test/sourced.R:
--------------------------------------------------------------------------------
1 |
2 | testFun <- function() {
3 | "test"
4 | }
5 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/_func_test/sourcing.R:
--------------------------------------------------------------------------------
1 | demo <- paste(remoteScriptDir, "/_func_test/sourced.R", sep="")
2 | source(demo)
3 |
4 | main <- function() {
5 | res <- testFun()
6 | list("shouldBeTest"=res)
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/_shared_functions/GEX/limmaUtils.R:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | ## This file contains more specific limma and limma-related functionalities ##
3 | ## to check the input data frame content and perform diff expr analysis for ##
4 | ## Microarray GEX data ##
5 | ################################################################################
6 |
7 |
8 |
9 |
10 | ## Loading functions ##
11 | dataFrameUtils <- paste(remoteScriptDir, "/_shared_functions/GEX/DataFrameAndGEXmatrixUtils.R", sep="")
12 |
13 | source(dataFrameUtils)
14 |
15 |
16 | ## Differential expression analysis comparing Subset 1 to subset 2
17 | ## using Limma package
18 | getDEgenes <- function(df) {
19 | measurements <- getMeasurements(df)
20 | design <- getDesign(measurements)
21 | contrast.matrix <- makeContrasts( S2-S1, levels = design )
22 | fit <- lmFit(measurements, design)
23 | fit <- contrasts.fit(fit, contrast.matrix)
24 | fit <- eBayes(fit)
25 | contr <- 1 # We need a vector, not a df, so we'll do [, contr] on all stats
26 | top.fit <- data.frame (
27 | logfold = fit$coefficients[, contr],
28 | ttest = fit$t[, contr],
29 | pval = fit$p.value[, contr],
30 | adjpval = p.adjust(
31 | p = fit$p.value[, contr],
32 | method ='fdr'),
33 | bval = fit$lods[, contr]
34 | )
35 |
36 | cbind(df[,1:2], top.fit)
37 | }
38 |
39 |
40 |
41 | ## Generate the design matrix
42 | getDesign <- function(measurements) {
43 |
44 | subsets <- getSubset(colnames(measurements)) #s1 = 1, s2 = 2
45 | classVectorS1 <- subsets #s1 = 1, s2 = 2
46 | classVectorS2 <- - subsets + 3 #s1 = 2, s2 = 1
47 |
48 | DesignMatrix = cbind(S1=subsets, S2=subsets)
49 | DesignMatrix[which(DesignMatrix[, "S1"]==2), "S1"]=0
50 | DesignMatrix[which(DesignMatrix[, "S2"]==1), "S2"]=0
51 | DesignMatrix[which(DesignMatrix[, "S2"]==2), "S2"]=1
52 |
53 | return(DesignMatrix)
54 |
55 | }
56 |
57 |
58 |
59 | # checking valid measurements on a matrix level
60 | isValidLimmaMeasurements <- function (measurements) {
61 | sum(apply(measurements, 1, validMeasurementsRow)) > 0
62 | }
63 |
64 |
65 |
66 | ## Writing jsn file containing the Limma diff expr analysis results
67 | writeMarkerTable <- function(markerTable, markerTableJson = "markerSelectionTable.json"){
68 | colnames(markerTable) <- c("rowLabel", "biomarker",
69 | "log2FoldChange", "t", "pValue", "adjustedPValue", "B")
70 | jsn <- toJSON(markerTable, pretty = TRUE, digits = I(17))
71 | write(jsn, file = markerTableJson)
72 | }
73 |
74 |
75 |
76 | ## Deleting the json-format topTable
77 | ## from last Limma diff expr analysis run
78 | cleanUpLimmaOutput <- function(markerTableJson = "markerSelectionTable.json") {
79 | if (file.exists(markerTableJson)) {
80 | file.remove(markerTableJson)
81 | }
82 | }
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/_shared_functions/Generic/utils.R:
--------------------------------------------------------------------------------
1 | ######################################################################
2 | ## This file contains generic functionalities when working with ##
3 | ## SmartR data objects ##
4 | ######################################################################
5 |
6 |
7 | ## Retrieve High dim node for vector of patientIDs
8 | getNode <- function(patientIDs) {
9 |
10 | splittedIds <- strsplit(patientIDs,"_") # During merge, which is always
11 | # run we append subset id, either
12 | # _s1 or _s2 to PATIENTID.
13 |
14 | sapply(splittedIds, FUN = tail_elem,n = 2) # In proper patienid subset will
15 | # always be at the end.
16 | # This select last but one elemnt
17 | # - the node
18 | }
19 |
20 |
21 |
22 | getTimelineValues <- function(nodes, ontologyTerms) {
23 | sapply(nodes, function(n) {
24 | metaValue <- ontologyTerms[[n]]$metadata$seriesMeta$value
25 | t <- !is.na(as.numeric(metaValue))
26 | if (length(t) && t) {
27 | as.numeric(metaValue)
28 | } else {
29 | Inf
30 | }
31 | }, USE.NAMES = FALSE)
32 | }
33 |
34 | getSubject <- function(str) {
35 | sub("_.*", "", str)
36 | }
37 |
38 | ## This function dumps the run parameters into a json format file
39 | ## The function can be called either
40 | ## ... with three unnamed params providing the following variables/info in this order:
41 | ## -- max_rows => integer()
42 | ## -- sorting => character("nodes", "subjects")
43 | ## -- ranking => charcter("logfold", "ttest", "pval", "adjpval", "mean", "median", "coef", "range")
44 | ## e.g.: "writeRunParams(max_rows, sorting, ranking)"
45 | ## ... or with a group of named params:
46 | ## e.g.: "writeRunParams(some_parameter=some_parameter, another_param=another_param, ... )"
47 | writeRunParams <- function(...) {
48 |
49 | ## Get the data objects passed
50 | params <- list(...)
51 |
52 | ## The default way of calling the function, this
53 | ## means providing 3 args and without named params
54 | if(length(names(params))==0 & length(params)==3)
55 | names(params) = c("max_rows", "sorting", "ranking")
56 |
57 | ## Get name of this function
58 | calledFun = as.list(sys.call())[[1]]
59 |
60 | ## Checking if params is a named list
61 | ## this means named parameters have been
62 | ## provided as input
63 | if(length(names(params))==0 & length(params)!=0)
64 | print(paste("Please provided named function parameters as input for ", calledFun, sep=": "))
65 |
66 | params <- c(params, fetch_params)
67 |
68 | if (exists("preprocessed") && exists("preprocessing_params")) {
69 | params <- c(params, preprocessing_params)
70 | }
71 | write(toJSON(params, pretty = TRUE), 'params.json')
72 | }
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/correlation/run.R:
--------------------------------------------------------------------------------
1 | library(reshape2)
2 |
3 | main <- function(method = "pearson", transformation = "raw", selectedPatientIDs = integer()) {
4 |
5 | df1 <- loaded_variables$datapoints_n0_s1
6 | df2 <- loaded_variables$datapoints_n1_s1
7 |
8 | if (nrow(df1) == 0) {
9 | stop(paste("Variable '", fetch_params$ontologyTerms$datapoints_n0$name, "' has no patients for subset 1"), sep="")
10 | }
11 | if (nrow(df2) == 0) {
12 | stop(paste("Variable '", fetch_params$ontologyTerms$datapoints_n1$name, "' has no patients for subset 1"), sep="")
13 | }
14 | num_data <- merge(df1, df2, by="Row.Label")
15 | colnames(num_data) <- c("patientID", "x", "y")
16 |
17 | if (transformation == "log2") {
18 | num_data$x <- log2(num_data$x)
19 | num_data$y <- log2(num_data$y)
20 | } else if (transformation == "log10") {
21 | num_data$x <- log10(num_data$x)
22 | num_data$y <- log10(num_data$y)
23 | }
24 | num_data <- na.omit(num_data)
25 | num_data <- num_data[!is.infinite(num_data$x), ]
26 | num_data <- num_data[!is.infinite(num_data$y), ]
27 |
28 | cat_data <- data.frame(patientID=integer(), annotation=character())
29 | filtered.loaded_variables <- get.loaded_variables.by.source("annotations", loaded_variables)
30 | if (length(filtered.loaded_variables) > 0) {
31 | merged.df <- Reduce(function(...) merge(..., by='Row.Label', all=T), filtered.loaded_variables)
32 | merged.df <- merged.df[, colSums(is.na(merged.df)) != nrow(merged.df)] # remove NA columns
33 |
34 | annotations <- apply(merged.df[,-1], 1, function(row) {
35 | row <- row[row != ""]
36 | paste(row, collapse="-AND-")
37 | })
38 | cat_data <- data.frame(
39 | patientID=as.integer(merged.df$Row.Label),
40 | annotation=as.character(annotations)
41 | )
42 | }
43 |
44 | df <- num_data
45 | if (nrow(cat_data) > 0) {
46 | df <- merge(df, cat_data, by="patientID")
47 | } else {
48 | df$annotation <- ''
49 | }
50 |
51 | colnames(df) <- c("patientID", "x", "y", "annotation")
52 |
53 | if (length(selectedPatientIDs) > 0) {
54 | df <- df[df$patientID %in% selectedPatientIDs, ]
55 | }
56 |
57 | corTest <- tryCatch({
58 | test <- cor.test(df$x, df$y, method=method)
59 | test$p.value <- ifelse(test$p.value == 0, paste("<", as.character(.Machine$double.eps)), as.character(test$p.value))
60 | test
61 | }, error = function(e) {
62 | ll <- list()
63 | ll$estimate <- as.numeric(NA)
64 | ll$p.value <- as.numeric(NA)
65 | ll
66 | })
67 |
68 | regLineSlope <- corTest$estimate * (sd(df$y) / sd(df$x))
69 | regLineYIntercept <- mean(df$y) - regLineSlope * mean(df$x)
70 |
71 | output <- list(
72 | correlation = corTest$estimate,
73 | pvalue = corTest$p.value,
74 | regLineSlope = regLineSlope,
75 | regLineYIntercept = regLineYIntercept,
76 | xArrLabel = fetch_params$ontologyTerms$datapoints_n0$fullName,
77 | yArrLabel = fetch_params$ontologyTerms$datapoints_n1$fullName,
78 | method = method,
79 | transformation = transformation,
80 | patientIDs = df$patientID,
81 | annotations = unique(df$annotation),
82 | points = df
83 | )
84 | toJSON(output)
85 | }
86 |
87 | conceptStrToFolderStr <- function(s) {
88 | splitString <- strsplit(s, "")[[1]]
89 | backslashs <- which(splitString == "\\")
90 | substr(s, 0, tail(backslashs, 2)[1])
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/heatmap/downloadData.R:
--------------------------------------------------------------------------------
1 | main <- function() {
2 | kZipFilename <- 'analysis_data.zip'
3 | kFiles <- c(
4 | "params.json",
5 | "heatmap_data.tsv",
6 | "heatmap_orig_values.tsv"
7 | )
8 | kDefaultZipLocation = "/usr/bin/zip"
9 |
10 | # main function
11 | function() {
12 | lapply(kFiles, function(file) {
13 | if (!file.exists(file)) {
14 | stop(paste("File ", file, " doesn't exist. Has the analysis been run?"))
15 | }
16 | })
17 | if (file.exists(kDefaultZipLocation)) {
18 | status <- zip(kZipFilename, kFiles, zip = kDefaultZipLocation)
19 | } else {
20 | # relying on default for zip parameter fails on my box: sh: 1: : Permission denied
21 | status <- zip(kZipFilename, kFiles)
22 | }
23 |
24 | if (status != 0) {
25 | stop(paste("Zipping failed with status ", as.character(status)))
26 | }
27 | list()
28 | }
29 | }()
30 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/heatmap/preprocess.R:
--------------------------------------------------------------------------------
1 | library(WGCNA)
2 |
3 | # Pre-processing data saved in preprocessed
4 | # Pre-processing parameters saved in global preprocessing_params
5 | # They can be considered fresh if the variable preprocessed exists
6 |
7 |
8 | # SE: Just to get things working for dev purposes
9 | #rm(list = ls())
10 | #load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/data.Rda")
11 | #load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/fetchParams.Rda")
12 | #load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/loaded_variables_withLDD.Rda")
13 | #load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/fetch_params_withLDD.Rda")
14 | #setwd("/Users/serge/GitHub/SmartR")
15 | #######
16 |
17 |
18 | if (!exists("remoteScriptDir")) { # Needed for unit-tests
19 | remoteScriptDir <- "web-app/HeimScripts"
20 | }
21 |
22 | ## Loading functions ##
23 | utils <- paste(remoteScriptDir, "/_shared_functions/Generic/utils.R", sep="")
24 | dataFrameUtils <- paste(remoteScriptDir, "/_shared_functions/GEX/DataFrameAndGEXmatrixUtils.R", sep="")
25 |
26 | source(utils)
27 | source(dataFrameUtils)
28 |
29 |
30 | ## SE: Handles Low and high dimensional data. Creates preprocessed variable as a list storing HD and LD data
31 | ## In case no LD data is available this list item is set to NULL
32 | main <- function(aggregate=FALSE) {
33 | msgs = c("")
34 |
35 | df <- loaded_variables[grep("highDimensional", names(loaded_variables))]
36 |
37 | ## High dimensional data
38 | df <- mergeFetchedData(loaded_variables[grep("highDimensional", names(loaded_variables))])
39 | ## Low dimensional data
40 | ld.idx = grep( "^(categoric)|(numeric)", names(loaded_variables))
41 |
42 | if(length(ld.idx)==0)
43 | ld = c()
44 | else
45 | ld = loaded_variables[ld.idx]
46 |
47 |
48 | ## SE: Modified cutoff for samples to 2 instead of 3
49 | good.input <- ncol(df) > 2 && nrow(df) > 0 && sum(df$Bio.marker != "") > 0 # min one sample, contains any rows, non empty Bio.marker column.
50 |
51 | if(aggregate && good.input){
52 | df <- dropEmptyGene(df)
53 |
54 | aggr <- aggregate.probes(df)
55 |
56 |
57 |
58 | ## SE: modified assign value to list type to handle also low dim data
59 | assign("preprocessed", list(HD = aggr, LD=ld), envir = .GlobalEnv)
60 |
61 | discarded.rows <- nrow(df) - nrow(aggr)
62 | msgs <- paste("Total discarded rows:",discarded.rows)
63 | }
64 | else if(aggregate && !good.input){
65 | stop("Incorrect subset - in order to perform probe aggregation more than one sample is needed.")
66 | }else{
67 | msgs <- c("No preprocessing applied.")
68 |
69 | ## SE: modified assign value to list type to handle also low dim data
70 | assign("preprocessed", list(HD = df, LD = ld), envir = .GlobalEnv)
71 | }
72 |
73 | assign("preprocessing_params", list(aggregate=aggregate), envir = .GlobalEnv)
74 |
75 |
76 | list(finished=T,messages=msgs)
77 |
78 | ## SE: For debug
79 | #return(df)
80 | }
81 |
82 |
83 |
84 |
85 | ## SE: For debug
86 | #main(aggregate=T)
87 | # print(dim(df))
88 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/linegraph/corrStats.R:
--------------------------------------------------------------------------------
1 | main <- function(params) {
2 | df <- runResults$data_matrix
3 | # we only care about categoric data in this script
4 | cat.df <- df[df$type == "categoric", ]
5 |
6 | patientIDs <- unique(cat.df$patientID)
7 | bioMarkers <- unique(cat.df$bioMarker)
8 | timeIntegers <- unique(cat.df$timeInteger)
9 |
10 | # create binary vector that represents occurence of params$bioMarker at params$timePoint for every patient
11 | time.df <- cat.df[cat.df$timeInteger == params$timeInteger & cat.df$bioMarker == params$bioMarker, ]
12 | bin.vec_1 <- as.numeric(patientIDs %in% time.df$patientID)
13 |
14 | # do the same for every other timepoint and biomarker to compute correlations
15 | output <- data.frame(bioMarker=character(), timeInteger=integer(), corrCoef=numeric(), pValue=numeric())
16 | for (bioMarker in bioMarkers) {
17 | for (timeInteger in timeIntegers[timeIntegers != params$timeInteger]) {
18 | time.df <- cat.df[cat.df$timeInteger == timeInteger & cat.df$bioMarker == bioMarker, ]
19 | bin.vec_2 <- as.numeric(patientIDs %in% time.df$patientID)
20 | test <- cor.test(bin.vec_1, bin.vec_2, method="pearson")
21 | output <- rbind(output, data.frame(bioMarker=bioMarker,
22 | timeInteger=timeInteger,
23 | corrCoef=as.numeric(test$estimate),
24 | pValue=test$p.value))
25 | }
26 | }
27 | json <- toJSON(output, digits=I(17))
28 | return(json)
29 | }
30 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/linegraph/lineStats.R:
--------------------------------------------------------------------------------
1 | main <- function(params) {
2 | y.vec <- params$yVec
3 |
4 | output <- list()
5 |
6 | mean <- mean(y.vec)
7 | sd <- sd(y.vec)
8 |
9 | output$mean <- mean
10 | output$sd <- sd
11 | toJSON(output)
12 | }
13 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/patientmapper/run.R:
--------------------------------------------------------------------------------
1 | main <- function() {
2 | output <- list()
3 | output$sourceIDs <- c()
4 | output$sourceSubsets <- c()
5 | output$targetIDs <- c()
6 | variables <- names(loaded_variables)
7 | source.vars <- names(loaded_variables)[grep("source", variables)]
8 | target.vars <- names(loaded_variables)[grep("target", variables)]
9 | target.vars <- sub("_s[1-2]{1}$", "", target.vars)
10 | target.vars <- unique(target.vars)
11 | for (var in source.vars) {
12 | data <- loaded_variables[var][[1]]
13 | data <- na.omit(data)
14 | data <- data[data[, 2] != "", ]
15 | if (nrow(data)) {
16 | source.id <- data[1,2]
17 | sourceSubset <- sub("^.*_s", "", var)
18 | output$sourceIDs <- c(output$sourceIDs, source.id)
19 | output$sourceSubsets <- c(output$sourceSubsets, sourceSubset)
20 | }
21 | }
22 | cohortNodes <- data.frame(key=c(),
23 | level=c(),
24 | fullName=c(),
25 | name=c(),
26 | tooltip=c(),
27 | visualAttributes=c(),
28 | subset=c())
29 | for (var in target.vars) {
30 | params <- fetch_params$ontologyTerms[var][[1]]
31 | subsets <- output$sourceSubsets[which(params$name == output$sourceIDs)]
32 | for (subset in subsets) {
33 | cohortNodes <- rbind(cohortNodes, data.frame(key=params$key,
34 | level=params$level,
35 | fullName=params$fullName,
36 | name=params$name,
37 | tooltip=params$tooltip,
38 | visualAttributes=paste(params$visualAttributes, collapse=","),
39 | subset=strtoi(subset)))
40 | }
41 | }
42 | output$cohortNodes <- cohortNodes
43 | toJSON(output)
44 | }
45 |
--------------------------------------------------------------------------------
/web-app/HeimScripts/volcanoplot/run.R:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env Rscript
2 |
3 | library(reshape2)
4 | library(limma)
5 | library(jsonlite)
6 |
7 | ## SE: Just to get things working for dev purposes
8 | # rm(list = ls())
9 | # load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/data.Rda")
10 | # load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/fetchParams.Rda")
11 | # load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/loaded_variables_withLDD.Rda")
12 | # load("/Users/serge/Documents/Projects/SmartR/Development_env_Input_workspace/R_workspace_objects/Heatmap/fetch_params_withLDD.Rda")
13 | # setwd("/Users/serge/GitHub/SmartR")
14 |
15 | if (!exists("remoteScriptDir")) { # Needed for unit-tests
16 | remoteScriptDir <- "web-app/HeimScripts"
17 | }
18 |
19 | ## Loading functions ##
20 | utils <- paste(remoteScriptDir, "/_shared_functions/Generic/utils.R", sep="")
21 | limmaUtils <- paste(remoteScriptDir, "/_shared_functions/GEX/limmaUtils.R", sep="")
22 | dataFrameUtils <- paste(remoteScriptDir, "/_shared_functions/GEX/DataFrameAndGEXmatrixUtils.R", sep="")
23 |
24 |
25 | source(utils)
26 | source(limmaUtils)
27 | source(dataFrameUtils)
28 |
29 |
30 | SUBSET1REGEX <- "_s1$" # Regex identifying columns of subset 1.
31 | markerTableJson <- "markerSelectionTable.json" # Name of the json file with limma outputs
32 |
33 |
34 |
35 |
36 | main <- function() {
37 |
38 |
39 | ## Get Gene Expression Matrix as data frame
40 | data.list = parseInput()
41 | df = data.list$HD
42 |
43 |
44 | ## Defining the content for these variables to
45 | ## perform differential expression analysis:
46 | max_rows = dim(df)[1]
47 | sorting = "nodes"
48 | ranking = "pval"
49 |
50 |
51 | ## File containing the original
52 | ## GEX values that can be downloaded
53 | ## by the user
54 | write.table(
55 | df,
56 | "volcanoplot_orig_values.tsv",
57 | sep = "\t",
58 | na = "",
59 | row.names = FALSE,
60 | col.names = TRUE
61 | )
62 |
63 | df <- addStats(df, ranking, max_rows)
64 | df <- mergeDuplicates(df)
65 | df <- df[!is.na(df["LOGFOLD"][,1]),] # remove rows with NA valued logFC
66 | df <- df[1:min(max_rows, nrow(df)), ] # apply max_rows
67 |
68 | fields <- buildFields(df)
69 | uids <- df[, 1]
70 | patientIDs <- unique(fields["PATIENTID"])[,1]
71 |
72 | negativeLog10PvalValues = -log10(df["PVAL"][,1])
73 |
74 | ## Output json object containing results
75 | jsn <- list(
76 | "uids" = uids,
77 | "logfoldValues" = df["LOGFOLD"][,1],
78 | "pvalValues" = df["PVAL"][,1],
79 | "negativeLog10PvalValues" = negativeLog10PvalValues,
80 | "patientIDs" = patientIDs,
81 | "warnings" = c() # initiate empty vector
82 | )
83 | jsn <- toJSON(jsn)
84 |
85 | writeRunParams()
86 |
87 | measurements <- cleanUp(df) # temporary stats like SD and MEAN need to be removed for clustering to work
88 |
89 | write(jsn, file = "volcanoplot.json")
90 | # json file be served the same way
91 | # like any other file would - get name via
92 | # /status call and then /download
93 |
94 | msgs <- c("Finished successfuly")
95 | list(messages = msgs)
96 |
97 | #return(df)
98 | }
99 |
100 |
101 | #####################
102 | #####################
103 |
104 |
105 | ## #SE: For dev purposes we call the function here
106 | ##out = main()
107 |
108 |
109 |
--------------------------------------------------------------------------------
/web-app/WEB-INF/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Grails application factory bean
8 |
9 |
10 |
11 |
12 |
13 | A bean that manages Grails plugins
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | utf-8
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/web-app/WEB-INF/sitemesh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/web-app/css/boxplot.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/transmart/SmartR/6ce267606ff9b99bb63a5a2db0e1b5303d50d206/web-app/css/boxplot.css
--------------------------------------------------------------------------------
/web-app/css/correlation.css:
--------------------------------------------------------------------------------
1 | .point {
2 | opacity: 0.8;
3 | stroke: #FFFFFF;
4 | }
5 |
6 | .regressionLine {
7 | stroke: #FFA500;
8 | stroke-width: 3px;
9 | }
10 |
11 | .regressionLine:hover {
12 | stroke: #FF0000;
13 | }
14 |
15 | .brush .extent {
16 | fill: blue;
17 | opacity: .125;
18 | shape-rendering: crispEdges;
19 | }
20 |
21 | .bar rect {
22 | fill: #FEEAAF;
23 | stroke: #DEAC3A;
24 | shape-rendering: crispEdges;
25 | }
26 |
27 | .bar text {
28 | fill: #ffa500;
29 | font-weight: bold;
30 | }
31 |
32 | .axis path, .axis line {
33 | fill: none;
34 | stroke: black;
35 | stroke-width: 1px;
36 | shape-rendering: crispEdges;
37 | }
38 |
39 | .axis {
40 | font-size: 10px;
41 | }
42 |
43 | .tick line {
44 | shape-rendering: crispEdges;
45 | opacity: 0.2;
46 | }
47 |
48 | .axisLabels {
49 | text-anchor: middle;
50 | font-size: 14px;
51 | font-weight: bold;
52 | }
53 |
54 | .legend1 {
55 | color: black;
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/web-app/css/heatmap.css:
--------------------------------------------------------------------------------
1 | .square {
2 | }
3 |
4 | .square:hover {
5 | opacity: 0.4;
6 | }
7 |
8 | .squareHighlighted {
9 | opacity: 0.4;
10 | }
11 |
12 | .extraSquare {
13 | }
14 |
15 | .feature {
16 | }
17 |
18 | .featureSortText {
19 | }
20 |
21 | .selected {
22 | opacity: 1;
23 | }
24 |
25 | .extraSquare:hover {
26 | opacity: 0.4;
27 | }
28 |
29 | .bar {
30 | shape-rendering: crispEdges;
31 | }
32 |
33 | .bar:hover {
34 | opacity: 0.4;
35 | }
36 |
37 | .barHighlighted {
38 | opacity: 0.4;
39 | }
40 |
41 | .cutoffHighlight {
42 | opacity: 0.4;
43 | }
44 |
45 | .box {
46 | fill-opacity: 0;
47 | shape-rendering: crispEdges;
48 | }
49 |
50 | .box:hover {
51 | cursor: pointer;
52 | fill-opacity: 0.2;
53 | }
54 |
55 | .sortedBy {
56 | stroke: red;
57 | }
58 |
59 | .rowname {
60 | cursor: pointer;
61 | }
62 |
63 | .rowname:hover {
64 | fill: blue;
65 | text-decoration: underline;
66 | }
67 |
68 | .selectText {
69 | }
70 |
71 | .highlight {
72 | font-weight: bold;
73 | fill: red;
74 | }
75 |
76 | .node {
77 | fill: white;
78 | stroke: rgb(0, 126, 126);
79 | stroke-width: 1.5px;
80 | }
81 |
82 | .node:hover {
83 | cursor: pointer;
84 | fill: rgb(0, 126, 126);
85 | }
86 |
87 | .link {
88 | fill: none;
89 | stroke: #FF8700;
90 | stroke-width: 2px;
91 | }
92 |
93 | .sr-heatmap-table {
94 | border-collapse: collapse;
95 | }
96 |
97 | .sr-heatmap-table th {
98 | font-weight: bold;
99 | }
100 |
101 | .sr-heatmap-table tr {
102 |
103 | }
104 |
105 | .sr-heatmap-table td {
106 | border: solid 1px black;
107 | padding: 4px;
108 | }
109 |
110 | .sr-workflow-controls>div {
111 | float: right;
112 | padding: 5px 15px 5px 15px;
113 | }
114 |
115 | #sr-heatmap-cluster-select {
116 | width: 100%;
117 | }
118 |
119 | #sr-heatmap-zoom-range {
120 | float: right;
121 | }
122 |
123 |
--------------------------------------------------------------------------------
/web-app/css/linegraph.css:
--------------------------------------------------------------------------------
1 | #visualisation {
2 | background: rgb(24, 24, 24);
3 | }
4 |
5 | /* all text elements */
6 | #visualisation text {
7 | fill: rgb(145, 145, 145);
8 | }
9 |
10 | /* axis itself */
11 | .sr-lg-x-axis path {
12 | fill: none;
13 | stroke: none;
14 | }
15 |
16 | /* axis ticks */
17 | .sr-lg-x-axis line {
18 | fill: none;
19 | stroke: rgb(52, 52, 52);
20 | stroke-width: 1px;
21 | }
22 |
23 | /* axis itself */
24 | .sr-lg-y-axis path {
25 | fill: none;
26 | stroke: none;
27 | }
28 |
29 | /* axis ticks */
30 | .sr-lg-y-axis line {
31 | fill: none;
32 | stroke: rgb(52, 52, 52);
33 | stroke-width: 1px;
34 | }
35 |
36 | /* all boxes of cat plot */
37 | .sr-lg-cat-plot-box {
38 | stroke-width: 2px;
39 | stroke: rgb(24, 24, 24);
40 | }
41 |
42 | /* subset 1 boxes of cat plot */
43 | .sr-lg-cat-plot-box.subset-1 {
44 | fill: rgba(94, 198, 216, 0.08);
45 | }
46 |
47 | .sr-lg-cat-plot-box.subset-1:hover {
48 | fill: rgba(94, 198, 216, 1);
49 | }
50 |
51 | /* subset 2 boxes of cat plot */
52 | .sr-lg-cat-plot-box.subset-2 {
53 | fill: rgba(217, 54, 51, 0.08);
54 | }
55 | .sr-lg-cat-plot-box.subset-2:hover {
56 | fill: rgba(217, 54, 51, 1);
57 | }
58 |
59 | .sr-lg-cat-plot-sig-bar {
60 | fill: #009e20;
61 | shape-rendering: crispEdge;
62 | }
63 |
64 | /* icons of cat plot */
65 | .sr-lg-cat-icon {
66 | stroke-width: 1px;
67 | stroke: rgb(24, 24, 24);
68 | shape-rendering: crispEdges;
69 | cursor: pointer;
70 | }
71 | .sr-lg-cat-icon:hover {
72 |
73 | }
74 | /* special class for icon hl */
75 | .icon-highlight {
76 | }
77 |
78 | .sr-lg-cat-stat-icon {
79 | stroke: none;
80 | fill: white;
81 | }
82 |
83 | /* arrows for scrolling up and down */
84 | .sr-lg-shift-element {
85 | fill: rgb(38, 38, 38);
86 | stroke: rgb(67, 67, 67);
87 | }
88 | .sr-lg-shift-element:hover {
89 | fill: rgb(45, 45, 45);
90 | cursor: pointer;
91 | }
92 |
93 | /* all boxes for num plots */
94 | .sr-lg-num-plot>rect {
95 | fill: none;
96 | stroke: none;
97 | }
98 |
99 | /* legend rects for subset 1 */
100 | .sr-lg-num-legend.subset-1 {
101 | fill: rgb(94, 198, 216);
102 | }
103 |
104 | /* legend rects for subset 1 */
105 | .sr-lg-num-legend.subset-2 {
106 | fill: rgb(217, 54, 51);
107 | }
108 |
109 | /* boxplots */
110 | .sr-lg-boxplot rect {
111 | stroke-width: 1px;
112 | shape-rendering: crispEdges;
113 | }
114 | .sr-lg-boxplot.subset-1 rect {
115 | stroke: none;
116 | fill: rgb(94, 198, 216);
117 | }
118 |
119 | .sr-lg-boxplot.subset-2 rect {
120 | stroke: none;
121 | fill: rgb(217, 54, 51);
122 | }
123 | .sr-lg-boxplot rect:hover {
124 | opacity: 0.5;
125 | }
126 |
127 | /* connection line between boxplots */
128 | .sr-lg-timeline {
129 | fill: none;
130 | stroke-width: 2px;
131 | }
132 | .sr-lg-timeline-highlight {
133 | stroke: yellow !important;
134 | stroke-width: 4px;
135 | }
136 | .sr-lg-timeline.subset-1 {
137 | stroke: rgb(94, 198, 216);
138 | }
139 | .sr-lg-timeline.subset-2 {
140 | stroke: rgb(217, 54, 51);
141 | }
142 |
143 | /* special hl class for all timeline objects */
144 | .timeline-lowlight {
145 | opacity: 0.2;
146 | }
147 |
148 | .sr-lg-sd-line>text {
149 | }
150 |
151 | .sr-lg-sd-line>line {
152 | fill: none;
153 | stroke-width: 3px;
154 | }
155 |
156 | /* invisible axis box to catch mouse events */
157 | .sr-lg-time-element > rect {
158 | opacity: 0;
159 | }
160 | .sr-lg-time-element > rect:hover {
161 | cursor: move;
162 | }
163 |
164 | /* arrows that appear left and right when hovering over axis element */
165 | .sr-lg-time-element > polygon {
166 | fill: rgba(94, 198, 216, 0.7);
167 | }
168 |
169 | /* control panel */
170 | .sr-workflow-controls>div {
171 | float: right;
172 | padding: 5px 15px 5px 15px;
173 | }
174 |
175 | .d3-tip {
176 | background: rgba(145, 145, 145, 0.8) !important;
177 | color: #000 !important;
178 | }
179 |
180 | .d3-tip:after {
181 | color: rgba(145, 145, 145, 0.8) !important;
182 | }
183 |
184 | .sr-lg-legend-item {
185 | cursor: ns-resize;
186 | }
187 |
188 | .sr-lg-legend-item>rect {
189 | fill: white;
190 | opacity: 0;
191 | }
192 |
--------------------------------------------------------------------------------
/web-app/css/patientmappper.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/transmart/SmartR/6ce267606ff9b99bb63a5a2db0e1b5303d50d206/web-app/css/patientmappper.css
--------------------------------------------------------------------------------
/web-app/css/tooltip.css:
--------------------------------------------------------------------------------
1 | .d3-tip {
2 | z-index: 9999;
3 | line-height: 1;
4 | font-weight: bold;
5 | padding: 12px;
6 | background: rgba(0, 0, 0, 0.8);
7 | color: #fff;
8 | border-radius: 2px;
9 | pointer-events: none;
10 | }
11 |
12 | /* Creates a small triangle extender for the tooltip */
13 | .d3-tip:after {
14 | box-sizing: border-box;
15 | display: inline;
16 | font-size: 16px;
17 | width: 100%;
18 | line-height: 0.8;
19 | color: rgba(0, 0, 0, 0.8);
20 | position: absolute;
21 | pointer-events: none;
22 | }
23 |
24 | /* Northward tooltips */
25 | .d3-tip.n:after {
26 | content: "\25BC";
27 | margin: -1px 0 0 0;
28 | top: 100%;
29 | left: 0;
30 | text-align: center;
31 | }
32 |
33 | /* Eastward tooltips */
34 | .d3-tip.e:after {
35 | content: "\25C0";
36 | margin: -4px 0 0 0;
37 | top: 50%;
38 | left: -12px;
39 | }
40 |
41 | /* Southward tooltips */
42 | .d3-tip.s:after {
43 | content: "\25B2";
44 | margin: 0 0 1px 0;
45 | top: -8px;
46 | left: 0;
47 | text-align: center;
48 | }
49 |
50 | /* Westward tooltips */
51 | .d3-tip.w:after {
52 | content: "\25B6";
53 | margin: -4px 0 0 -1px;
54 | top: 50%;
55 | left: 100%;
56 | }
57 |
--------------------------------------------------------------------------------
/web-app/css/volcanoplot.css:
--------------------------------------------------------------------------------
1 | .point {
2 |
3 | }
4 |
5 | .axis path,
6 | .axis line {
7 | fill: none;
8 | stroke: black;
9 | shape-rendering: crispEdges;
10 | }
11 |
12 | .square {
13 |
14 | }
15 |
16 | .tick line {
17 | shape-rendering: crispEdges;
18 | opacity: 0.2;
19 | }
20 |
21 | .pLine {
22 | stroke: red;
23 | stroke-width: 2px;
24 | shape-rendering: crispEdges;
25 | }
26 |
27 | .pHandle {
28 | cursor: ns-resize;
29 | opacity: 0;
30 | }
31 |
32 | .logFCLine {
33 | stroke: #0000FF;
34 | stroke-width: 2px;
35 | shape-rendering: crispEdges;
36 | }
37 |
38 | .logFCHandle {
39 | cursor: ew-resize;
40 | opacity: 0;
41 | }
42 |
43 | .axisText {
44 | font-size: 14px;
45 | }
46 |
47 | .volcanoplot-table, .myth, .mytd {
48 | border: 1px solid black;
49 | border-collapse: collapse;
50 | }
51 |
52 | .myth, .mytd {
53 | padding: 5px;
54 | }
55 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/controllers/boxplot.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=boxplot.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.controller('BoxplotController', [
6 | '$scope',
7 | 'smartRUtils',
8 | 'commonWorkflowService',
9 | function($scope, smartRUtils, commonWorkflowService) {
10 |
11 | commonWorkflowService.initializeWorkflow('boxplot', $scope);
12 |
13 | $scope.fetch = {
14 | running: false,
15 | disabled: false,
16 | button: {
17 | disabled: false,
18 | message: ''
19 | },
20 | loaded: false,
21 | selectedBiomarkers: [],
22 | conceptBoxes: {
23 | numData: {concepts: [], valid: false},
24 | highDimensional: {concepts: [], valid: false},
25 | groups: {concepts: [], valid: true}
26 | }
27 | };
28 |
29 | $scope.runAnalysis = {
30 | running: false,
31 | disabled: true,
32 | scriptResults: {},
33 | params: {
34 | transformation: 'raw'
35 | }
36 | };
37 |
38 | $scope.$watch(function() {
39 | return $scope.fetch.conceptBoxes.highDimensional.concepts.length + ' ' + $scope.fetch.selectedBiomarkers.length;
40 | },
41 | function() {
42 | if ($scope.fetch.conceptBoxes.highDimensional.concepts.length > 0 &&
43 | ($scope.fetch.selectedBiomarkers.length === 0 || $scope.fetch.selectedBiomarkers.length > 10)) {
44 | $scope.fetch.button.disabled = true;
45 | $scope.fetch.button.message = 'Please select between 1 and 10 biomarkers for your high dimensional data';
46 | } else {
47 | $scope.fetch.button.disabled = false;
48 | $scope.fetch.button.message = '';
49 | }
50 | });
51 |
52 | $scope.$watchGroup(['fetch.running', 'runAnalysis.running'],
53 | function(newValues) {
54 | var fetchRunning = newValues[0],
55 | runAnalysisRunning = newValues[1];
56 |
57 | // clear old results
58 | if (fetchRunning) {
59 | $scope.runAnalysis.scriptResults = {};
60 | }
61 |
62 | // disable tabs when certain criteria are not met
63 | $scope.fetch.disabled = runAnalysisRunning;
64 | $scope.runAnalysis.disabled = fetchRunning || !$scope.fetch.loaded;
65 | }
66 | );
67 |
68 | }]);
69 |
70 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/controllers/correlation.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=correlation.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.controller('CorrelationController',
6 | ['$scope', 'smartRUtils', 'commonWorkflowService', function($scope, smartRUtils, commonWorkflowService) {
7 |
8 | commonWorkflowService.initializeWorkflow('correlation', $scope);
9 |
10 | $scope.fetch = {
11 | disabled: false,
12 | running: false,
13 | loaded: false,
14 | conceptBoxes: {
15 | datapoints: {concepts: [], valid: false},
16 | annotations: {concepts: [], valid: true}
17 | }
18 | };
19 |
20 | $scope.runAnalysis = {
21 | disabled: true,
22 | running: false,
23 | scriptResults: {},
24 | params: {
25 | method: 'pearson',
26 | transformation: 'raw'
27 | }
28 | };
29 |
30 | $scope.$watch('runAnalysis.params.transformation', function(newValue, oldValue) {
31 | // spearman and kendall are resistant to log transformation. Therefor the default to spearman if log used
32 | if (newValue !== oldValue && newValue !== 'raw' && $scope.runAnalysis.params.method === 'pearson') {
33 | $scope.runAnalysis.params.method = 'spearman';
34 | }
35 | });
36 |
37 | $scope.$watchGroup(['fetch.running', 'runAnalysis.running'],
38 | function(newValues) {
39 | var fetchRunning = newValues[0],
40 | runAnalysisRunning = newValues[1];
41 |
42 | // clear old results
43 | if (fetchRunning) {
44 | $scope.runAnalysis.scriptResults = {};
45 | }
46 |
47 | // disable tabs when certain criteria are not met
48 | $scope.fetch.disabled = runAnalysisRunning;
49 | $scope.runAnalysis.disabled = fetchRunning || !$scope.fetch.loaded;
50 | }
51 | );
52 |
53 | }]);
54 |
55 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/controllers/heatmap.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=heatmap.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.controller('HeatmapController', [
6 | '$scope',
7 | 'commonWorkflowService',
8 | 'smartRUtils',
9 | function($scope, commonWorkflowService, smartRUtils) {
10 |
11 | commonWorkflowService.initializeWorkflow('heatmap', $scope);
12 |
13 | // ------------------------------------------------------------- //
14 | // Fetch data //
15 | // ------------------------------------------------------------- //
16 | $scope.fetch = {
17 | disabled: false,
18 | running: false,
19 | loaded: false,
20 | conceptBoxes: {
21 | highDimensional: {concepts: [], valid: false},
22 | numeric: {concepts: [], valid: true},
23 | categoric: {concepts: [], valid: true}
24 | },
25 | selectedBiomarkers: [],
26 | scriptResults: {}
27 | };
28 |
29 | // ------------------------------------------------------------- //
30 | // Preprocess //
31 | // ------------------------------------------------------------- //
32 | $scope.preprocess = {
33 | disabled: true,
34 | running: false,
35 | params: {
36 | aggregate: false
37 | },
38 | scriptResults: {}
39 | };
40 |
41 | // ------------------------------------------------------------- //
42 | // Run Heatmap //
43 | // ------------------------------------------------------------- //
44 | $scope.runAnalysis = {
45 | disabled: true,
46 | running: false,
47 | params: {
48 | selections: {
49 | selectedRownames: [],
50 | },
51 | max_row: 100,
52 | sorting: 'nodes',
53 | ranking: 'coef',
54 | geneCardsAllowed: false,
55 | },
56 | download: {
57 | disabled: true
58 | },
59 | scriptResults: {}
60 | };
61 |
62 | $scope.common = {
63 | totalSamples: 0,
64 | numberOfRows: 0,
65 | subsets: 0
66 | };
67 |
68 | $scope.$watchGroup(['fetch.running', 'preprocess.running', 'runAnalysis.running'], function(newValues) {
69 | var fetchRunning = newValues[0],
70 | preprocessRunning = newValues[1],
71 | runAnalysisRunning = newValues[2];
72 |
73 | // clear old results
74 | if (fetchRunning) {
75 | $scope.preprocess.scriptResults = {};
76 | $scope.runAnalysis.scriptResults = {};
77 | $scope.runAnalysis.params.ranking = '';
78 | $scope.common.subsets = smartRUtils.countCohorts();
79 | }
80 |
81 | // clear old results
82 | if (preprocessRunning) {
83 | $scope.runAnalysis.scriptResults = {};
84 | $scope.runAnalysis.params.ranking = '';
85 | $scope.common.subsets = smartRUtils.countCohorts();
86 | }
87 |
88 | // disable tabs when certain criteria are not met
89 | $scope.fetch.disabled = preprocessRunning || runAnalysisRunning;
90 | $scope.preprocess.disabled = fetchRunning || runAnalysisRunning || !$scope.fetch.loaded;
91 | $scope.runAnalysis.disabled = fetchRunning || preprocessRunning || !$scope.fetch.loaded;
92 |
93 | // disable buttons when certain criteria are not met
94 | $scope.runAnalysis.download.disabled = runAnalysisRunning ||
95 | $.isEmptyObject($scope.runAnalysis.scriptResults);
96 |
97 | // set ranking criteria
98 | if (!fetchRunning &&
99 | !preprocessRunning &&
100 | $scope.common.totalSamples < 2 &&
101 | $scope.runAnalysis.params.ranking === '') {
102 | $scope.runAnalysis.params.ranking = 'mean';
103 | } else if (!fetchRunning &&
104 | !preprocessRunning &&
105 | $scope.common.subsets < 2 &&
106 | $scope.runAnalysis.params.ranking === '') {
107 | $scope.runAnalysis.params.ranking = 'coef';
108 | } else if (!fetchRunning &&
109 | !preprocessRunning &&
110 | $scope.common.subsets > 1 &&
111 | $scope.runAnalysis.params.ranking === '') {
112 | $scope.runAnalysis.params.ranking = 'adjpval';
113 | }
114 | });
115 | }]);
116 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/controllers/ipaconnector.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=ipaconnector.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.controller('IpaconnectorController', [
6 | '$scope',
7 | 'commonWorkflowService',
8 | 'smartRUtils',
9 | function($scope, commonWorkflowService, smartRUtils) {
10 | commonWorkflowService.initializeWorkflow('ipaconnector', $scope);
11 |
12 | $scope.debug = true;
13 |
14 | // ------------------------------------------------------------- //
15 | // Fetch data //
16 | // ------------------------------------------------------------- //
17 | $scope.fetch = {
18 | disabled: false,
19 | running: false,
20 | loaded: false,
21 | conceptBoxes: {
22 | highDimensional: {concepts: [], valid: false},
23 | },
24 | selectedBiomarkers: [],
25 | scriptResults: {}
26 | };
27 |
28 | // ------------------------------------------------------------- //
29 | // IPA //
30 | // ------------------------------------------------------------- //
31 | function clearAll() {
32 | $scope.differentiallyExpressed = null
33 | }
34 | $scope.clearAll = clearAll;
35 |
36 | $scope.$watch('runAnalysis.params', function(newValue) {
37 | clearAll();
38 | }, true);
39 |
40 | $scope.runAnalysis = {
41 | disabled: false,
42 | running: false,
43 | params: {
44 | significanceMeasure: 'pval',
45 | significanceCutoff: 0.05,
46 | fcCutoff: 1.5,
47 | },
48 | ipaCredentials: {
49 | username: null,
50 | password: null,
51 | },
52 | ipaConnectionIsSecure: window.location.protocol == 'https:' ? true : false,
53 | scriptResults: {}
54 | };
55 |
56 | function listsToSimpleTable(lists) {
57 | if (lists == null)
58 | return null;
59 |
60 | var simpleTable;
61 | if ('header' in lists) {
62 | simpleTable = {
63 | header: lists.header,
64 | rows: [],
65 | };
66 | if (lists.columnTypes != null) {
67 | simpleTable.columnTypes = lists.columnTypes;
68 | }
69 | // ensure all columns exist
70 | for (var c=0; c < simpleTable.header.length; c++) {
71 | if (! c in lists)
72 | throw "column "+c+" not in lists";
73 | }
74 | } else {
75 | // NOTE this is not ordered
76 | simpleTable = {
77 | header: [],
78 | rows: [],
79 | };
80 | for (var c in lists) {
81 | simpleTable.header.push(c);
82 | }
83 | }
84 |
85 | // empty
86 | if (simpleTable.header.length == 0 || lists[simpleTable.header[0]].length == 0)
87 | return null;
88 |
89 | for (var i=0; i < lists[simpleTable.header[0]].length; i++) {
90 | var row = []
91 | for (var c=0; c < simpleTable.header.length; c++) {
92 | row.push(lists[simpleTable.header[c]][i]);
93 | }
94 | simpleTable.rows.push(row);
95 | }
96 |
97 | return simpleTable;
98 | }
99 |
100 | $scope.$watch('fetch.conceptBoxes.highDimensional', function(newValue) {
101 | $scope.differentiallyExpressed = null;
102 | }, true);
103 |
104 | $scope.$watch('runAnalysis.scriptResults', function(newValue) {
105 | $scope.differentiallyExpressed = listsToSimpleTable($scope.runAnalysis.scriptResults);
106 | }, true);
107 |
108 | $scope.common = {
109 | totalSamples: 0,
110 | numberOfRows: 0,
111 | subsets: 0
112 | };
113 |
114 | $scope.$watchGroup(['fetch.running', 'fetch.loaded', 'runAnalysis.running'], function(newValues) {
115 | var fetchRunning = newValues[0],
116 | fetchLoaded = newValues[1],
117 | runAnalysisRunning = newValues[2];
118 |
119 | // clear old results
120 | if (fetchRunning) {
121 | $scope.runAnalysis.scriptResults = {};
122 | $scope.common.subsets = smartRUtils.countCohorts();
123 | }
124 |
125 | // disable tabs when certain criteria are met
126 | $scope.fetch.disabled = runAnalysisRunning;
127 | $scope.runAnalysis.disabled = fetchRunning || !fetchLoaded;
128 | });
129 | }]);
130 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/controllers/linegraph.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=linegraph.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.controller('LinegraphController',
6 | ['$scope', 'smartRUtils', 'commonWorkflowService', function($scope, smartRUtils, commonWorkflowService) {
7 |
8 | commonWorkflowService.initializeWorkflow('linegraph', $scope);
9 |
10 | $scope.fetch = {
11 | disabled: false,
12 | running: false,
13 | loaded: false,
14 | selectedBiomarkers: [],
15 | button: {
16 | disabled: false
17 | },
18 | conceptBoxes: {
19 | highData: {concepts: [], valid: true},
20 | numData: {concepts: [], valid: true},
21 | catData: {concepts: [], valid: true}
22 | }
23 | };
24 |
25 | $scope.runAnalysis = {
26 | disabled: true,
27 | running: false,
28 | scriptResults: {},
29 | params: {}
30 | };
31 |
32 | $scope.$watch(function() {
33 | return $scope.fetch.conceptBoxes.highData.concepts.length + ' ' + $scope.fetch.selectedBiomarkers.length;
34 | },
35 | function() {
36 | if ($scope.fetch.conceptBoxes.highData.concepts.length > 0 &&
37 | ($scope.fetch.selectedBiomarkers.length === 0 || $scope.fetch.selectedBiomarkers.length > 10)) {
38 | $scope.fetch.button.disabled = true;
39 | $scope.fetch.button.message = 'Please select between 1 and 10 biomarker for your high dimensional data';
40 | } else {
41 | $scope.fetch.button.disabled = false;
42 | $scope.fetch.button.message = '';
43 | }
44 | });
45 |
46 | $scope.$watchGroup(['fetch.running', 'runAnalysis.running'],
47 | function(newValues) {
48 | var fetchRunning = newValues[0],
49 | runAnalysisRunning = newValues[1];
50 |
51 | // clear old results
52 | if (fetchRunning) {
53 | $scope.runAnalysis.scriptResults = {};
54 | }
55 |
56 | // disable tabs when certain criteria are not met
57 | $scope.fetch.disabled = runAnalysisRunning;
58 | $scope.runAnalysis.disabled = fetchRunning || !$scope.fetch.loaded;
59 | }
60 | );
61 |
62 | }]);
63 |
64 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/controllers/patientmapper.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=patientmapper.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.controller('PatientmapperController',
6 | ['$scope', 'smartRUtils', 'commonWorkflowService', function($scope, smartRUtils, commonWorkflowService) {
7 |
8 | commonWorkflowService.initializeWorkflow('patientmapper', $scope);
9 |
10 | $scope.fetch = {
11 | disabled: false,
12 | running: false,
13 | loaded: false,
14 | conceptBoxes: {
15 | source: {concepts: [], valid: false},
16 | target: {concepts: [], valid: false}
17 | }
18 | };
19 |
20 | $scope.runAnalysis = {
21 | disabled: true,
22 | running: false,
23 | scriptResults: {},
24 | params: {}
25 | };
26 |
27 |
28 | $scope.$watch('runAnalysis.scriptResults', function(results) {
29 | if (!$.isEmptyObject(results)) {
30 | clearQuery();
31 | results.cohortNodes.forEach(function(d) {
32 | var panel = document.querySelector('.panelModel[subset="' + d.subset + '"] .panelBoxList');
33 | var concept = {
34 | name: d.name,
35 | key: d.key,
36 | level: d.level,
37 | tooltip: d.tooltip,
38 | dimcode: d.fullname,
39 | value: {
40 | mode: 'novalue',
41 | operator: 'LT',
42 | highlowselect: 'N',
43 | },
44 | oktousevalues: 'N',
45 | nodeType: 'alphaicon',
46 | visualattributes: d.visualAttributes,
47 | applied_path: '@',
48 | modifiedNode: {}
49 | };
50 | selectConcept(createPanelItemNew(panel, concept));
51 | });
52 | appendQueryPanelInto(1);
53 | appendQueryPanelInto(2);
54 | runAllQueries(function() {
55 | $.ajax({
56 | url: pageInfo.basePath + '/chart/clearGrid',
57 | method: 'POST',
58 | data: {
59 | charttype: 'cleargrid',
60 | }
61 | });
62 | });
63 | }
64 | });
65 | }]);
66 |
67 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/controllers/volcanoplot.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=volcanoplot.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.controller('VolcanoplotController', [
6 | '$scope',
7 | 'smartRUtils',
8 | 'commonWorkflowService',
9 | function($scope, smartRUtils, commonWorkflowService) {
10 |
11 | commonWorkflowService.initializeWorkflow('volcanoplot', $scope);
12 |
13 | $scope.fetch = {
14 | disabled: false,
15 | running: false,
16 | loaded: false,
17 | conceptBoxes: {
18 | highDimensional: {concepts: [], valid: false}
19 | }
20 | };
21 |
22 | $scope.runAnalysis = {
23 | params: {},
24 | disabled: true,
25 | running: false,
26 | scriptResults: {}
27 | };
28 |
29 | $scope.$watchGroup(['fetch.running', 'runAnalysis.running'], function(newValues) {
30 | var fetchRunning = newValues[0],
31 | runAnalysisRunning = newValues[1];
32 |
33 | // clear old results
34 | if (fetchRunning) {
35 | $scope.runAnalysis.scriptResults = {};
36 | }
37 |
38 | // disable tabs when certain criteria are not met
39 | $scope.fetch.disabled = runAnalysisRunning;
40 | $scope.runAnalysis.disabled = fetchRunning || !$scope.fetch.loaded;
41 | });
42 |
43 | }]);
44 |
45 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/capturePlotButton.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=capturePlotButton.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('capturePlotButton', [function() {
6 |
7 | return {
8 | restrict: 'E',
9 | scope: {
10 | disabled: '=?',
11 | filename: '@',
12 | target: '@'
13 | },
14 | template: 'Capture SVG ',
15 | link: function(scope, elements) {
16 | if (!scope.filename) {
17 | // default filename
18 | scope.filename = 'image.svg';
19 | }
20 |
21 |
22 | var template_btn = elements.children()[0];
23 | template_btn.addEventListener('click', function() {
24 | var svg = $(scope.target + ' svg');
25 | if (!svg.length) {
26 | return;
27 | }
28 |
29 | var smartRSheets = [];
30 | for (var i = 0; i < document.styleSheets.length; i++) {
31 | var sheet = document.styleSheets[i];
32 | if (sheet.href && sheet.href.indexOf('smart-r') !== -1) {
33 | smartRSheets.push(sheet);
34 | }
35 | }
36 |
37 | var rules = [];
38 | smartRSheets.forEach(function(d) {
39 | for (var key in d.cssRules) {
40 | if (d.cssRules.hasOwnProperty(key) && d.cssRules[key] instanceof CSSStyleRule) {
41 | rules.push(d.cssRules[key].cssText);
42 | }
43 | }
44 | });
45 |
46 | function b64toBlob(b64Data, contentType, sliceSize) {
47 | contentType = contentType || '';
48 | sliceSize = sliceSize || 512;
49 |
50 | var byteCharacters = atob(b64Data);
51 | var byteArrays = [];
52 |
53 | for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
54 | var slice = byteCharacters.slice(offset, offset + sliceSize);
55 |
56 | var byteNumbers = new Array(slice.length);
57 | for (var i = 0; i < slice.length; i++) {
58 | byteNumbers[i] = slice.charCodeAt(i);
59 | }
60 |
61 | var byteArray = new Uint8Array(byteNumbers);
62 |
63 | byteArrays.push(byteArray);
64 | }
65 |
66 | var blob = new Blob(byteArrays, {type: contentType});
67 | return blob;
68 | }
69 |
70 | var defs = ' ';
71 | svg.attr({version: '1.1' , xmlns:"http://www.w3.org/2000/svg"});
72 | svg.append(defs);
73 | $(scope.target + ' svg').wrap('
');
74 | var b64 = btoa(unescape(encodeURIComponent($('#sr-capture-container').html())));
75 | var blob = b64toBlob(b64, 'image/svg+xml');
76 | var blobUrl = URL.createObjectURL(blob);
77 | var a = document.createElement('a');
78 | a.style = 'display: none';
79 | a.href = blobUrl;
80 | a.download = scope.filename;
81 | a.click();
82 | URL.revokeObjectURL(blobUrl);
83 | $(scope.target + ' svg').unwrap();
84 | $(scope.target + ' svg defs').remove();
85 | });
86 | }
87 | };
88 | }]);
89 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/cohortSummaryInfo.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=cohortSummaryInfo.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('cohortSummaryInfo', [function() {
6 |
7 | return {
8 | restrict: 'E',
9 | template: ' ',
10 | controller: function($scope, $element) {
11 | var span = $element.children()[0];
12 |
13 | function _showCohortInfo() {
14 | var cohortsSummary = '';
15 |
16 | for(var i = 1; i <= GLOBAL.NumOfSubsets; i++) {
17 | var currentQuery = getQuerySummary(i);
18 | if(currentQuery !== '') {
19 | cohortsSummary += ' Subset ' + (i) + ': ';
20 | cohortsSummary += currentQuery;
21 | cohortsSummary += ' ';
22 | }
23 | }
24 | if (!cohortsSummary) {
25 | cohortsSummary = ' WARNING: No subsets have been selected! Please go to the "Comparison" tab and select your subsets.';
26 | }
27 |
28 | span.innerHTML = cohortsSummary;
29 | }
30 |
31 | // Trigger for update is clicking the SmartR panel item. Maybe there is a more elegant way?
32 | $scope.$evalAsync(function() {
33 | _showCohortInfo(); // set it one time initially
34 | $('#resultsTabPanel__smartRPanel').on('click', function() {
35 | _showCohortInfo();
36 | });
37 | });
38 | }
39 | };
40 |
41 | }]);
42 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/downloadResultsButton.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=downloadResultsButton.js
2 |
3 | window.smartRApp.directive('downloadResultsButton', ['rServeService', function(rServeService)
4 | {
5 | function downloadFile(data) {
6 | var link = jQuery(' ')
7 | .attr('href', rServeService.urlForFile(data.executionId, 'analysis_data.zip'))
8 | .attr('download', 'analysis_data.zip')
9 | .css('display', 'none');
10 | jQuery('body').append(link);
11 | link[0].click();
12 | link.remove();
13 | }
14 |
15 | return {
16 | restrict: 'E',
17 | scope: {
18 | disabled: '='
19 | },
20 | template:
21 | ' ' +
22 | ' ',
23 | link: function(scope, element) {
24 |
25 | var template_btn = element.children()[0];
26 | var template_msg = element.children()[1];
27 |
28 | template_btn.disabled = scope.disabled;
29 |
30 | scope.$watch('disabled', function (newValue) {
31 | template_btn.disabled = newValue;
32 | }, true);
33 |
34 | template_btn.onclick = function() {
35 |
36 | template_msg.innerHTML = 'Download data, please wait _ ';
37 |
38 | rServeService.startScriptExecution({
39 | taskType: 'downloadData',
40 | arguments: {}
41 | }).then(
42 | function (data){
43 | // download file
44 | template_msg.innerHTML = '';
45 | downloadFile(data);
46 | },
47 | function (msg){
48 | template_msg.innerHTML = 'Failure: ' + msg;
49 | }
50 | )
51 | };
52 | }
53 | };
54 | }]);
55 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/ngTranscludeReplace.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=ngTranscludeReplace.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('ngTranscludeReplace', ['$log', function ($log) {
6 | return {
7 | terminal: true,
8 | restrict: 'EA',
9 |
10 | link: function ($scope, $element, $attr, ctrl, transclude) {
11 | if (!transclude) {
12 | $log.error('orphan',
13 | 'Illegal use of ngTranscludeReplace directive in the template! ' +
14 | 'No parent directive that requires a transclusion found. ');
15 | return;
16 | }
17 | transclude(function (clone) {
18 | if (clone.length) {
19 | $element.replaceWith(clone);
20 | }
21 | else {
22 | $element.remove();
23 | }
24 | });
25 | }
26 | };
27 | }]);
28 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/preprocessButton.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=preprocessButton.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('preprocessButton', [
6 | 'rServeService',
7 | '$rootScope',
8 | function(rServeService, $rootScope) {
9 | return {
10 | restrict: 'E',
11 | scope: {
12 | running: '=?',
13 | params: '=?',
14 | showSummaryStats: '=',
15 | summaryData: '=',
16 | allSamples: '=?',
17 | numberOfRows: '=?',
18 | projection: '@?'
19 | },
20 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/preprocessButton.html',
21 | link: function(scope, element) {
22 |
23 | var template_btn = element.children()[0];
24 | var template_msg = element.children()[1];
25 |
26 | var _onSuccess = function() {
27 | template_msg.innerHTML = 'Task complete! Go to the "Run Analysis" tab to continue.';
28 | template_btn.disabled = false;
29 | scope.running = false;
30 | };
31 |
32 | var _onFail = function(msg) {
33 | template_msg.innerHTML = 'Error: ' + msg;
34 | template_btn.disabled = false;
35 | scope.running = false;
36 | };
37 |
38 | // we add this conditional $watch because there is some crazy promise resolving for allSamples
39 | // going on. This is a workaround which observes allSamples and uses it as criteria for successful
40 | // completion. FIXME
41 | scope.$watch('summaryData', function(newValue) {
42 | if (scope.summaryData &&
43 | scope.showSummaryStats &&
44 | scope.running &&
45 | Object.keys(newValue).indexOf('subsets') !== -1) {
46 | scope.allSamples = newValue.allSamples;
47 | scope.numberOfRows = newValue.numberOfRows;
48 | _onSuccess();
49 | }
50 | }, true);
51 |
52 | var _showSummaryStats = function() {
53 | template_msg.innerHTML = 'Execute summary statistics, please wait _ ';
54 | rServeService.executeSummaryStats('preprocess', scope.projection).then(
55 | function (data) {
56 | scope.summaryData = data.result; // this will trigger $watch
57 | },
58 | _onFail
59 | );
60 | };
61 |
62 | template_btn.onclick = function() {
63 | scope.summaryData = {};
64 | scope.disabled = true;
65 | scope.running = true;
66 | template_msg.innerHTML = 'Preprocessing, please wait _ ';
67 |
68 | var params = scope.params ? scope.params : {};
69 | rServeService.preprocess(params).then(
70 | scope.showSummaryStats ? _showSummaryStats : _onSuccess,
71 | _onFail
72 | );
73 | };
74 | }
75 | };
76 | }]);
77 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/runButton.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=runButton.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('runButton', [
6 | '$rootScope',
7 | 'rServeService',
8 | 'smartRUtils',
9 | function($rootScope, rServeService, smartRUtils) {
10 | return {
11 | restrict: 'E',
12 | scope: {
13 | running: '=?',
14 | storage: '=storeResultsIn',
15 | script: '@scriptToRun',
16 | name: '@buttonName',
17 | filename: '@?',
18 | params: '=?argumentsToUse',
19 | waitMessage: '@?'
20 | },
21 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/runButton.html',
22 | link: function(scope, element) {
23 | var params = scope.params ? scope.params : {};
24 | if (!scope.waitMessage) {
25 | scope.waitMessage = 'Creating plot, please wait';
26 | }
27 |
28 | var template_btn = element.children()[0],
29 | template_msg = element.children()[1];
30 |
31 | var _onSuccess = function(data) {
32 | scope.storage = data;
33 | template_msg.innerHTML = '';
34 | scope.disabled = false;
35 | scope.running = false;
36 | };
37 |
38 | var _onFail = function(msg) {
39 | template_msg.innerHTML = 'Error: ' + msg;
40 | scope.disabled = false;
41 | scope.running = false;
42 | };
43 |
44 | var _prepareResults = function(response) {
45 | if (scope.filename) {
46 | // when filename is specified it is assumed that results are serialized
47 | // if results are serialized, we need to deserialized them by
48 | // downloading the results files.
49 | rServeService.downloadJsonFile(response.executionId, scope.filename).then(
50 | function(d) { _onSuccess(d.data); },
51 | _onFail
52 | );
53 | } else {
54 | _onSuccess(JSON.parse(response.result.artifacts.value));
55 | }
56 | };
57 |
58 | template_btn.onclick = function() {
59 | smartRUtils.cleanUp();
60 | scope.disabled = true;
61 | scope.storage = {};
62 | scope.running = true;
63 | template_msg.innerHTML = scope.waitMessage + ' _ ';
64 |
65 | rServeService.startScriptExecution({
66 | taskType: scope.script,
67 | arguments: params
68 | }).then(
69 | _prepareResults,
70 | _onFail
71 | );
72 | };
73 | }
74 | };
75 | }]);
76 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/sortingCriteria.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=sortingCriteria.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('sortingCriteria', [
6 | '$rootScope',
7 | function($rootScope) {
8 | return {
9 | restrict: 'E',
10 | scope: {
11 | criteria : '=',
12 | samples: '=',
13 | subsets: '='
14 | },
15 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/sortingCriteria.html'
16 | };
17 | }
18 | ]);
19 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/summaryStatistics.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=summaryStatistics.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('summaryStats', [
6 | '$rootScope',
7 | function($rootScope) {
8 | return {
9 | restrict: 'E',
10 | scope: {
11 | summaryData: '='
12 | },
13 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/summaryStatistics.html'
14 | };
15 | }
16 | ]);
17 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/tabContainer.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=tabContainer.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('tabContainer',
6 | ['$rootScope', 'smartRUtils', '$timeout', function($rootScope, smartRUtils, $timeout) {
7 | return {
8 | restrict: 'E',
9 | transclude: true,
10 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/tabContainer.html',
11 | controller: function($scope) {
12 | $scope.tabs = [];
13 | this.addTab = function(tab) {
14 | $scope.tabs.push(tab);
15 | };
16 | },
17 | link: function() {
18 | $timeout(function() { // init jQuery UI tabs after DOM has rendered
19 | $('#heim-tabs').tabs();
20 | });
21 | }
22 | };
23 | }]);
24 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/workflowControls.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=workflowControls.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('workflowControls', [
6 | '$rootScope',
7 | 'smartRUtils',
8 | function($rootScope, smartRUtils) {
9 | return {
10 | restrict: 'E',
11 | transclude: true,
12 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/workflowControls.html',
13 | link: function(scope, element) {
14 | var controls = element.children()[0];
15 | var scrollbarWidth = smartRUtils.getScrollBarWidth();
16 | controls.style.bottom = scrollbarWidth + 'px';
17 | controls.style.right = scrollbarWidth + 105 + 'px';
18 | }
19 | };
20 | }
21 | ]);
22 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/workflowTab.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=workflowTab.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('workflowTab', ['smartRUtils', function(smartRUtils) {
6 | return {
7 | restrict: 'E',
8 | scope: {
9 | name: '@tabName',
10 | disabled: '='
11 | },
12 | require: '^tabContainer',
13 | transclude: true,
14 | template: ' ',
15 | link: function(scope, element, attrs, tabContainerCtrl) {
16 | var id = 'fragment-' + smartRUtils.makeSafeForCSS(scope.name);
17 | scope.id = id;
18 | element[0].id = id;
19 | tabContainerCtrl.addTab(scope);
20 | }
21 | };
22 | }]);
23 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/directives/workflowWarnings.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=workflowWarnings.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('workflowWarnings', [
6 | '$rootScope',
7 | function($rootScope) {
8 | return {
9 | restrict: 'E',
10 | scope: {
11 | warnings: '='
12 | },
13 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/workflowWarnings.html',
14 | link: function(scope) {
15 | scope.$watch('warnings', function() {
16 | scope.visibility = $.isEmptyObject(scope.warnings) ? 'hidden' : 'visible';
17 | scope.text = '';
18 | for (var warn in scope.warnings) {
19 | if (scope.warnings.hasOwnProperty(warn)) {
20 | scope.text += scope.warnings[warn] + '\n';
21 | }
22 | }
23 | }, true);
24 | }
25 | };
26 | }
27 | ]);
28 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/services/commonWorkflowService.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=commonWorkflowService.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.factory('commonWorkflowService', ['rServeService', '$css', function(rServeService, $css) {
6 |
7 | var service = {};
8 |
9 | service.initializeWorkflow = function(workflowName, scope) {
10 | service.currentScope = scope;
11 | // load workflow specific css
12 | $css.bind({
13 | href: scope.smartRPath + '/css/' + workflowName + '.css'
14 | }, scope);
15 |
16 | rServeService.destroyAndStartSession(workflowName);
17 | };
18 |
19 | return service;
20 |
21 | }]);
22 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/smartRApp.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=smartRApp.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp = angular.module('smartRApp', ['ngRoute', 'door3.css', 'ipaApi'])
6 | .config(['$httpProvider', function($httpProvider) {
7 | //initialize get if not there
8 | if (!$httpProvider.defaults.headers.get) {
9 | $httpProvider.defaults.headers.get = {};
10 | }
11 | //disable IE ajax request caching
12 | $httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
13 | }])
14 | .run(function($rootScope, $http) {
15 | // get plugin context path and put it in root scope
16 | $http.get(pageInfo.basePath + '/SmartR/smartRContextPath').then(
17 | function(d) { $rootScope.smartRPath = d.data; },
18 | function(msg) { throw 'Error: ' + msg; }
19 | );
20 | });
21 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/biomarkerSelection.html:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/boxplot.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/conceptBox.html:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/fetchButton.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/heatmap.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Animate
5 |
6 |
14 |
31 |
32 |
33 |
34 |
35 | Red to Green Schema
36 | Red to Blue Schema
37 | Blue Schema
38 | Green Schema
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/ipaApi.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | Analysis name in IPA: {{ ipaState.analysisName }}
9 |
10 |
11 |
12 |
Limma
13 |
Canonical Pathways
14 |
Upstream Regulators
15 |
Tox Functions
16 |
Networks
17 |
Diseases and Bio Functions
18 |
Analysis Ready Molecules
19 |
20 |
25 |
26 |
27 |
31 |
32 |
33 |
37 |
38 |
39 |
43 |
44 |
45 |
49 |
50 |
51 |
55 |
56 |
57 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/linegraph.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | No. of patients to show
4 |
5 |
6 |
7 |
8 | User weighted events
9 |
10 |
11 |
12 | Smooth Graph
13 |
14 |
15 |
16 | Evenly space timepoints
17 |
18 |
19 |
20 | Mean with SD
21 | Median with SD
22 | Mean with SEM
23 | Median with SEM
24 | Show individuals
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/preprocessButton.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/runButton.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/sortingCriteria.html:
--------------------------------------------------------------------------------
1 | Ranking Criteria:
2 |
26 |
46 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/summaryStatistics.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Plot
5 | Labels
6 | Subset 1
7 | Subset 2
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
21 |
22 |
23 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/tabContainer.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/workflowControls.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/templates/workflowWarnings.html:
--------------------------------------------------------------------------------
1 |
2 | {{text}}
3 |
4 |
--------------------------------------------------------------------------------
/web-app/js/smartR/_angular/viz/plotlyBoxplot.js:
--------------------------------------------------------------------------------
1 | //# sourceURL=plotlyBoxplot.js
2 |
3 | 'use strict';
4 |
5 | window.smartRApp.directive('boxplot', [
6 | 'smartRUtils',
7 | 'rServeService',
8 | '$rootScope',
9 | function(smartRUtils, rServeService, $rootScope) {
10 |
11 | return {
12 | restrict: 'E',
13 | scope: {
14 | data: '='
15 | },
16 | templateUrl: $rootScope.smartRPath + '/js/smartR/_angular/templates/boxplot.html',
17 | link: function (scope, element) {
18 | var vizDiv = element.children()[0];
19 | /**
20 | * Watch data model (which is only changed by ajax calls when we want to (re)draw everything)
21 | */
22 | scope.$watch('data', function () {
23 | $(vizDiv).empty();
24 | if (! $.isEmptyObject(scope.data)) {
25 | createBoxplot(scope, vizDiv);
26 | }
27 | });
28 | }
29 | };
30 |
31 | function createBoxplot(scope, vizDiv) {
32 | var cf = crossfilter(scope.data.dataMatrix);
33 | var byValue = cf.dimension(function(d) { return d.value; });
34 | var byBioMarker = cf.dimension(function(d) { return d.bioMarker; });
35 |
36 | var plotData = [];
37 | smartRUtils.unique(smartRUtils.getValuesForDimension(byBioMarker)).forEach(function(bioMarker) {
38 | byBioMarker.filterExact(bioMarker);
39 | plotData.push({
40 | type: 'box',
41 | y: smartRUtils.getValuesForDimension(byValue),
42 | name: bioMarker,
43 | boxpoints: 'all',
44 | boxmean: 'sd',
45 | jitter: 0.5
46 | });
47 | byBioMarker.filterAll();
48 | });
49 |
50 | var title = 'Boxplots (' + scope.data.transformation + ')';
51 | title += scope.data.pValue ? ' ANOVA pValue = ' + scope.data.pValue : '';
52 | var layout = {
53 | title: title,
54 | height: 800
55 | };
56 | Plotly.newPlot(vizDiv, plotData, layout);
57 | }
58 | }]);
59 |
60 |
--------------------------------------------------------------------------------
/web-app/js/smartR/smartR.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Avoid `console` errors in browsers that lack a console.
4 | (function() {
5 | var method;
6 | var noop = function () {};
7 | var methods = [
8 | 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
9 | 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
10 | 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
11 | 'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
12 | ];
13 | var length = methods.length;
14 | console = (window.console = window.console || {}); // jshint ignore:line
15 |
16 | while (length--) {
17 | method = methods[length];
18 |
19 | // Only stub undefined methods.
20 | if (!console[method]) {
21 | console[method] = noop;
22 | }
23 | }
24 | }());
25 |
26 | window.smartRPanel = new Ext.Panel({
27 | id: 'smartRPanel',
28 | title: 'SmartR',
29 | region: 'center',
30 | split: true,
31 | height: 90,
32 | layout: 'fit',
33 | collapsible: true,
34 | autoScroll: true,
35 | tbar: new Ext.Toolbar({
36 | id: 'smartRToolbar',
37 | title: 'R Scripts',
38 | items: []
39 | }),
40 | listeners: {
41 | render: function (panel) {
42 | /**
43 | * WORKAROUND : code below is needed to reorder the javascript script load that're broken due to
44 | * ExtJS panel
45 | */
46 | // start workaround
47 | var updater = panel.getUpdater();
48 | updater.on('update', function() {
49 | var panelBody = jQuery(arguments[0].dom);
50 | var scripts = panelBody.children('script');
51 | scripts.remove(); // remove scripts from panel body
52 | panelBody.append(scripts); // re-append again
53 | });
54 | updater.update({
55 | url: pageInfo.basePath + '/smartR/index',
56 | method: 'POST',
57 | scripts: false });
58 | // end workaround
59 | }
60 | }
61 | });
62 |
63 | window.addSmartRPanel = function addSmartRPanel(parentPanel) {
64 | parentPanel.insert(4, window.smartRPanel);
65 | };
66 |
67 | function cleanUpSmartR() {
68 | var d3tips = document.getElementsByClassName('d3-tip');
69 | Array.prototype.forEach.call(d3tips, function(el) { // d3tips is array-like object
70 | el.parentNode.removeChild(el);
71 | });
72 | }
73 | cleanUpSmartR();
74 |
75 |
--------------------------------------------------------------------------------