diff --git a/README.md b/README.md
index 0ab5295..f8a980d 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,7 @@ JSON should be POSTed to `/runs` with following fields:
**Optional**
`dataset` -- The name of the dataset on which the caller was run (e.g. Dream Chromosome 20).
+`projectName` -- The name of the project this run belongs to.
`tumorPath` -- The path on HDFS of the tumor BAM on which the caller was run.
`normalPath` -- The path on HDFS of the normalBAM on which the caller was run.
`params` -- Params that the caller was run with, or other notes relevant to the run.
diff --git a/cycledash/static/css/runs.css b/cycledash/static/css/runs.css
index 841e96d..395352b 100644
--- a/cycledash/static/css/runs.css
+++ b/cycledash/static/css/runs.css
@@ -42,7 +42,6 @@ tr.run.selected {
font-weight: 500;
}
tr.run-info {
- display: none;
background: rgb(247, 252, 253);
}
tr.run-info ul {
diff --git a/cycledash/static/js/runs.js b/cycledash/static/js/runs.js
index 8de2a87..a900d9c 100644
--- a/cycledash/static/js/runs.js
+++ b/cycledash/static/js/runs.js
@@ -1,5 +1,157 @@
'use strict';
-var d3 = require('d3');
+var d3 = require('d3'),
+ React = require('react'),
+ _ = require('underscore');
+
+
+ //////////////////////////
+ // React Run page code: //
+//////////////////////////
+var NO_FILTER = '----';
+
+var RunsPage = React.createClass({
+ propTypes: {
+ runs: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
+ runKeyVals: React.PropTypes.object.isRequired
+ },
+ getInitialState: function() {
+ return {selectedRunId: null, projectFilter: null};
+ },
+ filteredRuns: function() {
+ return this.props.runs.filter(run => (this.state.projectFilter === null) || this.state.projectFilter.match(run.project_name) );
+ },
+ handleClickRun: function(runId) {
+ return () => {
+ this.setState({selectedRunId: this.state.selectedRunId === runId ? null : runId});
+ };
+ },
+ handleProjectFilter: function(evt) {
+ var val = evt.target.value;
+ this.setState({projectFilter: val === NO_FILTER ? null : val});
+ },
+ render: function() {
+ var projectNames = _.unique(_.pluck(this.props.runs, 'project_name'));
+ var projects = projectNames.map(name => );
+ var runs = this.filteredRuns();
+ var rows = runs.map((run) => {
+ var rows = [];
+ if (run.id === this.state.selectedRunId) {
+ rows.push();
+ }
+ return rows;
+ });
+ return (
+
+
Filter runs by project name:
+
+
+
+
+
+ |
+ Caller Name |
+ Dataset |
+ Submitted On |
+ |
+ |
+
+
+
+ {rows}
+
+
+
+ );
+ }
+});
+
+var RunRow = React.createClass({
+ propTypes: {
+ run: React.PropTypes.object.isRequired,
+ handleClick: React.PropTypes.func.isRequired
+ },
+ render: function() {
+ var run = this.props.run;
+ return (
+
+
+ { run.id }
+ Examine
+ |
+ { run.caller_name } |
+ { run.dataset_name } |
+ { run.created_at } |
+
+
+
+ );
+ }
+});
+
+var RowValues = React.createClass({
+ propTypes: {
+ run: React.PropTypes.object.isRequired,
+ runKeyVals: React.PropTypes.object.isRequired
+ },
+ render: function() {
+ var run = this.props.run,
+ descriptions = _.reduce(this.props.runKeyVals, function(acc, key, title) {
+ if (run[key]) {
+ acc.push({ title });
+ acc.push({ run[key] });
+ }
+ return acc;
+ }, []);
+ return (
+
+
+
+ {descriptions}
+
+ |
+
+ );
+ }
+});
+
+var RowLabels = React.createClass({
+ propTypes: {
+ run: React.PropTypes.object.isRequired
+ },
+ render: function() {
+ var run = this.props.run;
+ return (
+
+ { run.validation_vcf ? validation : null }
+ { run.tumor_bam_uri ? tumor : null }
+ { run.normal_bam_uri ? normal : null }
+ |
+ );
+ }
+});
+
+var RowComments = React.createClass({
+ propTypes: {
+ run: React.PropTypes.object.isRequired
+ },
+ render: function() {
+ var run = this.props.run;
+ return (
+
+
+
+ { run.num_comments }
+
+ |
+ );
+ }
+});
+
+
+ ///////////////////////////////////////
+ // Non-React code for form handling: //
+///////////////////////////////////////
// Upload the file and place its path in the element.
function upload(file, fileInputElement) {
@@ -37,27 +189,9 @@ function extractError(response) {
return json.message || json.error || 'Upload failed.';
}
-window.activateRunsUI = function() {
- d3.selectAll('tr.run')
- .on('click', function(d, i) {
- // Ignore clicks on links in the row.
- var tag = d3.event.target.tagName;
- if (tag == 'A' || tag == 'INPUT') return;
-
- var selected = d3.select(this).attr('data-selected');
- var $this = d3.select(this);
- if (selected == 'true') {
- $this.attr('data-selected', false)
- .attr('class', 'run');
- d3.select($this.node().nextElementSibling)
- .style('display', 'none');
- } else {
- $this.attr('data-selected', true)
- .attr('class', 'run selected');
- d3.select($this.node().nextElementSibling)
- .style('display', 'table-row');
- }
- });
+
+window.renderRunPage = function(el, runs, runKeyVals) {
+ React.render(, el);
d3.select('#show-submit')
.on('click', function() {
diff --git a/cycledash/templates/macros/run_form.html b/cycledash/templates/macros/run_form.html
index 123cf8f..e1066c1 100644
--- a/cycledash/templates/macros/run_form.html
+++ b/cycledash/templates/macros/run_form.html
@@ -11,6 +11,11 @@
+
+
+
+
Optional all paths should be HDFS paths to canonical data
-
+
{%- endmacro -%}
diff --git a/cycledash/templates/runs.html b/cycledash/templates/runs.html
index cd42c4a..b8f0498 100644
--- a/cycledash/templates/runs.html
+++ b/cycledash/templates/runs.html
@@ -19,58 +19,12 @@
{%- endif %}
-
-
-
- |
- Caller Name |
- Dataset |
- Submitted On |
- |
- |
-
-
-
- {%- for run in runs %}
-
-
- {{ run['id'] }}
- Examine
- |
- {{ run['caller_name'] }} |
- {{ run['dataset_name'] }} |
- {{ run['created_at'].strftime('%Y-%m-%d') }} |
-
- {%- if run['validation_vcf'] %}validation{% endif -%}
- {%- if run['tumor_bam_uri'] %}tumor{% endif -%}
- {%- if run['normal_bam_uri'] %}normal{% endif -%}
- |
-
-
-
-
-
- {% for name, key in run_kvs.iteritems() %}
- {%- if run.get(key) %}
- - {{ name }}
- {{ run[key] }}
- {% endif -%}
- {% endfor %}
-
- |
-
- {% endfor -%}
-
-
-
-
-
-
- {% endblock %}
+
+
+
+
+{% endblock %}
diff --git a/cycledash/validations.py b/cycledash/validations.py
index ee29ea3..fe32061 100644
--- a/cycledash/validations.py
+++ b/cycledash/validations.py
@@ -23,6 +23,7 @@ def is_path(s):
'is_validation': bool,
'params': unicode,
'dataset': unicode,
+ 'project_name': unicode,
'vcf_header': unicode
})
@@ -35,6 +36,7 @@ def is_path(s):
'is_validation': bool,
'params': unicode,
'dataset': unicode,
+ 'project_name': unicode,
'vcf_header': unicode,
'true_positive': Coerce(int),
'false_positive': Coerce(int),
diff --git a/cycledash/views.py b/cycledash/views.py
index a8794f7..4dcbae5 100644
--- a/cycledash/views.py
+++ b/cycledash/views.py
@@ -38,7 +38,8 @@
RUN_ADDL_KVS = {'Tumor BAM': 'tumor_bam_uri',
'Normal BAM': 'normal_bam_uri',
'VCF URI': 'uri',
- 'Notes': 'notes'}
+ 'Notes': 'notes',
+ 'Project': 'project_name'}
VCF_FILENAME = 'cycledash-run-{}.vcf'
diff --git a/schema.sql b/schema.sql
index a25ad1f..3a4a099 100644
--- a/schema.sql
+++ b/schema.sql
@@ -3,6 +3,9 @@ CREATE TABLE vcfs (
created_at TIMESTAMP DEFAULT statement_timestamp() NOT NULL,
caller_name TEXT NOT NULL, -- Name of the caller this came from.
dataset_name TEXT NOT NULL, -- Name of the dataset VCF is from (e.g. Synth4, PC7114)
+
+ project_name TEXT, -- Name of the project (research, patient, etc) this VCF is part of.
+
tumor_bam_uri TEXT, -- URI of tumor BAM
normal_bam_uri TEXT, -- URI of normal BAM
validation_vcf BOOLEAN DEFAULT false, -- whether or not this is a validation VCF
diff --git a/tests/pdifftests/runs-info.png b/tests/pdifftests/runs-info.png
index da491be..a352f59 100644
Binary files a/tests/pdifftests/runs-info.png and b/tests/pdifftests/runs-info.png differ
diff --git a/tests/pdifftests/runs.png b/tests/pdifftests/runs.png
index ea93a69..d30b89b 100644
Binary files a/tests/pdifftests/runs.png and b/tests/pdifftests/runs.png differ