sequenceserver 2.2.0 → 3.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.
Potentially problematic release.
This version of sequenceserver might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/COPYRIGHT.txt +1 -1
- data/bin/sequenceserver +4 -2
- data/lib/sequenceserver/blast/error.rb +53 -0
- data/lib/sequenceserver/blast/job.rb +2 -43
- data/lib/sequenceserver/job.rb +21 -11
- data/lib/sequenceserver/makeblastdb-modified-with-cache.rb +345 -0
- data/lib/sequenceserver/makeblastdb.rb +26 -12
- data/lib/sequenceserver/routes.rb +29 -3
- data/lib/sequenceserver/server.rb +1 -1
- data/lib/sequenceserver/version.rb +1 -1
- data/lib/sequenceserver.rb +3 -0
- data/public/404.html +27 -0
- data/public/config.js +0 -6
- data/public/css/grapher.css +1 -1
- data/public/css/sequenceserver.css +22 -11
- data/public/css/sequenceserver.min.css +2 -2
- data/public/js/circos.js +7 -3
- data/public/js/dnd.js +3 -3
- data/public/js/fastq_to_fasta.js +35 -0
- data/public/js/form.js +30 -11
- data/public/js/grapher.js +123 -113
- data/public/js/hit.js +8 -2
- data/public/js/hits_overview.js +4 -1
- data/public/js/jquery_world.js +0 -1
- data/public/js/kablammo.js +4 -0
- data/public/js/length_distribution.js +5 -1
- data/public/js/null_plugins/download_links.js +7 -0
- data/public/js/null_plugins/hit_buttons.js +11 -0
- data/public/js/null_plugins/report_plugins.js +18 -0
- data/public/js/query.js +26 -6
- data/public/js/report.js +33 -17
- data/public/js/search.js +0 -8
- data/public/js/sidebar.js +11 -1
- data/public/js/tests/mock_data/sequences.js +18 -1
- data/public/js/tests/search_query.spec.js +12 -3
- data/public/sequenceserver-report.min.js +76 -42
- data/public/sequenceserver-search.min.js +34 -33
- data/views/layout.erb +9 -12
- metadata +32 -23
data/public/js/report.js
CHANGED
@@ -8,15 +8,14 @@ import { ReportQuery } from './query';
|
|
8
8
|
import Hit from './hit';
|
9
9
|
import HSP from './hsp';
|
10
10
|
import AlignmentExporter from './alignment_exporter';
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
import ReportPlugins from 'report_plugins';
|
14
12
|
|
15
13
|
/**
|
16
14
|
* Renders entire report.
|
17
15
|
*
|
18
16
|
* Composed of Query and Sidebar components.
|
19
17
|
*/
|
18
|
+
|
20
19
|
class Report extends Component {
|
21
20
|
constructor(props) {
|
22
21
|
super(props);
|
@@ -47,15 +46,21 @@ class Report extends Component {
|
|
47
46
|
this.prepareAlignmentOfSelectedHits = this.prepareAlignmentOfSelectedHits.bind(this);
|
48
47
|
this.prepareAlignmentOfAllHits = this.prepareAlignmentOfAllHits.bind(this);
|
49
48
|
this.setStateFromJSON = this.setStateFromJSON.bind(this);
|
49
|
+
this.plugins = new ReportPlugins(this);
|
50
50
|
}
|
51
|
+
|
51
52
|
/**
|
52
53
|
* Fetch results.
|
53
54
|
*/
|
54
55
|
fetchResults() {
|
56
|
+
const path = location.pathname + '.json' + location.search;
|
57
|
+
this.pollPeriodically(path, this.setStateFromJSON, this.props.showErrorModal);
|
58
|
+
}
|
59
|
+
|
60
|
+
pollPeriodically(path, callback, errCallback) {
|
55
61
|
var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
|
56
|
-
var component = this;
|
57
62
|
function poll() {
|
58
|
-
$.getJSON(
|
63
|
+
$.getJSON(path).complete(function (jqXHR) {
|
59
64
|
switch (jqXHR.status) {
|
60
65
|
case 202:
|
61
66
|
var interval;
|
@@ -67,12 +72,12 @@ class Report extends Component {
|
|
67
72
|
setTimeout(poll, interval);
|
68
73
|
break;
|
69
74
|
case 200:
|
70
|
-
|
75
|
+
callback(jqXHR.responseJSON);
|
71
76
|
break;
|
72
|
-
case 404:
|
73
77
|
case 400:
|
78
|
+
case 422:
|
74
79
|
case 500:
|
75
|
-
|
80
|
+
errCallback(jqXHR.responseJSON);
|
76
81
|
break;
|
77
82
|
}
|
78
83
|
});
|
@@ -93,6 +98,7 @@ class Report extends Component {
|
|
93
98
|
this.setState(responseJSON, this.prepareAlignmentOfAllHits);
|
94
99
|
}
|
95
100
|
}
|
101
|
+
|
96
102
|
/**
|
97
103
|
* Called as soon as the page has loaded and the user sees the loading spinner.
|
98
104
|
* We use this opportunity to setup services that make use of delegated events
|
@@ -100,6 +106,7 @@ class Report extends Component {
|
|
100
106
|
*/
|
101
107
|
componentDidMount() {
|
102
108
|
this.fetchResults();
|
109
|
+
this.plugins.init();
|
103
110
|
// This sets up an event handler which enables users to select text from
|
104
111
|
// hit header without collapsing the hit.
|
105
112
|
this.preventCollapseOnSelection();
|
@@ -112,7 +119,7 @@ class Report extends Component {
|
|
112
119
|
* and circos would have been rendered at this point. At this stage we kick
|
113
120
|
* start iteratively adding 1 HSP to the page every 25 milli-seconds.
|
114
121
|
*/
|
115
|
-
componentDidUpdate() {
|
122
|
+
componentDidUpdate(prevProps, prevState) {
|
116
123
|
// Log to console how long the last update take?
|
117
124
|
// console.log((Date.now() - this.lastTimeStamp) / 1000);
|
118
125
|
|
@@ -130,6 +137,8 @@ class Report extends Component {
|
|
130
137
|
} else {
|
131
138
|
this.componentFinishedUpdating();
|
132
139
|
}
|
140
|
+
|
141
|
+
this.plugins.componentDidUpdate(prevProps, prevState);
|
133
142
|
}
|
134
143
|
|
135
144
|
/**
|
@@ -140,13 +149,14 @@ class Report extends Component {
|
|
140
149
|
var numHSPsProcessed = 0;
|
141
150
|
while (this.nextQuery < this.state.queries.length) {
|
142
151
|
var query = this.state.queries[this.nextQuery];
|
152
|
+
|
143
153
|
// We may see a query multiple times during rendering because only
|
144
|
-
// 3 hsps
|
154
|
+
// 3 hsps are rendered in each cycle, but we want to create the
|
145
155
|
// corresponding Query component only the first time we see it.
|
146
156
|
if (this.nextHit == 0 && this.nextHSP == 0) {
|
147
157
|
results.push(
|
148
158
|
<ReportQuery
|
149
|
-
key={'Query_' + query.
|
159
|
+
key={'Query_' + query.id}
|
150
160
|
query={query}
|
151
161
|
program={this.state.program}
|
152
162
|
querydb={this.state.querydb}
|
@@ -156,6 +166,8 @@ class Report extends Component {
|
|
156
166
|
veryBig={this.state.veryBig}
|
157
167
|
/>
|
158
168
|
);
|
169
|
+
|
170
|
+
results.push(...this.plugins.queryResults(query));
|
159
171
|
}
|
160
172
|
|
161
173
|
while (this.nextHit < query.hits.length) {
|
@@ -190,11 +202,11 @@ class Report extends Component {
|
|
190
202
|
<HSP
|
191
203
|
key={
|
192
204
|
'Query_' +
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
205
|
+
query.number +
|
206
|
+
'_Hit_' +
|
207
|
+
hit.number +
|
208
|
+
'_HSP_' +
|
209
|
+
hsp.number
|
198
210
|
}
|
199
211
|
query={query}
|
200
212
|
hit={hit}
|
@@ -261,6 +273,9 @@ class Report extends Component {
|
|
261
273
|
<br />
|
262
274
|
You can bookmark the page and come back to it later or share the
|
263
275
|
link with someone.
|
276
|
+
<br />
|
277
|
+
<br />
|
278
|
+
{ process.env.targetEnv === 'cloud' && <b>If the job takes more than 10 minutes to complete, we will send you an email upon completion.</b> }
|
264
279
|
</p>
|
265
280
|
</div>
|
266
281
|
</div>
|
@@ -451,7 +466,7 @@ class Report extends Component {
|
|
451
466
|
toggleTable() {
|
452
467
|
$('body').on(
|
453
468
|
'mousedown',
|
454
|
-
'.resultn
|
469
|
+
'.resultn .caption[data-toggle="collapse"]',
|
455
470
|
function (event) {
|
456
471
|
var $this = $(this);
|
457
472
|
$this.on('mouseup mousemove', function handler(event) {
|
@@ -509,6 +524,7 @@ class Report extends Component {
|
|
509
524
|
} else {
|
510
525
|
$hit.removeClass('glow');
|
511
526
|
$hit.next('.hsp').removeClass('glow');
|
527
|
+
$('.download-fasta-of-selected').attr('href', '#').removeAttr('download');
|
512
528
|
}
|
513
529
|
|
514
530
|
var $a = $('.download-fasta-of-selected');
|
data/public/js/search.js
CHANGED
@@ -3,14 +3,6 @@ import React, { Component } from "react";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
4
4
|
import { DnD } from "./dnd";
|
5
5
|
import { Form } from "./form";
|
6
|
-
/**
|
7
|
-
* Load necessary polyfills.
|
8
|
-
*/
|
9
|
-
$.webshims.setOptions(
|
10
|
-
"basePath",
|
11
|
-
"/vendor/npm/webshim@1.15.8/js-webshim/minified/shims/"
|
12
|
-
);
|
13
|
-
$.webshims.polyfill("forms");
|
14
6
|
|
15
7
|
/**
|
16
8
|
* Clear sessionStorage on reload.
|
data/public/js/sidebar.js
CHANGED
@@ -4,7 +4,7 @@ import _ from 'underscore';
|
|
4
4
|
import downloadFASTA from './download_fasta';
|
5
5
|
import asMailtoHref from './mailto';
|
6
6
|
import CloudShareModal from './cloud_share_modal';
|
7
|
-
|
7
|
+
import DownloadLinks from 'download_links';
|
8
8
|
/**
|
9
9
|
* checks whether code is being run by jest
|
10
10
|
*/
|
@@ -172,6 +172,9 @@ export default class extends Component {
|
|
172
172
|
var sequence_ids = $('.hit-links :checkbox:checked').map(function () {
|
173
173
|
return this.value;
|
174
174
|
}).get();
|
175
|
+
if (sequence_ids.length === 0) {
|
176
|
+
return false;
|
177
|
+
}
|
175
178
|
var database_ids = _.map(this.props.data.querydb, _.iteratee('id'));
|
176
179
|
downloadFASTA(sequence_ids, database_ids);
|
177
180
|
return false;
|
@@ -347,6 +350,7 @@ export default class extends Component {
|
|
347
350
|
</a>
|
348
351
|
</li>
|
349
352
|
}
|
353
|
+
<DownloadLinks imported_xml={this.props.data.imported_xml} search_id={this.props.data.search_id} />
|
350
354
|
</ul>
|
351
355
|
</div>
|
352
356
|
);
|
@@ -406,6 +410,12 @@ export default class extends Component {
|
|
406
410
|
{this.topPanelJSX()}
|
407
411
|
{this.downloadsPanelJSX()}
|
408
412
|
{this.sharingPanelJSX()}
|
413
|
+
<div className="referral-panel">
|
414
|
+
<div className="section-header-sidebar">
|
415
|
+
<h4>Recommend SequenceServer</h4>
|
416
|
+
<p><a href="https://sequenceserver.com/referral-program" target="_blank">Earn up to $100 per signup</a></p>
|
417
|
+
</div>
|
418
|
+
</div>
|
409
419
|
</div>
|
410
420
|
);
|
411
421
|
}
|
@@ -29,4 +29,21 @@ GAGATGGAAATGGCCGATTACCCGCTCGCCTATGATATTTCCCCGTATCTTCCGCCGTTC
|
|
29
29
|
CTGTCGCGAGCGAGGGCACGGGGAATGTTAGACGGTCGCTTCGCCGGCAGACGCTACCGA
|
30
30
|
AGGGAGTCGCGGGGCATTCACGAGGAGTGTTGCATCAACGGATGTACGATAAACGAATTG
|
31
31
|
ACCAGCTACTGCGGCCCC
|
32
|
-
`;
|
32
|
+
`;
|
33
|
+
|
34
|
+
export const FASTQ_SEQUENCE =
|
35
|
+
`@SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
|
36
|
+
GGGTGATGGCCGCTGCCGATGGCGTCAAATCCCACCAAGTTACCCTTAACAACTTAAGGGTTTTCAAATAGA
|
37
|
+
+SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
|
38
|
+
IIIIIIIIIIIIIIIIIIIIIIIIIIIIII9IG9ICIIIIIIIIIIIIIIIIIIIIDIIIIIII>IIIIII/
|
39
|
+
@SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
|
40
|
+
GTTCAGGGATACGACGTTTGTATTTTAAGAATCTGAAGCAGAAGTCGATGATAATACGCGTCGTTTTATCAT
|
41
|
+
+SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
|
42
|
+
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII6IBIIIIIIIIIIIIIIIIIIIIIIIGII>IIIII-I)8I
|
43
|
+
`;
|
44
|
+
|
45
|
+
export const FASTA_OF_FASTQ_SEQUENCE =
|
46
|
+
`>SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
|
47
|
+
GGGTGATGGCCGCTGCCGATGGCGTCAAATCCCACCAAGTTACCCTTAACAACTTAAGGGTTTTCAAATAGA
|
48
|
+
>SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
|
49
|
+
GTTCAGGGATACGACGTTTGTATTTTAAGAATCTGAAGCAGAAGTCGATGATAATACGCGTCGTTTTATCAT`;
|
@@ -3,7 +3,7 @@
|
|
3
3
|
import { render, screen, fireEvent } from '@testing-library/react';
|
4
4
|
import { SearchQueryWidget } from '../query';
|
5
5
|
import { Form } from '../form';
|
6
|
-
import { AMINO_ACID_SEQUENCE, NUCLEOTIDE_SEQUENCE } from './mock_data/sequences';
|
6
|
+
import { AMINO_ACID_SEQUENCE, NUCLEOTIDE_SEQUENCE, FASTQ_SEQUENCE, FASTA_OF_FASTQ_SEQUENCE } from './mock_data/sequences';
|
7
7
|
import '@testing-library/jest-dom/extend-expect';
|
8
8
|
import '@testing-library/react/dont-cleanup-after-each';
|
9
9
|
|
@@ -16,7 +16,7 @@ describe('SEARCH COMPONENT', () => {
|
|
16
16
|
} />).container;
|
17
17
|
inputEl = screen.getByRole('textbox', { name: '' });
|
18
18
|
});
|
19
|
-
|
19
|
+
|
20
20
|
test('should render the search component textarea', () => {
|
21
21
|
expect(inputEl).toHaveClass('form-control');
|
22
22
|
});
|
@@ -47,7 +47,7 @@ describe('SEARCH COMPONENT', () => {
|
|
47
47
|
expect(activeNotification.id).toBe('nucleotide-sequence-notification');
|
48
48
|
expect(alertWrapper).toHaveTextContent('Detected: nucleotide sequence(s).');
|
49
49
|
});
|
50
|
-
|
50
|
+
|
51
51
|
test('should correctly detect the mixed sequences and show error notification', () => {
|
52
52
|
fireEvent.change(inputEl, { target: { value: `${NUCLEOTIDE_SEQUENCE}${AMINO_ACID_SEQUENCE}` } });
|
53
53
|
const activeNotification = container.querySelector('.notification.active');
|
@@ -55,4 +55,13 @@ describe('SEARCH COMPONENT', () => {
|
|
55
55
|
const alertWrapper = activeNotification.children[0];
|
56
56
|
expect(alertWrapper).toHaveTextContent('Error: mixed nucleotide and amino-acid sequences detected.');
|
57
57
|
});
|
58
|
+
|
59
|
+
test('should correctly detect FASTQ and convert it to FASTA', () => {
|
60
|
+
fireEvent.change(inputEl, { target: { value: FASTQ_SEQUENCE } });
|
61
|
+
const activeNotification = container.querySelector('.notification.active');
|
62
|
+
const alertWrapper = activeNotification.children[0];
|
63
|
+
expect(activeNotification.id).toBe('fastq-sequence-notification');
|
64
|
+
expect(alertWrapper).toHaveTextContent('Detected FASTQ and automatically converted to FASTA.');
|
65
|
+
expect(inputEl).toHaveValue(FASTA_OF_FASTQ_SEQUENCE);
|
66
|
+
});
|
58
67
|
});
|