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.

Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/COPYRIGHT.txt +1 -1
  3. data/bin/sequenceserver +4 -2
  4. data/lib/sequenceserver/blast/error.rb +53 -0
  5. data/lib/sequenceserver/blast/job.rb +2 -43
  6. data/lib/sequenceserver/job.rb +21 -11
  7. data/lib/sequenceserver/makeblastdb-modified-with-cache.rb +345 -0
  8. data/lib/sequenceserver/makeblastdb.rb +26 -12
  9. data/lib/sequenceserver/routes.rb +29 -3
  10. data/lib/sequenceserver/server.rb +1 -1
  11. data/lib/sequenceserver/version.rb +1 -1
  12. data/lib/sequenceserver.rb +3 -0
  13. data/public/404.html +27 -0
  14. data/public/config.js +0 -6
  15. data/public/css/grapher.css +1 -1
  16. data/public/css/sequenceserver.css +22 -11
  17. data/public/css/sequenceserver.min.css +2 -2
  18. data/public/js/circos.js +7 -3
  19. data/public/js/dnd.js +3 -3
  20. data/public/js/fastq_to_fasta.js +35 -0
  21. data/public/js/form.js +30 -11
  22. data/public/js/grapher.js +123 -113
  23. data/public/js/hit.js +8 -2
  24. data/public/js/hits_overview.js +4 -1
  25. data/public/js/jquery_world.js +0 -1
  26. data/public/js/kablammo.js +4 -0
  27. data/public/js/length_distribution.js +5 -1
  28. data/public/js/null_plugins/download_links.js +7 -0
  29. data/public/js/null_plugins/hit_buttons.js +11 -0
  30. data/public/js/null_plugins/report_plugins.js +18 -0
  31. data/public/js/query.js +26 -6
  32. data/public/js/report.js +33 -17
  33. data/public/js/search.js +0 -8
  34. data/public/js/sidebar.js +11 -1
  35. data/public/js/tests/mock_data/sequences.js +18 -1
  36. data/public/js/tests/search_query.spec.js +12 -3
  37. data/public/sequenceserver-report.min.js +76 -42
  38. data/public/sequenceserver-search.min.js +34 -33
  39. data/views/layout.erb +9 -12
  40. 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(location.pathname + '.json' + location.search).complete(function (jqXHR) {
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
- component.setStateFromJSON(jqXHR.responseJSON);
75
+ callback(jqXHR.responseJSON);
71
76
  break;
72
- case 404:
73
77
  case 400:
78
+ case 422:
74
79
  case 500:
75
- component.props.showErrorModal(jqXHR.responseJSON);
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 or are rendered in each cycle, but we want to create the
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.number}
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
- query.number +
194
- '_Hit_' +
195
- hit.number +
196
- '_HSP_' +
197
- hsp.number
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 > .section-content > .table-hit-overview > .caption',
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
  });