sequenceserver 3.1.3 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/sequenceserver/api_errors.rb +18 -0
- data/lib/sequenceserver/blast/hit.rb +1 -2
- data/lib/sequenceserver/blast/job.rb +15 -5
- data/lib/sequenceserver/blast/report.rb +5 -1
- data/lib/sequenceserver/database.rb +5 -1
- data/lib/sequenceserver/job.rb +26 -10
- data/lib/sequenceserver/makeblastdb.rb +2 -1
- data/lib/sequenceserver/routes.rb +37 -12
- data/lib/sequenceserver/version.rb +1 -1
- data/lib/sequenceserver/zip_file_generator.rb +1 -1
- data/public/404.html +8 -16
- data/public/css/app.css +794 -111
- data/public/css/app.min.css +1 -1
- data/public/css/sequenceserver.min.css +1 -8
- data/public/js/circos.js +103 -96
- data/public/{packages/circosJS@1.7.0.js → js/circosjs.js} +156 -778
- data/public/js/cloud_share_modal.js +55 -58
- data/public/js/collapse_preferences.js +13 -15
- data/public/js/databases.js +4 -3
- data/public/js/databases_tree.js +51 -30
- data/public/js/error_modal.js +35 -22
- data/public/js/exporter.js +2 -0
- data/public/js/form.js +43 -7
- data/public/js/grapher.js +94 -92
- data/public/js/hit.js +21 -19
- data/public/js/hits.js +45 -38
- data/public/js/hits_overview.js +8 -27
- data/public/js/hsp.js +140 -144
- data/public/js/jquery_world.js +24 -15
- data/public/js/kablammo.js +14 -10
- data/public/js/length_distribution.js +23 -37
- data/public/js/null_plugins/report_plugins.js +3 -5
- data/public/js/options.js +17 -19
- data/public/js/query.js +36 -35
- data/public/js/report/alignment_results.js +18 -0
- data/public/js/report/graphical_overview.js +38 -0
- data/public/js/report/report_header.js +28 -0
- data/public/js/report/run_summary.js +43 -0
- data/public/js/report.js +62 -102
- data/public/js/report_root.js +3 -3
- data/public/js/search.js +5 -2
- data/public/js/search_button.js +85 -78
- data/public/js/sequence.js +11 -22
- data/public/js/sequence_modal.js +38 -37
- data/public/js/share_url.js +5 -5
- data/public/js/sidebar.js +148 -86
- data/public/js/svgExporter.js +8 -3
- data/public/js/tests/form.spec.js +1 -1
- data/public/js/tests/visualisation_helpers.spec.js +28 -0
- data/public/js/visualisation_helpers.js +24 -27
- data/public/sequenceserver-report.min.js +1 -1
- data/public/sequenceserver-report.min.js.LICENSE.txt +1 -88
- data/public/sequenceserver-report.min.js.map +1 -1
- data/public/sequenceserver-search.min.js +1 -1
- data/public/sequenceserver-search.min.js.LICENSE.txt +0 -30
- data/public/sequenceserver-search.min.js.map +1 -1
- data/views/error.erb +1 -1
- data/views/layout.erb +123 -130
- data/views/report.erb +3 -2
- data/views/search.erb +10 -14
- metadata +13 -26
- data/public/css/fonts.css +0 -31
- data/public/css/grapher.css +0 -670
- data/public/css/sequenceserver.css +0 -509
- data/public/vendor/github/mbostock/d3@3.5.6/d3.js +0 -9506
- data/public/vendor/github/mbostock/d3@3.5.6.js +0 -1
- data/views/search_layout.erb +0 -153
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import d3 from 'd3';
|
|
1
|
+
import * as d3 from 'd3';
|
|
2
2
|
import _ from 'underscore';
|
|
3
3
|
import Grapher from 'grapher';
|
|
4
4
|
import * as Helpers from './visualisation_helpers';
|
|
@@ -61,20 +61,19 @@ class Graph {
|
|
|
61
61
|
this.draw_rectangles();
|
|
62
62
|
this.draw_query_line();
|
|
63
63
|
this.draw_axes();
|
|
64
|
-
this.setupTooltip();
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
define_scale_and_bins() {
|
|
68
|
-
this._scale_x = d3.
|
|
67
|
+
this._scale_x = d3.scaleLinear()
|
|
69
68
|
.domain([
|
|
70
69
|
0,
|
|
71
70
|
(d3.max([this.query_length, d3.max(this._data)]) * 1.01)
|
|
72
71
|
]).nice()
|
|
73
72
|
.range([0, this._width]);
|
|
74
|
-
this._bins = d3.
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
this._scale_y = d3.
|
|
73
|
+
this._bins = d3.bin()
|
|
74
|
+
.domain(this._scale_x.domain())
|
|
75
|
+
.thresholds(this._scale_x.ticks(50))(this._data);
|
|
76
|
+
this._scale_y = d3.scaleLinear()
|
|
78
77
|
.domain([0, d3.max(this._bins, function(d) { return d.length; })])
|
|
79
78
|
.range([this._height, 0]).nice();
|
|
80
79
|
}
|
|
@@ -83,13 +82,6 @@ class Graph {
|
|
|
83
82
|
this._data = _.map(this.query.hits, _.iteratee('length'));
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
setupTooltip() {
|
|
87
|
-
this.svg_container.find('[data-toggle="tooltip"]').tooltip({
|
|
88
|
-
'placement': 'top', 'container': 'body', 'html': 'true',
|
|
89
|
-
'delay': 0, 'white-space': 'nowrap'
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
85
|
setupResponsiveness() {
|
|
94
86
|
var currentWidth = $(window).width();
|
|
95
87
|
console.log('cureent '+currentWidth);
|
|
@@ -104,20 +96,19 @@ class Graph {
|
|
|
104
96
|
}
|
|
105
97
|
|
|
106
98
|
tick_formatter(seq_type) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
112
|
-
if (d === 0)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
} else {
|
|
117
|
-
return (format(prefix.scale(d)) + ' ' + prefix.symbol + suffixes[seq_type]);
|
|
118
|
-
}
|
|
99
|
+
const ticks = this._scale_x.ticks();
|
|
100
|
+
const prefix = d3.format('~s');
|
|
101
|
+
const suffixes = { amino_acid: 'aa', nucleic_acid: 'bp' };
|
|
102
|
+
|
|
103
|
+
return (d) => {
|
|
104
|
+
if (d === 0 || !ticks.includes(d)) return;
|
|
105
|
+
|
|
106
|
+
if (suffixes[seq_type] === 'aa') {
|
|
107
|
+
return `${d} ${suffixes[seq_type]}`;
|
|
119
108
|
} else {
|
|
120
|
-
|
|
109
|
+
const formatted = prefix(d);
|
|
110
|
+
|
|
111
|
+
return `${Helpers.formatNumberUnits(formatted)}${suffixes[seq_type]}`;
|
|
121
112
|
}
|
|
122
113
|
};
|
|
123
114
|
}
|
|
@@ -144,7 +135,7 @@ class Graph {
|
|
|
144
135
|
};
|
|
145
136
|
inner_data.push(item);
|
|
146
137
|
});
|
|
147
|
-
var item = {data: inner_data, x: bin.
|
|
138
|
+
var item = {data: inner_data, x: bin.x0, dx: bin.x1, length: bin.length};
|
|
148
139
|
data2.push(item);
|
|
149
140
|
});
|
|
150
141
|
this._update_data = data2;
|
|
@@ -166,13 +157,12 @@ class Graph {
|
|
|
166
157
|
.attr('xlink:href', function(i) { return i.url; })
|
|
167
158
|
.append('rect')
|
|
168
159
|
.attr('class','bar')
|
|
169
|
-
.attr('data-toggle','tooltip')
|
|
170
160
|
.attr('title', function(i) {
|
|
171
161
|
return i.id+' '+'<br>E value: '+Helpers.prettify_evalue(i.evalue)+'<br>Length: '+i.value;
|
|
172
162
|
})
|
|
173
163
|
.attr('x', 1)
|
|
174
164
|
.attr('y', function(i) { return (self._scale_y(i.y0)); })
|
|
175
|
-
.attr('width', self._scale_x(this._bins[1].
|
|
165
|
+
.attr('width', self._scale_x(this._bins[1].x0) - self._scale_x(this._bins[0].x0) - 1)
|
|
176
166
|
.attr('height', function (i) { return self._scale_y(i.y1) - self._scale_y(i.y0); })
|
|
177
167
|
.attr('fill', function(i) {
|
|
178
168
|
return i.color;
|
|
@@ -210,16 +200,12 @@ class Graph {
|
|
|
210
200
|
space = len;
|
|
211
201
|
}
|
|
212
202
|
var formatter = this.tick_formatter(this._seq_type.subject_seq_type);
|
|
213
|
-
var x_axis = d3.
|
|
214
|
-
.scale(this._scale_x)
|
|
215
|
-
.orient('bottom')
|
|
203
|
+
var x_axis = d3.axisTop(this._scale_x)
|
|
216
204
|
.ticks(50)
|
|
217
205
|
.tickFormat(formatter);
|
|
218
|
-
var y_axis = d3.
|
|
219
|
-
.scale(this._scale_y)
|
|
220
|
-
.orient('left')
|
|
206
|
+
var y_axis = d3.axisLeft(this._scale_y)
|
|
221
207
|
.tickValues(this._scale_y.ticks(space))
|
|
222
|
-
.
|
|
208
|
+
.tickSizeOuter(0)
|
|
223
209
|
.tickFormat(function (e) {
|
|
224
210
|
if (Math.floor(e) != e) {
|
|
225
211
|
return ;
|
|
@@ -6,14 +6,12 @@ class ReportPlugins {
|
|
|
6
6
|
this.parent = parent;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
init() {
|
|
9
|
+
init(_callback) {
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
componentDidUpdate(_prevProps, _prevState) {
|
|
13
|
-
}
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
return
|
|
13
|
+
queryResult(_query) {
|
|
14
|
+
return null;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
generateStats() {
|
data/public/js/options.js
CHANGED
|
@@ -72,9 +72,10 @@ export class Options extends Component {
|
|
|
72
72
|
return (
|
|
73
73
|
<div id="options-presets" className="w-full">
|
|
74
74
|
{ Object.keys(this.props.predefinedOptions).length > 1 && <>
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
<div className="flex items-center border-b border-seqorange mb-2">
|
|
76
|
+
<h3 className="text-base md:text-lg font-medium pr-1">Settings</h3>
|
|
77
|
+
<p className="text-base text-gray-500">Choose a predefined setting or customize parameters.</p>
|
|
78
|
+
</div>
|
|
78
79
|
{this.presetListJSX()}
|
|
79
80
|
</>}
|
|
80
81
|
</div>
|
|
@@ -83,7 +84,7 @@ export class Options extends Component {
|
|
|
83
84
|
|
|
84
85
|
presetListJSX() {
|
|
85
86
|
return (
|
|
86
|
-
<ul className="text-
|
|
87
|
+
<ul className="md:text-lg my-1">
|
|
87
88
|
{
|
|
88
89
|
Object.entries(this.props.predefinedOptions).map(
|
|
89
90
|
([key, config], index) => {
|
|
@@ -91,7 +92,7 @@ export class Options extends Component {
|
|
|
91
92
|
let description = config.description || textValue;
|
|
92
93
|
|
|
93
94
|
return (
|
|
94
|
-
<label key={index} className={`block w-full px-2 py-1 hover:bg-gray-200 cursor-pointer`}>
|
|
95
|
+
<label key={index} className={`block w-full px-2 py-1 text-seqblue hover:bg-gray-200 hover:text-seqorange cursor-pointer`}>
|
|
95
96
|
<input
|
|
96
97
|
type="radio"
|
|
97
98
|
name="predefinedOption"
|
|
@@ -119,26 +120,20 @@ export class Options extends Component {
|
|
|
119
120
|
return(
|
|
120
121
|
<div className={this.state.paramsMode !== 'advanced' ? 'w-full hidden' : 'w-full'}>
|
|
121
122
|
<div className="flex items-end">
|
|
122
|
-
<label className="flex items-center" htmlFor="advanced">
|
|
123
|
-
|
|
123
|
+
<label className="flex items-center text-base md:text-lg" htmlFor="advanced">
|
|
124
|
+
Parameters:
|
|
124
125
|
</label>
|
|
125
126
|
|
|
126
127
|
{this.props.blastMethod &&
|
|
127
128
|
<button
|
|
128
|
-
className="text-seqblue ml-2"
|
|
129
|
+
className="text-seqblue text-base md:text-lg ml-2"
|
|
129
130
|
type="button"
|
|
130
131
|
onClick={this.showAdvancedOptionsHelp}
|
|
131
132
|
data-toggle="modal" data-target="#help">
|
|
132
|
-
See
|
|
133
|
+
See options
|
|
133
134
|
<i className="fa fa-question-circle ml-1 w-3 h-4 fill-current"></i>
|
|
134
135
|
</button>
|
|
135
136
|
}
|
|
136
|
-
|
|
137
|
-
{!this.props.blastMethod &&
|
|
138
|
-
<span className="text-gray-600 ml-2 text-sm hidden sm:block">
|
|
139
|
-
Select databases and fill in the query to see options.
|
|
140
|
-
</span>
|
|
141
|
-
}
|
|
142
137
|
</div>
|
|
143
138
|
|
|
144
139
|
<div className='flex-grow flex w-full'>
|
|
@@ -151,8 +146,8 @@ export class Options extends Component {
|
|
|
151
146
|
title="View, and enter advanced parameters."
|
|
152
147
|
/>
|
|
153
148
|
</div>
|
|
154
|
-
<div className="
|
|
155
|
-
Options as they would appear in a command
|
|
149
|
+
<div className="mt-1 text-gray-700">
|
|
150
|
+
Options as they would appear in a command-line.
|
|
156
151
|
</div>
|
|
157
152
|
</div>
|
|
158
153
|
)
|
|
@@ -161,15 +156,18 @@ export class Options extends Component {
|
|
|
161
156
|
showAdvancedOptionsHelp(e) {
|
|
162
157
|
const ids = ['blastn', 'tblastn', 'blastp', 'blastx', 'tblastx'];
|
|
163
158
|
const method = this.props.blastMethod.toLowerCase();
|
|
159
|
+
const modal = document.querySelector("dialog.advanced-modal");
|
|
160
|
+
|
|
164
161
|
// hide options for other algorithms and only show for selected algorithm
|
|
165
162
|
for (const id of ids) {
|
|
166
163
|
if (id === method) {
|
|
167
|
-
document.getElementById(id).classList.remove('hidden')
|
|
164
|
+
document.getElementById(id).classList.remove('hidden');
|
|
168
165
|
} else {
|
|
169
166
|
document.getElementById(id).classList.add('hidden');
|
|
170
167
|
}
|
|
171
168
|
}
|
|
172
|
-
|
|
169
|
+
|
|
170
|
+
modal.showModal();
|
|
173
171
|
}
|
|
174
172
|
|
|
175
173
|
render() {
|
data/public/js/query.js
CHANGED
|
@@ -6,6 +6,7 @@ import LengthDistribution from './length_distribution'; // length distribution o
|
|
|
6
6
|
import Utils from './utils';
|
|
7
7
|
import { fastqToFasta } from './fastq_to_fasta';
|
|
8
8
|
import CollapsePreferences from './collapse_preferences';
|
|
9
|
+
import './jquery_world';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Query component displays query defline, graphical overview, length
|
|
@@ -44,21 +45,22 @@ export class ReportQuery extends Component {
|
|
|
44
45
|
return this.props.query.hits.length;
|
|
45
46
|
}
|
|
46
47
|
headerJSX() {
|
|
47
|
-
var meta = `
|
|
48
|
+
var meta = `Length: ${this.queryLength().toLocaleString()}`;
|
|
49
|
+
|
|
48
50
|
if (this.props.showQueryCrumbs) {
|
|
49
|
-
meta = `query ${this.props.query.number}
|
|
51
|
+
meta = `query ${this.props.query.number}. ${meta}`;
|
|
50
52
|
}
|
|
51
|
-
return <div className="section-header">
|
|
52
|
-
<h3>
|
|
53
|
-
<strong>Query
|
|
54
|
-
{this.props.query.title}
|
|
53
|
+
return <div className="section-header border-b border-seqorange justify-between w-full flex flex-col sm:flex-row gap-4">
|
|
54
|
+
<h3 className="text-base cursor-pointer flex flex-col sm:flex-row items-start">
|
|
55
|
+
<strong>Query=<span className="ml-1">{this.props.query.id}</span></strong>
|
|
56
|
+
<span className="ml-1">{this.props.query.title}</span>
|
|
55
57
|
</h3>
|
|
56
|
-
<span className="label
|
|
58
|
+
<span className="label first-letter:capitalize text-sm text-right font-normal text-inherit pt-0 px-0">{meta}</span>
|
|
57
59
|
</div>;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
hitsListJSX() {
|
|
61
|
-
return <div className="
|
|
63
|
+
return <div className="pt-0 px-0 pb-px">
|
|
62
64
|
<HitsOverview key={'GO_' + this.props.query.number} query={this.props.query} program={this.props.program} collapsed={this.props.veryBig} />
|
|
63
65
|
<LengthDistribution key={'LD_' + this.props.query.id} query={this.props.query} algorithm={this.props.program} />
|
|
64
66
|
<HitsTable key={'HT_' + this.props.query.number} query={this.props.query} imported_xml={this.props.imported_xml} />
|
|
@@ -66,14 +68,14 @@ export class ReportQuery extends Component {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
noHitsJSX() {
|
|
69
|
-
return <div className="
|
|
71
|
+
return <div className="pt-0 px-0 pb-px">
|
|
70
72
|
<strong> ****** No BLAST hits found ****** </strong>
|
|
71
73
|
</div>;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
render() {
|
|
75
77
|
return (
|
|
76
|
-
<div className="resultn" id={this.domID()}
|
|
78
|
+
<div className="resultn mt-1.5" id={this.domID()}
|
|
77
79
|
data-query-len={this.props.query.length}
|
|
78
80
|
data-algorithm={this.props.program}>
|
|
79
81
|
{this.headerJSX()}
|
|
@@ -348,7 +350,7 @@ export class SearchQueryWidget extends Component {
|
|
|
348
350
|
className="sequence">
|
|
349
351
|
<textarea
|
|
350
352
|
id="sequence" ref={this.textareaRef}
|
|
351
|
-
className="block w-full p-4 text-gray-900 border border-gray-300 rounded-l-lg rounded-tr-lg bg-gray-50 text-
|
|
353
|
+
className="block w-full p-4 text-gray-900 border border-gray-300 rounded-l-lg rounded-tr-lg bg-gray-50 text-sm font-mono min-h-52 resize-y"
|
|
352
354
|
name="sequence" value={this.state.value}
|
|
353
355
|
rows="6"
|
|
354
356
|
required="required"
|
|
@@ -408,16 +410,16 @@ class HitsTable extends Component {
|
|
|
408
410
|
if (this.props.imported_xml) seqwidth += 15;
|
|
409
411
|
|
|
410
412
|
return <table
|
|
411
|
-
className="table table-hover table-condensed tabular-view ">
|
|
413
|
+
className="table table-hover table-condensed tabular-view text-sm min-w-full mb-0">
|
|
412
414
|
<thead>
|
|
413
|
-
<tr>
|
|
414
|
-
<th className="text-left">#</th>
|
|
415
|
-
<th
|
|
416
|
-
{hasName && <th
|
|
417
|
-
{!this.props.imported_xml && <th
|
|
418
|
-
<th
|
|
419
|
-
<th
|
|
420
|
-
<th
|
|
415
|
+
<tr className="text-neutral-500">
|
|
416
|
+
<th className="text-left font-normal">#</th>
|
|
417
|
+
<th style={{ width: `${seqwidth}%` }} className="text-left font-normal">Similar sequences</th>
|
|
418
|
+
{hasName && <th className="text-left font-normal w-2/12">Species</th>}
|
|
419
|
+
{!this.props.imported_xml && <th className="text-right font-normal w-2/12">Query coverage (%)</th>}
|
|
420
|
+
<th className="text-right font-normal w-1/12">Total score</th>
|
|
421
|
+
<th className="text-right font-normal w-1/12">E value</th>
|
|
422
|
+
<th className="text-right font-normal w-1/12">Identity (%)</th>
|
|
421
423
|
</tr>
|
|
422
424
|
</thead>
|
|
423
425
|
<tbody>
|
|
@@ -426,22 +428,19 @@ class HitsTable extends Component {
|
|
|
426
428
|
return (
|
|
427
429
|
<tr key={hit.number}>
|
|
428
430
|
<td className="text-left">{hit.number + '.'}</td>
|
|
429
|
-
<td
|
|
430
|
-
title={`${hit.id} ${hit.title}`}
|
|
431
|
-
data-toggle="tooltip" data-placement="left">
|
|
431
|
+
<td>
|
|
432
432
|
<a href={'#Query_' + this.props.query.number + '_hit_' + hit.number}
|
|
433
|
-
className="
|
|
433
|
+
className="text-sm text-seqblue hover:text-seqorange cursor-pointer pe-1 line-clamp-1 tooltip-item" title={`${hit.id} ${hit.title}`}>{hit.id} {hit.title}</a>
|
|
434
434
|
</td>
|
|
435
435
|
{hasName &&
|
|
436
|
-
<td className="
|
|
437
|
-
data-toggle="tooltip" data-placement="top">
|
|
436
|
+
<td className="pe-1 line-clamp-1 tooltip-item" title={hit.sciname}>
|
|
438
437
|
{hit.sciname}
|
|
439
438
|
</td>
|
|
440
439
|
}
|
|
441
440
|
{!this.props.imported_xml && <td className="text-right">{hit.qcovs}</td>}
|
|
442
|
-
<td className="text-right">{hit.total_score}</td>
|
|
443
|
-
<td className="text-right">{Utils.inExponential(hit.hsps[0].evalue)}</td>
|
|
444
|
-
<td className="text-right">{Utils.inPercentage(hit.hsps[0].identity, hit.hsps[0].length)}</td>
|
|
441
|
+
<td className="pe-1 text-right">{hit.total_score}</td>
|
|
442
|
+
<td className="pe-1 text-right">{Utils.inExponential(hit.hsps[0].evalue)}</td>
|
|
443
|
+
<td className="pe-1 text-right">{Utils.inPercentage(hit.hsps[0].identity, hit.hsps[0].length)}</td>
|
|
445
444
|
</tr>
|
|
446
445
|
);
|
|
447
446
|
}, this))
|
|
@@ -452,12 +451,14 @@ class HitsTable extends Component {
|
|
|
452
451
|
|
|
453
452
|
render() {
|
|
454
453
|
return (
|
|
455
|
-
<div className=
|
|
456
|
-
<
|
|
457
|
-
{this.collapsePreferences.
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
454
|
+
<div className={`table-hit-overview ${this.state.collapsed ? 'print:hidden' : ''}`}>
|
|
455
|
+
<div className="grapher-header pr-px">
|
|
456
|
+
<h4 className="inline-block pl-px m-0 caption cursor-pointer text-sm caption" onClick={() => this.collapsePreferences.toggleCollapse()}>
|
|
457
|
+
{this.collapsePreferences.renderCollapseIcon()}
|
|
458
|
+
<span> {this.name}</span>
|
|
459
|
+
</h4>
|
|
460
|
+
</div>
|
|
461
|
+
<div id={'Query_' + this.props.query.number + 'HT_' + this.props.query.number} className="overflow-auto md:overflow-hidden">
|
|
461
462
|
{!this.state.collapsed && this.tableJSX()}
|
|
462
463
|
</div>
|
|
463
464
|
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import ReportHeader from './report_header';
|
|
2
|
+
import Hits from '../hits';
|
|
3
|
+
|
|
4
|
+
const AlignmentResults = (props) => {
|
|
5
|
+
const renderContent = () => (
|
|
6
|
+
<div>
|
|
7
|
+
{props.state.results}
|
|
8
|
+
<Hits
|
|
9
|
+
{...props}
|
|
10
|
+
componentFinishedUpdating={(_) => props.componentFinishedUpdating(_)}
|
|
11
|
+
/>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
return <ReportHeader name="Alignment Results" renderContent={renderContent} />;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default AlignmentResults;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Circos from '../circos';
|
|
2
|
+
import ReportHeader from './report_header';
|
|
3
|
+
|
|
4
|
+
const GraphicalOverview = ({queries, program, plugins}) => {
|
|
5
|
+
/**
|
|
6
|
+
* Does the report have at least two hits? This is used to determine
|
|
7
|
+
* whether Circos should be enabled or not.
|
|
8
|
+
*/
|
|
9
|
+
const atLeastTwoHits = () => {
|
|
10
|
+
let hitNum = 0;
|
|
11
|
+
return queries.some((query) => {
|
|
12
|
+
hitNum += query.hits.length;
|
|
13
|
+
return hitNum > 1;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const renderContent = () => {
|
|
18
|
+
if(!atLeastTwoHits()) return null;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Circos queries={queries} program={program} />
|
|
23
|
+
{plugins.generateStats(queries)}
|
|
24
|
+
</>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<ReportHeader
|
|
30
|
+
name="Graphical Overview"
|
|
31
|
+
renderContent={renderContent}
|
|
32
|
+
renderable={atLeastTwoHits()}
|
|
33
|
+
defaultCollapsed={true}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default GraphicalOverview;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import CollapsePreferences from '../collapse_preferences';
|
|
3
|
+
|
|
4
|
+
const ReportHeader = (props) => {
|
|
5
|
+
const [state, setState] = useState({ collapsed: null });
|
|
6
|
+
const [renderable, _setRenderable] = useState(props.renderable === undefined ? true : props.renderable);
|
|
7
|
+
const collapsePreferences = new CollapsePreferences(
|
|
8
|
+
{ name: props.name, state: { ...state }, setState: setState },
|
|
9
|
+
props.defaultCollapsed
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
if (state.collapsed === null) setState({ collapsed: collapsePreferences.preferenceStoredAsCollapsed() });
|
|
13
|
+
if (!renderable) return null;
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className={`${state.collapsed ? 'print:!hidden' : ''}`}>
|
|
17
|
+
<h3 className="caption font-bold border-b-2 border-seqorange" onClick={() => collapsePreferences.toggleCollapse()}>
|
|
18
|
+
{collapsePreferences.renderCollapseIcon()}
|
|
19
|
+
<span> {props.name}</span>
|
|
20
|
+
</h3>
|
|
21
|
+
<div className='mx-1 print:mx-0'>
|
|
22
|
+
{!state.collapsed && props.renderContent()}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default ReportHeader;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import ReportHeader from './report_header';
|
|
2
|
+
import _ from 'underscore';
|
|
3
|
+
|
|
4
|
+
const RunSummary = (props) => {
|
|
5
|
+
const renderContent = () => {
|
|
6
|
+
return (
|
|
7
|
+
<div className="overview mr-0 mb-0">
|
|
8
|
+
<p className="m-0 text-sm">
|
|
9
|
+
<strong>SequenceServer {props.seqserv_version}</strong> using{' '}
|
|
10
|
+
<strong>{props.program_version}</strong>
|
|
11
|
+
{props.submitted_at &&
|
|
12
|
+
`, query submitted on ${props.submitted_at}`}
|
|
13
|
+
</p>
|
|
14
|
+
<p className="m-0 text-sm">
|
|
15
|
+
<strong> Databases: </strong>
|
|
16
|
+
{props.querydb
|
|
17
|
+
.map((db) => {
|
|
18
|
+
return db.title;
|
|
19
|
+
})
|
|
20
|
+
.join(', ')}{' '}
|
|
21
|
+
({props.stats.nsequences} sequences,
|
|
22
|
+
{props.stats.ncharacters} characters)
|
|
23
|
+
</p>
|
|
24
|
+
<p className="m-0 text-sm">
|
|
25
|
+
<strong>Parameters: </strong>{' '}
|
|
26
|
+
{_.map(props.params, function (val, key) {
|
|
27
|
+
return key + ' ' + val;
|
|
28
|
+
}).join(', ')}
|
|
29
|
+
</p>
|
|
30
|
+
<p className="m-0 text-sm">
|
|
31
|
+
Please cite:{' '}
|
|
32
|
+
<a href="https://doi.org/10.1093/molbev/msz185" className="text-seqblue hover:text-seqorange">
|
|
33
|
+
https://doi.org/10.1093/molbev/msz185
|
|
34
|
+
</a>
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return <ReportHeader name="Run Summary" renderContent={renderContent} />;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default RunSummary;
|