diff --git a/config/build-utils.js b/config/build-utils.js
index 2a889f550..6e94e92ff 100644
--- a/config/build-utils.js
+++ b/config/build-utils.js
@@ -3,8 +3,11 @@ const path = require('path');
const fs = require('fs');
const helpers = require('./helpers');
+const APP_COMMON_CONFIG = require('./config.common.json');
+
const DEFAULT_METADATA = {
- title: 'Wall of Targets',
+ title: APP_COMMON_CONFIG.title,
+ description: APP_COMMON_CONFIG.description,
baseUrl: '/',
isDevServer: helpers.isWebpackDevServer(),
HMR: helpers.hasProcessFlag('hot'),
diff --git a/config/config.common.json b/config/config.common.json
new file mode 100644
index 000000000..24a178546
--- /dev/null
+++ b/config/config.common.json
@@ -0,0 +1,4 @@
+{
+ "title": "Wall of Targets",
+ "description": "Wall of Targets is an application to aggregate genomic evidence supporting candidate gene and module targets nominated by members of the AMP-AD consortium."
+}
\ No newline at end of file
diff --git a/config/config.dev.json b/config/config.dev.json
new file mode 100644
index 000000000..691531f96
--- /dev/null
+++ b/config/config.dev.json
@@ -0,0 +1,10 @@
+{
+ "firebase": {
+ "apiKey": "AIzaSyBMS96wgJfydRf7BLDVh4DGtRKAZT8UpTM",
+ "authDomain": "wall-of-targets.firebaseapp.com",
+ "databaseURL": "https://wall-of-targets.firebaseio.com",
+ "projectId": "wall-of-targets",
+ "storageBucket": "wall-of-targets.appspot.com",
+ "messagingSenderId": "256222676709"
+ }
+}
\ No newline at end of file
diff --git a/config/config.prod.json b/config/config.prod.json
new file mode 100644
index 000000000..86edfcdd2
--- /dev/null
+++ b/config/config.prod.json
@@ -0,0 +1,11 @@
+{
+ "firebase": {
+ "apiKey": "AIzaSyBMS96wgJfydRf7BLDVh4DGtRKAZT8UpTM",
+ "authDomain": "wall-of-targets.firebaseapp.com",
+ "databaseURL": "https://wall-of-targets.firebaseio.com",
+ "projectId": "wall-of-targets",
+ "storageBucket": "wall-of-targets.appspot.com",
+ "messagingSenderId": "256222676709"
+ },
+ "gtmKey" : "GTM-XXXXXXX"
+}
\ No newline at end of file
diff --git a/config/karma.conf.js b/config/karma.conf.js
index 46799a5c9..f98787bb1 100644
--- a/config/karma.conf.js
+++ b/config/karma.conf.js
@@ -1,11 +1,11 @@
/**
- * @author: @AngularClass
+ * @author: tipe.io
*/
module.exports = function (config) {
- var testWebpackConfig = require('./webpack.test.js')({ env: 'test' });
+ const testWebpackConfig = require('./webpack.test.js')({ env: 'test' });
- var configuration = {
+ const configuration = {
/**
* Base path that will be used to resolve all patterns (e.g. files, exclude).
@@ -74,7 +74,7 @@ module.exports = function (config) {
* webpack-dev-middleware configuration
* i.e.
*/
- noInfo: true,
+ logLevel: 'warn',
/**
* and use stats to turn off verbose output
*/
@@ -120,13 +120,14 @@ module.exports = function (config) {
* available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
*/
browsers: [
- 'Chrome'
+ 'Chrome',
+ 'ChromeTravisCi'
],
customLaunchers: {
ChromeTravisCi: {
- base: 'Chrome',
- flags: ['--no-sandbox']
+ base: 'ChromeHeadless',
+ flags: ['--no-sandbox', '--disable-gpu']
}
},
@@ -134,9 +135,35 @@ module.exports = function (config) {
* Continuous Integration mode
* if true, Karma captures browsers, runs the tests and exits
*/
- singleRun: true
+ singleRun: true,
+ /**
+ * For slower machines you may need to have a longer browser
+ * wait time . Uncomment the line below if required.
+ */
+ // browserNoActivityTimeout: 30000
+
};
+ // Optional Sonar Qube Reporter
+ if (process.env.SONAR_QUBE) {
+
+ // SonarQube reporter plugin configuration
+ configuration.sonarQubeUnitReporter = {
+ sonarQubeVersion: '5.x',
+ outputFile: 'reports/ut_report.xml',
+ overrideTestDescription: true,
+ testPath: 'src/app',
+ testFilePattern: '.spec.ts',
+ useBrowserName: false
+ };
+
+ // Additional lcov format required for
+ // sonarqube
+ configuration.remapCoverageReporter.lcovonly = './coverage/coverage.lcov';
+
+ configuration.reporters.push('sonarqubeUnit');
+ }
+
if (process.env.TRAVIS) {
configuration.browsers = [
'ChromeTravisCi'
@@ -144,4 +171,4 @@ module.exports = function (config) {
}
config.set(configuration);
-};
+};
\ No newline at end of file
diff --git a/config/nginx-custom.conf b/config/nginx-custom.conf
new file mode 100644
index 000000000..ca0ed1021
--- /dev/null
+++ b/config/nginx-custom.conf
@@ -0,0 +1,19 @@
+server {
+ listen 80;
+
+ gzip on;
+ gzip_http_version 1.1;
+ gzip_disable "MSIE [1-6]\.";
+ gzip_min_length 1100;
+ gzip_vary on;
+ gzip_proxied expired no-cache no-store private auth;
+ gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
+ gzip_comp_level 5;
+
+ root /usr/share/nginx/html;
+
+ location / {
+ index index.html index.htm;
+ try_files $uri $uri/ /index.html =404;
+ }
+}
\ No newline at end of file
diff --git a/config/protractor.conf.js b/config/protractor.conf.js
index acaa95cb4..acca1ea4d 100644
--- a/config/protractor.conf.js
+++ b/config/protractor.conf.js
@@ -1,5 +1,5 @@
/**
- * @author: @AngularClass
+ * @author: tipe.io
*/
require('ts-node/register');
@@ -17,24 +17,23 @@ exports.config = {
],
exclude: [],
- framework: 'jasmine2',
+ framework: 'jasmine',
- allScriptsTimeout: 110000,
+ allScriptsTimeout: 11000,
jasmineNodeOpts: {
showTiming: true,
showColors: true,
isVerbose: false,
includeStackTrace: false,
- defaultTimeoutInterval: 400000
+ defaultTimeoutInterval: 40000
},
- directConnect: true,
+ directConnect: true,
capabilities: {
- 'browserName': 'chrome',
- 'chromeOptions': {
- //'args': ["--headless", "--disable-gpu", "--window-size=1280x800", "--no-sandbox"]
- 'args': ['show-fps-counter=true']
+ browserName: 'chrome',
+ chromeOptions: {
+ args: [ "--headless", "--disable-gpu", "--window-size=800x600", "--no-sandbox" ]
}
},
@@ -51,4 +50,4 @@ exports.config = {
useAllAngular2AppRoots: true,
SELENIUM_PROMISE_MANAGER: false,
-};
+};
\ No newline at end of file
diff --git a/config/spec-bundle.js b/config/spec-bundle.js
index d4ca968dd..c814277e4 100644
--- a/config/spec-bundle.js
+++ b/config/spec-bundle.js
@@ -1,5 +1,5 @@
/**
- * @author: @AngularClass
+ * @author: tipe.io
*/
/**
@@ -60,4 +60,4 @@ function requireAll(requireContext) {
/**
* Requires and returns all modules that match
*/
-var modules = requireAll(testContext);
+var modules = requireAll(testContext);
\ No newline at end of file
diff --git a/config/webpack.common.js b/config/webpack.common.js
index 881d60fcb..818136ab3 100644
--- a/config/webpack.common.js
+++ b/config/webpack.common.js
@@ -17,6 +17,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin');
const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
+const ProvidePlugin = require('webpack/lib/ProvidePlugin');
const ngcWebpack = require('ngc-webpack');
const buildUtils = require('./build-utils');
@@ -28,20 +29,24 @@ const buildUtils = require('./build-utils');
* See: http://webpack.github.io/docs/configuration.html#cli
*/
module.exports = function (options) {
- const isProd = options.env === 'production';
- const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA, options.metadata || {});
- const ngcWebpackConfig = buildUtils.ngcWebpackSetup(isProd, METADATA);
- const supportES2015 = buildUtils.supportES2015(METADATA.tsConfigPath);
-
- const entry = {
- polyfills: './src/polyfills.browser.ts',
- main: './src/main.browser.ts'
- };
+ const isProd = options.env === 'production';
+ const APP_CONFIG = require(process.env.ANGULAR_CONF_FILE || (isProd ? './config.prod.json' : './config.dev.json'));
+
+ const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA,options.metadata || {});
+ const GTM_API_KEY = process.env.GTM_API_KEY || APP_CONFIG.gtmKey;
+
+ const ngcWebpackConfig = buildUtils.ngcWebpackSetup(isProd, METADATA);
+ const supportES2015 = buildUtils.supportES2015(METADATA.tsConfigPath);
- Object.assign(ngcWebpackConfig.plugin, {
- tsConfigPath: METADATA.tsConfigPath,
- mainPath: entry.main
- });
+ const entry = {
+ polyfills: './src/polyfills.browser.ts',
+ main: './src/main.browser.ts'
+ };
+
+ Object.assign(ngcWebpackConfig.plugin, {
+ tsConfigPath: METADATA.tsConfigPath,
+ mainPath: entry.main
+ });
return {
/**
@@ -179,7 +184,8 @@ module.exports = function (options) {
'AOT': METADATA.AOT,
'process.env.ENV': JSON.stringify(METADATA.ENV),
'process.env.NODE_ENV': JSON.stringify(METADATA.ENV),
- 'process.env.HMR': METADATA.HMR
+ 'process.env.HMR': METADATA.HMR,
+ 'FIREBASE_CONFIG': JSON.stringify(APP_CONFIG.firebase)
}),
/**
@@ -303,6 +309,10 @@ module.exports = function (options) {
* https://github.com/szrenwei/inline-manifest-webpack-plugin
*/
new InlineManifestWebpackPlugin(),
+
+ new ProvidePlugin({
+ 'dc': 'dc'
+ })
],
/**
@@ -312,14 +322,12 @@ module.exports = function (options) {
* See: https://webpack.github.io/docs/configuration.html#node
*/
node: {
- global: true,
- crypto: 'empty',
- process: true,
- module: false,
- clearImmediate: false,
- setImmediate: false,
- fs: 'empty',
- readline: 'empty'
+ global: true,
+ crypto: 'empty',
+ process: true,
+ module: false,
+ clearImmediate: false,
+ setImmediate: false
}
};
diff --git a/src/app/charts/charts.module.ts b/src/app/charts/charts.module.ts
index 794aeaf7a..5858e7247 100644
--- a/src/app/charts/charts.module.ts
+++ b/src/app/charts/charts.module.ts
@@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
import { AppSharedModule } from '../shared';
import { ScatterPlotViewComponent } from './scatter-plot/scatter-plot-view';
-import { LineChartViewComponent } from './line-chart/line-chart-view';
import { SelectMenuViewComponent } from './select-menu/select-menu-view';
import { RowChartViewComponent } from './row-chart/row-chart-view';
@@ -22,7 +21,6 @@ import {
@NgModule({
declarations: [
ScatterPlotViewComponent,
- LineChartViewComponent,
SelectMenuViewComponent,
RowChartViewComponent
],
@@ -41,7 +39,6 @@ import {
],
exports: [
ScatterPlotViewComponent,
- LineChartViewComponent,
SelectMenuViewComponent,
RowChartViewComponent
]
diff --git a/src/app/charts/line-chart/line-chart-view/index.ts b/src/app/charts/line-chart/line-chart-view/index.ts
deleted file mode 100644
index 186956419..000000000
--- a/src/app/charts/line-chart/line-chart-view/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './line-chart-view.component';
diff --git a/src/app/charts/line-chart/line-chart-view/line-chart-view.component.html b/src/app/charts/line-chart/line-chart-view/line-chart-view.component.html
deleted file mode 100644
index f239521ac..000000000
--- a/src/app/charts/line-chart/line-chart-view/line-chart-view.component.html
+++ /dev/null
@@ -1 +0,0 @@
-
Nested
diff --git a/src/app/charts/line-chart/line-chart-view/line-chart-view.component.scss b/src/app/charts/line-chart/line-chart-view/line-chart-view.component.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/app/charts/line-chart/line-chart-view/line-chart-view.component.ts b/src/app/charts/line-chart/line-chart-view/line-chart-view.component.ts
deleted file mode 100644
index 41ac583bf..000000000
--- a/src/app/charts/line-chart/line-chart-view/line-chart-view.component.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Component, OnInit, ViewEncapsulation } from '@angular/core';
-
-import {
- Router,
- ActivatedRoute
-} from '@angular/router';
-
-@Component({
- selector: 'line-chart',
- templateUrl: './line-chart-view.component.html',
- styleUrls: [ './line-chart-view.component.scss' ]
-})
-export class LineChartViewComponent implements OnInit {
-
- constructor(
- private router : Router,
- private route: ActivatedRoute
- ) { }
-
- ngOnInit() {
- }
-
- goToRoute(path: string, outlets?: any) {
- (outlets) ? this.router.navigate([path, outlets], {relativeTo: this.route}) : this.router.navigate([path], {relativeTo: this.route});
- }
-}
diff --git a/src/app/charts/row-chart/row-chart-view/row-chart-view.component.html b/src/app/charts/row-chart/row-chart-view/row-chart-view.component.html
index 954127a60..2a18d0fd5 100644
--- a/src/app/charts/row-chart/row-chart-view/row-chart-view.component.html
+++ b/src/app/charts/row-chart/row-chart-view/row-chart-view.component.html
@@ -8,7 +8,7 @@
diff --git a/src/app/charts/row-chart/row-chart-view/row-chart-view.component.ts b/src/app/charts/row-chart/row-chart-view/row-chart-view.component.ts
index 292cc152f..d957ec266 100644
--- a/src/app/charts/row-chart/row-chart-view/row-chart-view.component.ts
+++ b/src/app/charts/row-chart/row-chart-view/row-chart-view.component.ts
@@ -1,4 +1,5 @@
import { Component, OnInit, ViewEncapsulation, ViewChild, ElementRef, Input } from '@angular/core';
+import { DecimalPipe } from '@angular/common';
import {
ActivatedRoute
@@ -14,7 +15,7 @@ import {
import * as d3 from 'd3';
import * as dc from 'dc';
-// Using a d3 v4 function to get all node
+// Using a d3 v4 function to get all nodes
d3.selection.prototype['nodes'] = function(){
var nodes = new Array(this.size()), i = -1;
this.each(function() { nodes[++i] = this; });
@@ -37,11 +38,13 @@ export class RowChartViewComponent implements OnInit {
@ViewChild('studies') stdCol: ElementRef;
changedLabels: boolean = false;
+ display: boolean = false;
constructor(
private route: ActivatedRoute,
private geneService: GeneService,
- private chartService: ChartService
+ private chartService: ChartService,
+ private decimalPipe: DecimalPipe
) { }
ngOnInit() {
@@ -64,7 +67,7 @@ export class RowChartViewComponent implements OnInit {
.elasticX(true)
.gap(4)
.title(function(d) {
- return d.value.logFC || 0;
+ return 'Log Fold Change: ' + (self.decimalPipe.transform(+d.value.logFC) || 0);
})
.valueAccessor(function(d) {
return d.value.logFC || 0;
@@ -77,74 +80,126 @@ export class RowChartViewComponent implements OnInit {
.dimension(this.chartService.getDimension(this.label))
.group(this.chartService.getGroup(this.label));
+ // Add this number of ticks so the x axis don't get cluttered with text
this.chart.xAxis().ticks(5);
- this.chart.on('filtered', function(chart, filter){
- console.log(chart, filter);
- });
+ // Register the row chart renderlet
+ this.registerChartEvent(this.chart);
- this.chart.on('renderlet', function (chart) {
- let barHeight = chart.select('g.row rect').attr('height');
- let newSvg = d3.select(self.stdCol.nativeElement).append('svg');
- let textGroup = newSvg.append('g')
- .attr('class', 'textGroup');
-
- let allText = chart.selectAll('g.row text');
- let removed = allText.remove();
-
- // Copy the texts to another div, so they show up
- if (!self.changedLabels) {
- let stdColHeight = chart.height();
- let step = chart.select('g.axis g.tick line.grid-line').node().getBBox().height / (removed.nodes().length);
- removed.nodes().forEach(n => {
- textGroup.append(function() {
- return n;
+ this.chart.render();
+ }
+
+ // A custom renderlet function for this chart, allows us to change
+ // what happens to the chart after rendering
+ registerChartEvent(chart: dc.RowChart, type: string = 'renderlet') {
+ let self = this;
+ this.chart.on(type, function (chart) {
+ let rectHeight = chart.select('g.row rect').attr('height');
+ let squareSize = 10;
+ let lineWidth = 60;
+
+ // Test if we should display the chart. Using this variable so we don't see
+ // the rows rectangles change into small squares abruptly
+ if (!self.display) {
+ // Copy all vertical texts to another div, so they don't get hidden by
+ // the row chart svg after being translated
+ self.moveTextToElement(chart, self.stdCol.nativeElement, squareSize/2);
+
+ // Insert a line for each row of the chart
+ self.insertLinesInRows(chart);
+
+ // Draw the inserted lines in each row
+ self.drawLines(chart, rectHeight/2, lineWidth);
+ } else {
+ // This part will be called on redraw after filtering, so at this point we just need
+ // to move the lines to the correct position again. First translate the parent element
+ let hlines = chart.selectAll('g.row g.hline');
+ hlines.each(function(d, i) {
+ d3.select(this).attr('transform', function(d) {
+ return 'translate('+d.value.logFC+')';
})
- })
- d3.select(self.stdCol.nativeElement).selectAll('g.textGroup text').each(function(d, i) {
- let currentStep = step * i;
- // your update code here as it was in your example
- d3.select(this).attr('transform', function () {
- return 'translate(0,' + (currentStep+5) + ')';
- });
});
- // Draw the lines through the squares
- let bar = chart.selectAll('g.row')
- .insert('g', ':first-child')
- .attr('class', 'hline');
- bar
- .insert('line')
- .attr({
- 'stroke-width': 1.5,
- stroke: 'wheat',
- x1: function(d) {
- return chart.x()(d.value.logFC) - 30;
- },
- y1: function(d) {
- return barHeight/2;
- },
- x2: function(d) {
- return chart.x()(d.value.logFC) + 40;
- },
- y2: function(d) {
- return barHeight/2;
- }
- });
-
- self.changedLabels = true;
+ // Finally redraw the lines in each row
+ self.drawLines(chart, rectHeight/2, lineWidth);
}
- // Change the rectangles to small ones
- chart
- .selectAll('g.row rect')
- .attr('transform', function(d) {
- return 'translate(' + chart.x()(d.value.logFC) + ',' + ((barHeight/2)-5) + ')';
- })
- .attr('width', '10')
- .attr('height', '10');
+ // Change the row rectangles into small squares, this happens on
+ // every render or redraw
+ self.rectToSquares(chart, squareSize, rectHeight);
+
+ // Finally show the chart
+ self.display = true;
});
+ }
- this.chart.render();
+ // Moves all text in textGroups to a new HTML element
+ moveTextToElement(chart: dc.RowChart, el: HTMLElement, vSpacing: number = 0) {
+ let newSvg = d3.select(el).append('svg');
+ let textGroup = newSvg.append('g')
+ .attr('class', 'textGroup');
+
+ // Remove the old texts and append to the new group
+ let allText = chart.selectAll('g.row text');
+ let removed = allText.remove();
+ removed['nodes']().forEach(n => {
+ textGroup.append(function() {
+ return n;
+ })
+ });
+
+ // Move the text to the correct position in the new svg
+ let stdColHeight = chart.height();
+ let step = (chart.select('g.axis g.tick line.grid-line').node() as SVGGraphicsElement).getBBox().height / (removed['nodes']().length);
+
+ d3.select(el).selectAll('g.textGroup text').each(function(d, i) {
+ let currentStep = step * i;
+ d3.select(this).attr('transform', function () {
+ return 'translate(0,' + (currentStep+(vSpacing)) + ')';
+ });
+ });
+ }
+
+ insertLinesInRows(chart: dc.RowChart) {
+ chart.selectAll('g.row')
+ .insert('g', ':first-child')
+ .attr('class', 'hline')
+ .insert('line');
+ }
+
+ // Draw the lines through the chart rows
+ drawLines(chart: dc.RowChart, yPos: number, lineWidth: number) {
+ let lines = chart.selectAll('g.row g.hline line')
+ .attr({
+ 'stroke-width': 1.5,
+ stroke: 'wheat',
+ x1: function(d) {
+ return chart.x()(d.value.logFC) - lineWidth/2;
+ },
+ y1: function(d) {
+ return yPos;
+ },
+ x2: function(d) {
+ return chart.x()(d.value.logFC) + lineWidth/2;
+ },
+ y2: function(d) {
+ return yPos;
+ }
+ });
+ }
+
+ // Changes the chart row rects into squares of the square size
+ rectToSquares(chart: dc.RowChart, squareSize: number, rectHeight: number) {
+ chart
+ .selectAll('g.row rect')
+ .attr('transform', function(d) {
+ return 'translate(' + (chart.x()(d.value.logFC)-(squareSize/2)) + ',' + ((rectHeight/2)-(squareSize/2)) + ')';
+ })
+ .attr('width', squareSize)
+ .attr('height', squareSize);
+ }
+
+ displayChart() {
+ return {'opacity': (this.display) ? 1 : 0};
}
}
diff --git a/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.scss b/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.scss
index 96ef71a53..5c917e383 100644
--- a/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.scss
+++ b/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.scss
@@ -28,7 +28,3 @@
.dc-chart {
height: 100%;
}
-
-.sc.dc-chart {
- width: 100%;
-}
diff --git a/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.ts b/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.ts
index c6c306130..5f37506c2 100644
--- a/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.ts
+++ b/src/app/charts/scatter-plot/scatter-plot-view/scatter-plot-view.component.ts
@@ -1,4 +1,5 @@
import { Component, OnInit, ViewEncapsulation, ViewChild, ElementRef, Input, ContentChild, AfterContentInit } from '@angular/core';
+import { DecimalPipe } from '@angular/common';
import {
ActivatedRoute
@@ -7,14 +8,14 @@ import {
import { Gene } from '../../../models';
import {
- ChartService,
- ColorService
+ ChartService
} from '../../../core/services';
import { GeneService } from '../../../core/services';
import * as d3 from 'd3';
import * as dc from 'dc';
+import '../../../../scripts/dc-canvas-scatterplot.js';
@Component({
selector: 'scatter-plot',
@@ -29,6 +30,8 @@ export class ScatterPlotViewComponent implements OnInit, AfterContentInit {
@Input() label: string;
@ViewChild('chart') scatterPlot: ElementRef;
+ subChart: any;
+ svgAdded: boolean = false;
private dim: CrossFilter.Dimension;
private group: CrossFilter.Group;
@@ -36,8 +39,8 @@ export class ScatterPlotViewComponent implements OnInit, AfterContentInit {
constructor(
private route: ActivatedRoute,
private chartService: ChartService,
- private colorService: ColorService,
- private geneService: GeneService
+ private geneService: GeneService,
+ private decimalPipe: DecimalPipe
) { }
ngOnInit() {}
@@ -62,38 +65,110 @@ export class ScatterPlotViewComponent implements OnInit, AfterContentInit {
this.dim = this.chartService.getDimension(this.label);
this.group = this.chartService.getGroup(this.label);
- this.chart = dc.scatterPlot(this.scatterPlot.nativeElement)
- .x(d3.scale.linear().domain([this.geneService.minLogFC, this.geneService.maxLogFC]))
- .y(d3.scale.linear().domain([0, this.geneService.maxAdjPVal]))
+ let currentGene = this.geneService.getCurrentGene();
+ // Create a symbol scale based on d3 types, then make the accessor
+ // return two different types
+ let symbolScale = d3.scale.ordinal().range(d3.svg.symbolTypes);
+ let symbolAccessor = function(d) {
+ return symbolScale(
+ (d.key[2] === currentGene.hgnc_symbol) ? '1' : '0'
+ )
+ };
+ // Add the scatter plot as a sub chart of a series chartso we can render two
+ // series of genes, the selected and the non selected ones
+ this.subChart = function(c) {
+ return dc.scatterPlot(c)
+ //['useCanvas'](true)
+ .symbol(symbolAccessor)
+ .symbolSize(5)
+ .highlightedSize(10)
+ .renderTitle(true)
+ .brushOn(false)
+ .title(function (p) {
+ return [
+ 'Log Fold Change: ' + self.decimalPipe.transform(+p.key[0]),
+ '-log10(Adjusted p-value): ' + self.decimalPipe.transform(+p.key[1])
+ ].join('\n');
+ });
+ };
+ this.chart = dc.seriesChart(this.scatterPlot.nativeElement)
+ .x(d3.scale.linear().domain([this.geneService.minLogFC*1.1, this.geneService.maxLogFC*1.1]))
+ .chart(this.subChart)
.brushOn(false)
.xAxisLabel(this.info.xAxisLabel)
.yAxisLabel(this.info.yAxisLabel)
+ .clipPadding(10)
.dimension(this.dim)
.group(this.group)
- //.linearColors(['#b30000', '#fdd49e'])
- .linearColors(['#000000', '#bbbbbb'])
- .colorAccessor(function(d) {
- if (Number.isNaN(+d.key[0]) || Number.isNaN(+d.key[1])) return 0;
- let a = 0 - +d.key[0];
- let b = 0 - +d.key[1];
- //return Math.sqrt(a*a + (b*b));
- return Math.abs(a);
- });
+ //.elasticY(true)
+ //.mouseZoomable(true)
+ .shareTitle(false)
+ // Using this notation because the typings for dc do not show this method for this chart
+ ['seriesAccessor'](function(d) {
+ return (d.key[2] === currentGene.hgnc_symbol) ? '1' : '0';
+ })
+ .keyAccessor(function(d) {return +d.key[0];})
+ .valueAccessor(function(d) { return +d.key[1]; });
+
+ // Separate this call so we can get the correct chart reference below
+ this.chart.yAxis().tickFormat(function(d) { return d3.format(',d')(d); });
+
+ // Register the scatter plot pretransition event
+ this.registerChartEvent(this.chart, 'pretransition');
+ this.chart.render();
+ }
- if (this.info.xUnits) this.chart.xUnits(this.info.xUnits);
+ registerChartEvent(chart: dc.SeriesChart, type: string = 'renderlet') {
+ let self = this;
+ chart.on(type, function (chart) {
+ if (!self.svgAdded) {
+ let blackGenes = chart.selectAll('g.sub._0 path.symbol');
+ let redGenes = chart.selectAll('g.sub._1 path.symbol');
+
+ // Make the selection render for last
+ self.moveToFront(redGenes);
+
+ // Add a black and white gradient to the non selected genes
+ let svg = d3.select(self.scatterPlot.nativeElement).select('svg');
+ self.addGradientToSVG(blackGenes, svg, 'black-gradient', [
+ {
+ 'offset': '0%', 'stop-color': 'white'
+ }, {
+ 'offset': '60%', 'stop-color': 'black'
+ }
+ ]);
+
+ // Add a red color tothe selected genes
+ redGenes
+ .style('stroke', 'white')
+ .style('fill', 'red')
+ .style('stroke-width', 0.5);
+
+ self.svgAdded = true;
+ }
+ });
+ }
- this.chart.render();
+ // Make the selection the last in the parent order
+ moveToFront(sel: d3.Selection) {
+ sel.each(function() {
+ this.parentNode.appendChild(this);
+ });
}
- getValue (d: any) {
- let value: number
- if (!(isNaN(d.value))) {
- value = d.value;
- } else if (!d.value) {
- value = 0;
- }
+ // Adds a gradient color to an SVG, uses offset and stop color
+ addGradientToSVG(parent: d3.Selection, svg: d3.Selection, name: string, options: any[]) {
+ let gradient = svg.append("defs")
+ .append("radialGradient")
+ .attr('id', name);
+
+ options.forEach(o => {
+ gradient.append('stop')
+ .attr('offset', o.offset)
+ .attr('stop-color', o['stop-color'])
+ })
- return value;
+ parent.style('fill', 'url(#'+name+')');
}
}
diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts
index 8c9678f36..149edf89f 100644
--- a/src/app/core/core.module.ts
+++ b/src/app/core/core.module.ts
@@ -29,10 +29,10 @@ import {
import { NgSelectModule } from '@ng-select/ng-select';
// Backend modules and extra config
-import { environment } from 'environments/environment';
+import { environment, ENV_FIREBASE_CONFIG } from 'environments/environment';
import { AngularFireModule } from 'angularfire2';
-export const firebaseConfig = environment.firebaseConfig;
+export const firebaseConfig = ENV_FIREBASE_CONFIG;
import { AngularFirestoreModule } from 'angularfire2/firestore';
import {
diff --git a/src/app/core/services/chart.service.ts b/src/app/core/services/chart.service.ts
index 70457592b..647424958 100644
--- a/src/app/core/services/chart.service.ts
+++ b/src/app/core/services/chart.service.ts
@@ -57,7 +57,8 @@ export class ChartService {
case 'scatter-plot':
return [
Number.isNaN(+d[dimValue[0]]) ? 0 : +d[dimValue[0]],
- Number.isNaN(+d[dimValue[1]]) ? 0 : +d[dimValue[1]]
+ Number.isNaN(+d[dimValue[1]]) ? 0 : +d[dimValue[1]],
+ d[dimValue[2]]
];
case 'select-menu':
if (info.filter) {
diff --git a/src/app/targets/gene-details/gene-rnaseq-de/gene-rnaseq-de.component.ts b/src/app/targets/gene-details/gene-rnaseq-de/gene-rnaseq-de.component.ts
index 7b538340b..d3152ea49 100644
--- a/src/app/targets/gene-details/gene-rnaseq-de/gene-rnaseq-de.component.ts
+++ b/src/app/targets/gene-details/gene-rnaseq-de/gene-rnaseq-de.component.ts
@@ -52,12 +52,12 @@ export class GeneRNASeqDEComponent implements OnInit {
this.chartService.addChartInfo(
'volcano-plot',
{
- dimension: ['logFC', 'neg_log10_adj_P_Val'],
+ dimension: ['logFC', 'neg_log10_adj_P_Val', 'hgnc_symbol'],
group: 'self',
type: 'scatter-plot',
title: 'Volcano Plot',
xAxisLabel: 'Log Fold Change',
- yAxisLabel: '-log10(Adjusted p-value',
+ yAxisLabel: '-log10(Adjusted p-value)',
x: ['logFC'],
y: ['neg_log10_adj_P_Val']
}
@@ -79,8 +79,7 @@ export class GeneRNASeqDEComponent implements OnInit {
{
dimension: ['tissue_study_pretty'],
group: 'self',
- type: 'select-menu',
- filter: true
+ type: 'select-menu'
}
);
this.chartService.addChartInfo(
@@ -88,8 +87,7 @@ export class GeneRNASeqDEComponent implements OnInit {
{
dimension: ['comparison_model_sex'],
group: 'self',
- type: 'select-menu',
- filter: true
+ type: 'select-menu'
}
);
}
diff --git a/src/app/targets/gene-search/gene-search.component.html b/src/app/targets/gene-search/gene-search.component.html
index 57e6fe885..3d6d40adf 100644
--- a/src/app/targets/gene-search/gene-search.component.html
+++ b/src/app/targets/gene-search/gene-search.component.html
@@ -9,7 +9,7 @@ Please type a gene symbol in the search box below.
Gene Symbol
- ;
+ @Input() genes: Gene[];
constructor(
private router: Router,
diff --git a/src/app/targets/targets-list/targets-list.component.html b/src/app/targets/targets-list/targets-list.component.html
index cea47c516..3e7fb0eaa 100644
--- a/src/app/targets/targets-list/targets-list.component.html
+++ b/src/app/targets/targets-list/targets-list.component.html
@@ -14,7 +14,7 @@
;
+ @Input() genes: Gene[];
msgs: Message[] = [];
totalRecords: number;
diff --git a/src/app/targets/targets-view/targets-view.component.html b/src/app/targets/targets-view/targets-view.component.html
index fd4028d61..a5dee3601 100644
--- a/src/app/targets/targets-view/targets-view.component.html
+++ b/src/app/targets/targets-view/targets-view.component.html
@@ -22,10 +22,10 @@
diff --git a/src/app/targets/targets-view/targets-view.component.ts b/src/app/targets/targets-view/targets-view.component.ts
index 904b1e462..7bfdf177a 100644
--- a/src/app/targets/targets-view/targets-view.component.ts
+++ b/src/app/targets/targets-view/targets-view.component.ts
@@ -16,6 +16,7 @@ import { Observable } from 'rxjs/Observable';
})
export class TargetsViewComponent implements OnInit {
genes$: Observable;
+ genes: Gene[];
dataLoaded: boolean = false;
@@ -35,6 +36,15 @@ export class TargetsViewComponent implements OnInit {
this.genes$ = this.geneService.loadGenesFile('sampleData.json');
this.genes$.subscribe(data => {
this.geneService.setGenes(data);
+ let gids = new Set();
+ let fgenes = data.filter((d: Gene) => {
+ if (gids.has(d.hgnc_symbol)) {
+ return false;
+ }
+ gids.add(d.hgnc_symbol);
+ return true;
+ })
+ this.genes = fgenes;
this.dataLoaded = true;
});
}
diff --git a/src/assets/data/service-key.json b/src/assets/data/service-key.json
deleted file mode 100644
index 03a996cd4..000000000
--- a/src/assets/data/service-key.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "type": "service_account",
- "project_id": "wall-of-targets",
- "private_key_id": "59223371f5fd4c30bdbf629f74938f0ef35fbdec",
- "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCf6BowLI0/ZzxJ\nZthE8G9oH2cBdZpmo6rJq54nvSUl1oAHl5lcAHAh8qNM84K+mPKUBK/mCEnu9eAO\nrGD7YLma49/i2Hwdr/LK5mwcg2HfKmzQAFP/IAIbnQTi1hutgPfs5Yn9M+uNOu1o\no2rCMdsKAyCJfCYVYdq0GT4NTlMbqe/uHIvOoWLGeLDeP1UJF0VgMIUKYw/b6NrM\ngllk8yt4vzddusTucevLxpYEMUN82GGO0MQTS2yz3xFNkqDjVrBgXermIVEnbNYl\nj+AlUPVKD5Cjb6sG6/jhm3pk/5tDxJXIhr4JFXq4xBbWrfa20qPLrJST8O44A9U6\nefAddc93AgMBAAECggEANqAfch2GEwa9PYX4k3RfWn5/vavjFvUKPuJ1oLWonwhr\nrCxJo3gy1iYBSyRUoa+2DoKbZEWonA06gUi5yKX+Qb9emCvEGpAkrNOSUxs+r/Ha\nQiQajajtj2lQZf0mb1sDVtYDEZBPA2wB9iD9M2G8perzjoPurpdDEwsce4cqNOSJ\n8AvLKHFQaG2PA5r7Zp23m3sqv8vhdMTASfO25BjfmUzInw/76/XLUOnVKdisbeI+\nqx7wWOeLn4/SwcgT8zumbhpcQIAYAUtqKWQGE6+AskiuurrW/LAtuo1cUWhn3rFT\nHCt9eLKXMenQjbwyJGk10u34Zm34h7yOC1kOIBjKAQKBgQDb09t5mP5wqOq3i0li\nxb+66jgY9SC1LuaCgaSzLGMAxd/+0h98peFY36fZNLHfj2+Z/8gMmOiWe3dbs7nJ\npdZWrfCidA+ejGNTTodmdMJykIjQ19UIo0uZJO9PKM7KdN2H9waQhaG9U+m4KZ+y\nh1ZmG0LZL8A+639PluPGzeMiwQKBgQC6OBoQptWpDPmDHyleO+tjkbO+Jnj/a2T9\nyktGiZ1vPH7ffmGSVc8y1wV3So9ICxAtW489lgN3I7zNgQYxoMJVGmHSRQMxVPjS\nmWXhSpY8cxKnDq9VmWELOepuqw2gvz8I6D+2i3Sn5BY2tJgUJx9dnPn0PXfSCnVc\ntDM2JndYNwKBgA8IpB/iyLPnuXWn/vLUV1JvJjr+uKfrdzRBrYPba0HoRnNk52Zc\n6dZsJUyDgA6r/pPnnETQSEfSVc1dsz0JLGltJ+HUUiUjsL9HxZbKMWooXFL8D8Ox\nd0AX+I3PWqUZ7PSZfkdv/Cm4jWlc32EURGJjRuUXkOw8qs5EoXs9JWWBAoGAYt7e\nl/C8iGEnKQMmh63wsJDvigoLPDHdERKTHQR73k+UXT1QuM1ukWOwt0GTCYE2OZ7f\nUqePd3Y2DDZw/3CHPYAgZ+/UXiDra4Wenhtn4PiH8sLNPPmOmNNLMS8IsUxZP3Vw\nPo+UhH+Je8ou5KRQ9vcdjxwDvjVnUq0qc5nAy8kCgYEAw6J0mHSWoef5MaV8jr+m\ncRqVgHo2JH/K6RkvBT9Rl6rDgP3WnrTTINgrQtpZwogjiSdqQJCkpGplhzhFOay0\nWXQQgfJcim0asyBH8kUO1wI9nm5wRWW6Te0iZR3rDEUGo6CZ6/XV5zUuLWYBFurV\ncnu0NNyskp6VU2XRqIEKdKA=\n-----END PRIVATE KEY-----\n",
- "client_email": "data-transfer@wall-of-targets.iam.gserviceaccount.com",
- "client_id": "104573732850432607336",
- "auth_uri": "https://accounts.google.com/o/oauth2/auth",
- "token_uri": "https://accounts.google.com/o/oauth2/token",
- "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
- "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/data-transfer%40wall-of-targets.iam.gserviceaccount.com"
-}
diff --git a/src/custom-typings.d.ts b/src/custom-typings.d.ts
index 621a416b4..8c409fff6 100644
--- a/src/custom-typings.d.ts
+++ b/src/custom-typings.d.ts
@@ -60,6 +60,16 @@ declare module 'modern-lru' {
declare var ENV: string;
declare var HMR: boolean;
declare var System: SystemJS;
+declare const FIREBASE_CONFIG: FirebaseConfig;
+
+interface FirebaseConfig {
+ apiKey: string;
+ authDomain: string;
+ databaseURL: string;
+ projectId: string;
+ storageBucket: string;
+ messagingSenderId: string;
+}
interface SystemJS {
import: (path?: string) => Promise;
@@ -70,6 +80,7 @@ interface GlobalEnvironment {
HMR: boolean;
SystemJS: SystemJS;
System: SystemJS;
+ FIREBASE_CONFIG: FirebaseConfig;
}
interface Es6PromiseLoader {
diff --git a/src/environments/environment.e2e.prod.ts b/src/environments/environment.e2e.prod.ts
index 71565abc3..2aaa245f2 100644
--- a/src/environments/environment.e2e.prod.ts
+++ b/src/environments/environment.e2e.prod.ts
@@ -5,6 +5,8 @@ import { Environment } from './model';
enableProdMode();
+// export const ENV_FIREBASE_CONFIG: any = FIREBASE_CONFIG;
+
export const environment: Environment = {
production: true,
showDevModule: true,
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index d9e846024..7c04dd7b9 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -5,6 +5,8 @@ import { Environment } from './model';
enableProdMode();
+// export const ENV_FIREBASE_CONFIG: any = FIREBASE_CONFIG;
+
export const environment: Environment = {
production: true,
showDevModule: false,
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index 714a15b86..4e108594f 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -7,6 +7,8 @@ import { Environment } from './model';
Error.stackTraceLimit = Infinity;
require('zone.js/dist/long-stack-trace-zone');
+export const ENV_FIREBASE_CONFIG: any = FIREBASE_CONFIG;
+
export const environment = {
production: false,
@@ -29,16 +31,6 @@ export const environment = {
},
ENV_PROVIDERS: [
- ],
-
- // Initialize Firebase
- firebaseConfig: {
- apiKey: 'AIzaSyBMS96wgJfydRf7BLDVh4DGtRKAZT8UpTM',
- authDomain: 'wall-of-targets.firebaseapp.com',
- databaseURL: 'https://wall-of-targets.firebaseio.com',
- projectId: 'wall-of-targets',
- storageBucket: 'wall-of-targets.appspot.com',
- messagingSenderId: '256222676709'
- }
+ ]
};
diff --git a/src/index.html b/src/index.html
index 84effd651..d71175edb 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,40 +1,58 @@
-
-
-
- <%= htmlWebpackPlugin.options.title %>
-
-
-
- <% if (webpackConfig.htmlElements.headTags) { %>
-
- <%= webpackConfig.htmlElements.headTags %>
- <% } %>
-
-
- <%= htmlWebpackPlugin.files.webpackManifest %>
-
- <% if (htmlWebpackPlugin.options.metadata.isDevServer && htmlWebpackPlugin.options.metadata.HMR !== true) { %>
-
-
- <% } %>
-
-
-
-
-
-
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+ <% if (webpackConfig.htmlElements.headTags) { %>
+
+ <%= webpackConfig.htmlElements.headTags %>
+ <% } %>
+
+
+ <%= htmlWebpackPlugin.files.webpackManifest %>
+
+ <% if (htmlWebpackPlugin.options.metadata.isDevServer && htmlWebpackPlugin.options.metadata.HMR !== true) { %>
+
+
+ <% } %>
+
+
+ <% if (htmlWebpackPlugin.options.gtmKey) { %>
+
+
+
+ <% } %>
+
+
+
+
+
+
+
-
-
- Loading...
-
-
-
+<% if (htmlWebpackPlugin.options.gtmKey) { %>
+
+
+
+<% } %>
+
+
+ Loading...
+
+
+