sequenceserver 3.1.2 → 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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequenceserver/api_errors.rb +18 -0
  3. data/lib/sequenceserver/blast/hit.rb +1 -2
  4. data/lib/sequenceserver/blast/job.rb +15 -5
  5. data/lib/sequenceserver/blast/report.rb +5 -1
  6. data/lib/sequenceserver/blast/tasks.rb +1 -1
  7. data/lib/sequenceserver/database.rb +5 -1
  8. data/lib/sequenceserver/job.rb +26 -10
  9. data/lib/sequenceserver/makeblastdb.rb +2 -1
  10. data/lib/sequenceserver/routes.rb +38 -13
  11. data/lib/sequenceserver/version.rb +1 -1
  12. data/lib/sequenceserver/zip_file_generator.rb +1 -1
  13. data/lib/sequenceserver.rb +1 -1
  14. data/public/404.html +8 -16
  15. data/public/css/app.css +794 -111
  16. data/public/css/app.min.css +1 -1
  17. data/public/css/sequenceserver.min.css +1 -8
  18. data/public/js/alignment_exporter.js +16 -28
  19. data/public/js/circos.js +103 -96
  20. data/public/{packages/circosJS@1.7.0.js → js/circosjs.js} +156 -778
  21. data/public/js/cloud_share_modal.js +95 -98
  22. data/public/js/collapse_preferences.js +13 -15
  23. data/public/js/databases.js +4 -3
  24. data/public/js/databases_tree.js +51 -30
  25. data/public/js/error_modal.js +35 -22
  26. data/public/js/exporter.js +2 -0
  27. data/public/js/form.js +55 -17
  28. data/public/js/grapher.js +94 -92
  29. data/public/js/hit.js +24 -22
  30. data/public/js/hits.js +283 -0
  31. data/public/js/hits_overview.js +8 -27
  32. data/public/js/hsp.js +140 -144
  33. data/public/js/jquery_world.js +25 -16
  34. data/public/js/kablammo.js +14 -10
  35. data/public/js/length_distribution.js +23 -37
  36. data/public/js/mailto.js +1 -3
  37. data/public/js/null_plugins/report_plugins.js +4 -5
  38. data/public/js/options.js +19 -25
  39. data/public/js/query.js +36 -35
  40. data/public/js/report/alignment_results.js +18 -0
  41. data/public/js/report/graphical_overview.js +38 -0
  42. data/public/js/report/report_header.js +28 -0
  43. data/public/js/report/run_summary.js +43 -0
  44. data/public/js/report.js +124 -348
  45. data/public/js/report_root.js +10 -8
  46. data/public/js/search.js +31 -11
  47. data/public/js/search_button.js +85 -78
  48. data/public/js/sequence.js +169 -180
  49. data/public/js/sequence_modal.js +58 -65
  50. data/public/js/share_url.js +5 -5
  51. data/public/js/sidebar.js +152 -89
  52. data/public/js/svgExporter.js +8 -3
  53. data/public/js/tests/alignment_exporter.spec.js +38 -0
  54. data/public/js/tests/cloud_share_modal.spec.js +75 -0
  55. data/public/js/tests/form.spec.js +1 -1
  56. data/public/js/tests/report.spec.js +37 -15
  57. data/public/js/tests/visualisation_helpers.spec.js +28 -0
  58. data/public/js/visualisation_helpers.js +24 -27
  59. data/public/packages/jquery-ui@1.13.3.js +19070 -0
  60. data/public/sequenceserver-report.min.js +3 -2481
  61. data/public/sequenceserver-report.min.js.LICENSE.txt +213 -0
  62. data/public/sequenceserver-report.min.js.map +1 -0
  63. data/public/sequenceserver-search.min.js +3 -2382
  64. data/public/sequenceserver-search.min.js.LICENSE.txt +262 -0
  65. data/public/sequenceserver-search.min.js.map +1 -0
  66. data/views/error.erb +1 -1
  67. data/views/layout.erb +122 -133
  68. data/views/report.erb +3 -2
  69. data/views/search.erb +10 -14
  70. metadata +21 -28
  71. data/public/config.js +0 -147
  72. data/public/css/fonts.css +0 -31
  73. data/public/css/grapher.css +0 -670
  74. data/public/css/sequenceserver.css +0 -527
  75. data/public/packages/jquery-ui@1.11.4.js +0 -16624
  76. data/public/vendor/github/mbostock/d3@3.5.6/d3.js +0 -9506
  77. data/public/vendor/github/mbostock/d3@3.5.6.js +0 -1
  78. 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.scale.linear()
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.layout.histogram()
75
- .range(this._scale_x.domain())
76
- .bins(this._scale_x.ticks(50))(this._data);
77
- this._scale_y = d3.scale.linear()
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
- var ticks = this._scale_x.ticks();
108
- var format = d3.format('.1f');
109
- var prefix = d3.formatPrefix(ticks[ticks.length - 1]);
110
- var suffixes = {amino_acid: 'aa', nucleic_acid: 'bp'};
111
- return function (d) {
112
- if (d === 0) { return ; }
113
- if (_.indexOf(ticks,d) >= 0) {
114
- if (suffixes[seq_type] == 'aa') {
115
- return (d + ' ' + suffixes[seq_type]);
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
- return ;
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.x, dx: bin.dx, length: bin.length};
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].x) - self._scale_x(this._bins[0].x) - 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.svg.axis()
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.svg.axis()
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
- .outerTickSize(0)
208
+ .tickSizeOuter(0)
223
209
  .tickFormat(function (e) {
224
210
  if (Math.floor(e) != e) {
225
211
  return ;
data/public/js/mailto.js CHANGED
@@ -5,9 +5,7 @@ export default function asMailtoHref(querydb, program, numQueries, url, isOpenAc
5
5
  }
6
6
 
7
7
  function formatDatabases(querydb) {
8
- return querydb
9
- .slice(0, 15)
10
- .map(db => ' ' + db.title);
8
+ return querydb ? querydb.slice(0, 15).map(db => ' ' + db.title) : "";
11
9
  }
12
10
 
13
11
  function composeEmail(dbsArr, program, numQueries, url, isOpenAccess) {
@@ -1,18 +1,17 @@
1
1
  import Histogram from 'histogram';
2
+ import chroma from 'chroma-js';
2
3
 
3
4
  class ReportPlugins {
4
5
  constructor(parent) {
5
6
  this.parent = parent;
6
7
  }
7
8
 
8
- init() {
9
+ init(_callback) {
9
10
  }
10
11
 
11
- componentDidUpdate(_prevProps, _prevState) {
12
- }
13
12
 
14
- queryResults(_query) {
15
- return [];
13
+ queryResult(_query) {
14
+ return null;
16
15
  }
17
16
 
18
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
- <h3 className="w-full font-medium border-b border-seqorange mb-2">Settings</h3>
76
-
77
- <p className="text-sm">Choose a predefined setting or customize BLAST parameters.</p>
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-sm my-1">
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"
@@ -110,39 +111,29 @@ export class Options extends Component {
110
111
  }
111
112
 
112
113
  advancedParamsJSX() {
113
- if (this.state.paramsMode !== 'advanced') {
114
- return null;
115
- }
116
-
117
- let classNames = 'flex-grow block px-4 py-1 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 text-base';
114
+ let classNames = 'flex-grow block px-4 py-1 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 text-base font-mono';
118
115
 
119
116
  if (this.state.textValue) {
120
117
  classNames += ' bg-yellow-100';
121
118
  }
122
119
 
123
120
  return(
124
- <div className="w-full">
121
+ <div className={this.state.paramsMode !== 'advanced' ? 'w-full hidden' : 'w-full'}>
125
122
  <div className="flex items-end">
126
- <label className="flex items-center" htmlFor="advanced">
127
- Advanced parameters
123
+ <label className="flex items-center text-base md:text-lg" htmlFor="advanced">
124
+ Parameters:
128
125
  </label>
129
126
 
130
127
  {this.props.blastMethod &&
131
128
  <button
132
- className="text-seqblue ml-2"
129
+ className="text-seqblue text-base md:text-lg ml-2"
133
130
  type="button"
134
131
  onClick={this.showAdvancedOptionsHelp}
135
132
  data-toggle="modal" data-target="#help">
136
- See available options
133
+ See options
137
134
  <i className="fa fa-question-circle ml-1 w-3 h-4 fill-current"></i>
138
135
  </button>
139
136
  }
140
-
141
- {!this.props.blastMethod &&
142
- <span className="text-gray-600 ml-2 text-sm hidden sm:block">
143
- Select databases and fill in the query to see options.
144
- </span>
145
- }
146
137
  </div>
147
138
 
148
139
  <div className='flex-grow flex w-full'>
@@ -155,8 +146,8 @@ export class Options extends Component {
155
146
  title="View, and enter advanced parameters."
156
147
  />
157
148
  </div>
158
- <div className="text-sm text-gray-600 mt-2">
159
- Options as they would appear in a command line when calling BLAST eg: <i>-evalue 1.0e-5 -num_alignments 100</i>
149
+ <div className="mt-1 text-gray-700">
150
+ Options as they would appear in a command-line.
160
151
  </div>
161
152
  </div>
162
153
  )
@@ -165,15 +156,18 @@ export class Options extends Component {
165
156
  showAdvancedOptionsHelp(e) {
166
157
  const ids = ['blastn', 'tblastn', 'blastp', 'blastx', 'tblastx'];
167
158
  const method = this.props.blastMethod.toLowerCase();
159
+ const modal = document.querySelector("dialog.advanced-modal");
160
+
168
161
  // hide options for other algorithms and only show for selected algorithm
169
162
  for (const id of ids) {
170
163
  if (id === method) {
171
- document.getElementById(id).classList.remove('hidden')
164
+ document.getElementById(id).classList.remove('hidden');
172
165
  } else {
173
166
  document.getElementById(id).classList.add('hidden');
174
167
  }
175
168
  }
176
- document.querySelector('[data-help-modal]').classList.remove('hidden')
169
+
170
+ modal.showModal();
177
171
  }
178
172
 
179
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 = `length: ${this.queryLength().toLocaleString()}`;
48
+ var meta = `Length: ${this.queryLength().toLocaleString()}`;
49
+
48
50
  if (this.props.showQueryCrumbs) {
49
- meta = `query ${this.props.query.number}, ` + meta;
51
+ meta = `query ${this.props.query.number}. ${meta}`;
50
52
  }
51
- return <div className="section-header">
52
- <h3>
53
- <strong>Query=&nbsp;{this.props.query.id}</strong>&nbsp;
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 label-reset pos-label">{meta}</span>
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="section-content">
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="section-content">
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-base text-monospace"
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 width={`${seqwidth}%`}>Similar sequences</th>
416
- {hasName && <th width="15%" className="text-left">Species</th>}
417
- {!this.props.imported_xml && <th width="15%" className="text-right">Query coverage (%)</th>}
418
- <th width="10%" className="text-right">Total score</th>
419
- <th width="10%" className="text-right">E value</th>
420
- <th width="10%" className="text-right">Identity (%)</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 className="nowrap-ellipsis"
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="btn-link">{hit.id} {hit.title}</a>
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="nowrap-ellipsis" title={hit.sciname}
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="table-hit-overview">
456
- <h4 className="caption" onClick={() => this.collapsePreferences.toggleCollapse()}>
457
- {this.collapsePreferences.renderCollapseIcon()}
458
- <span> {this.name}</span>
459
- </h4>
460
- <div id={'Query_' + this.props.query.number + 'HT_' + this.props.query.number}>
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,&nbsp;
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;