sequenceserver 2.0.0.beta4 → 2.0.0.rc5

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 (119) hide show
  1. checksums.yaml +5 -5
  2. data/.dockerignore +1 -0
  3. data/.travis.yml +7 -4
  4. data/AppImage/sequenceserver.sh +5 -0
  5. data/Dockerfile +14 -12
  6. data/bin/sequenceserver +37 -28
  7. data/lib/sequenceserver.rb +35 -7
  8. data/lib/sequenceserver/blast/job.rb +18 -25
  9. data/lib/sequenceserver/blast/report.rb +68 -34
  10. data/lib/sequenceserver/config.rb +1 -1
  11. data/lib/sequenceserver/database.rb +0 -129
  12. data/lib/sequenceserver/makeblastdb.rb +243 -0
  13. data/lib/sequenceserver/routes.rb +28 -2
  14. data/lib/sequenceserver/version.rb +1 -1
  15. data/public/SequenceServer_logo.png +0 -0
  16. data/public/css/grapher.css +8 -15
  17. data/public/css/sequenceserver.css +119 -55
  18. data/public/css/sequenceserver.min.css +3 -3
  19. data/public/js/circos.js +1 -1
  20. data/public/js/download_fasta.js +17 -0
  21. data/public/js/grapher.js +7 -9
  22. data/public/js/hit.js +217 -0
  23. data/public/js/hits_overview.js +12 -13
  24. data/public/js/hsp.js +104 -84
  25. data/public/js/{sequenceserver.js → jquery_world.js} +1 -18
  26. data/public/js/kablammo.js +337 -334
  27. data/public/js/length_distribution.js +1 -1
  28. data/public/js/query.js +147 -0
  29. data/public/js/report.js +216 -836
  30. data/public/js/search.js +194 -192
  31. data/public/js/sequence_modal.js +167 -0
  32. data/public/js/sidebar.js +210 -0
  33. data/public/js/utils.js +2 -19
  34. data/public/js/visualisation_helpers.js +2 -2
  35. data/public/sequenceserver-report.min.js +19 -19
  36. data/public/sequenceserver-search.min.js +11 -11
  37. data/public/vendor/github/twbs/bootstrap@3.3.5/js/bootstrap.js +2 -2
  38. data/spec/blast_versions/blast_2.2.30/import_spec_capybara_local_2.2.30.rb +15 -15
  39. data/spec/blast_versions/blast_2.2.31/import_spec_capybara_local_2.2.31.rb +15 -15
  40. data/spec/blast_versions/blast_2.3.0/import_spec_capybara_local_2.3.0.rb +15 -15
  41. data/spec/blast_versions/blast_2.4.0/import_spec_capybara_local_2.4.0.rb +15 -15
  42. data/spec/blast_versions/blast_2.5.0/import_spec_capybara_local_2.5.0.rb +15 -15
  43. data/spec/blast_versions/blast_2.6.0/import_spec_capybara_local_2.6.0.rb +15 -15
  44. data/spec/blast_versions/blast_2.7.1/import_spec_capybara_local_2.7.1.rb +15 -15
  45. data/spec/blast_versions/blast_2.8.1/import_spec_capybara_local_2.8.1.rb +15 -15
  46. data/spec/blast_versions/blast_2.9.0/import_spec_capybara_local_2.9.0.rb +15 -15
  47. data/spec/blast_versions/diamond_0.9.24/import_spec_capybara_local_0.9.24.rb +6 -6
  48. data/spec/capybara_spec.rb +14 -3
  49. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.ndb +0 -0
  50. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhr +0 -0
  51. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nin +0 -0
  52. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nos +0 -0
  53. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.not +0 -0
  54. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.ntf +0 -0
  55. data/spec/database/sample/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nto +0 -0
  56. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pdb +0 -0
  57. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phr +0 -0
  58. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pin +0 -0
  59. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pos +0 -0
  60. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pot +0 -0
  61. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.ptf +0 -0
  62. data/spec/database/sample/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pto +0 -0
  63. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pdb +0 -0
  64. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phr +0 -0
  65. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pin +0 -0
  66. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pos +0 -0
  67. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pot +0 -0
  68. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.ptf +0 -0
  69. data/spec/database/sample/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pto +0 -0
  70. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.ndb +0 -0
  71. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhr +0 -0
  72. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nin +0 -0
  73. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nos +0 -0
  74. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.not +0 -0
  75. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsq +0 -0
  76. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.ntf +0 -0
  77. data/spec/database/sample/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nto +0 -0
  78. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhd +8 -0
  79. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhi +0 -0
  80. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nhr +0 -0
  81. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nin +0 -0
  82. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nog +0 -0
  83. data/spec/database/{sample → v4}/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nsd +0 -0
  84. data/spec/database/{sample → v4}/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nsi +0 -0
  85. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.fasta.nsq +0 -0
  86. data/spec/database/v4/genome/Solenopsis_invicta/Solenopsis_invicta_gnG_subset.txt +8 -0
  87. data/spec/database/v4/links.rb +23 -0
  88. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta +6449 -0
  89. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phd +1189 -0
  90. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phi +0 -0
  91. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.phr +0 -0
  92. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pin +0 -0
  93. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.pog +0 -0
  94. data/spec/database/{sample → v4}/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.psd +0 -0
  95. data/spec/database/{sample → v4}/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.psi +0 -0
  96. data/spec/database/v4/proteins/Solenopsis_invicta/Sinvicta2-2-3.prot.subset.fasta.psq +0 -0
  97. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phd +9140 -0
  98. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phi +0 -0
  99. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.phr +0 -0
  100. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pin +0 -0
  101. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.pog +0 -0
  102. data/spec/database/{sample → v4}/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.psd +0 -0
  103. data/spec/database/{sample → v4}/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.psi +0 -0
  104. data/spec/database/v4/proteins/uniprot/2018-04-Swiss-Prot_insecta.fasta.psq +0 -0
  105. data/spec/database/v4/proteins/uniprot/URL +1 -0
  106. data/spec/database/v4/si_uniprot_idmap.yml +14180 -0
  107. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta +5486 -0
  108. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhd +473 -0
  109. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhi +0 -0
  110. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nhr +0 -0
  111. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nin +0 -0
  112. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nog +0 -0
  113. data/spec/database/{sample → v4}/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsd +0 -0
  114. data/spec/database/{sample → v4}/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsi +0 -0
  115. data/spec/database/v4/transcripts/Solenopsis_invicta/Sinvicta2-2-3.cdna.subset.fasta.nsq +0 -0
  116. data/spec/database_spec.rb +0 -76
  117. data/spec/makeblastdb_spec.rb +121 -0
  118. data/views/layout.erb +5 -1
  119. metadata +75 -15
@@ -0,0 +1,217 @@
1
+ import React from 'react';
2
+ import _ from 'underscore';
3
+
4
+ import HSPOverview from './kablammo';
5
+ import downloadFASTA from './download_fasta';
6
+ import AlignmentExporter from './alignment_exporter'; // to download textual alignment
7
+
8
+ /**
9
+ * Component for each hit. Receives props from Report. Has no state.
10
+ */
11
+ export default React.createClass({
12
+ /**
13
+ * Returns accession number of the hit sequence.
14
+ */
15
+ accession: function () {
16
+ return this.props.hit.accession;
17
+ },
18
+
19
+ /**
20
+ * Returns length of the hit sequence.
21
+ */
22
+ hitLength: function () {
23
+ return this.props.hit.length;
24
+ },
25
+
26
+ // Internal helpers. //
27
+
28
+ /**
29
+ * Returns id that will be used for the DOM node corresponding to the hit.
30
+ */
31
+ domID: function () {
32
+ return 'Query_' + this.props.query.number + '_hit_' + this.props.hit.number;
33
+ },
34
+
35
+ databaseIDs: function () {
36
+ return _.map(this.props.querydb, _.iteratee('id'));
37
+ },
38
+
39
+ showSequenceViewer: function () {
40
+ this.props.showSequenceModal(this.viewSequenceLink());
41
+ },
42
+
43
+ viewSequenceLink: function () {
44
+ return encodeURI(`get_sequence/?sequence_ids=${this.accession()}&database_ids=${this.databaseIDs()}`);
45
+ },
46
+
47
+ downloadFASTA: function (event) {
48
+ var accessions = [this.accession()];
49
+ downloadFASTA(accessions, this.databaseIDs());
50
+ },
51
+
52
+ // Event-handler for exporting alignments.
53
+ // Calls relevant method on AlignmentExporter defined in alignment_exporter.js.
54
+ downloadAlignment: function (event) {
55
+ var hsps = _.map(this.props.hit.hsps, _.bind(function (hsp) {
56
+ hsp.query_id = this.props.query.id;
57
+ hsp.hit_id = this.props.hit.id;
58
+ return hsp;
59
+ }, this));
60
+
61
+ var aln_exporter = new AlignmentExporter();
62
+ aln_exporter.export_alignments(hsps, this.props.query.id+'_'+this.props.hit.id);
63
+ },
64
+
65
+
66
+ // Life cycle methods //
67
+
68
+ render: function () {
69
+ return (
70
+ <div className="hit" id={this.domID()} data-hit-def={this.props.hit.id}
71
+ data-hit-len={this.props.hit.length} data-hit-evalue={this.props.hit.evalue}>
72
+ { this.headerJSX() } { this.contentJSX() }
73
+ </div>
74
+ );
75
+ },
76
+
77
+ // See Query.shouldComponentUpdate. The same applies for hits.
78
+ shouldComponentUpdate: function () {
79
+ return !this.props.hit;
80
+ },
81
+
82
+ headerJSX: function () {
83
+ var meta = `length: ${this.hitLength().toLocaleString()}`;
84
+
85
+ if (this.props.showQueryCrumbs && this.props.showHitCrumbs) {
86
+ // Multiper queries, multiple hits
87
+ meta = `hit ${this.props.hit.number} of query ${this.props.query.number}, ` + meta;
88
+ }
89
+ else if (this.props.showQueryCrumbs && !this.props.showHitCrumbs) {
90
+ // Multiple queries, single hit
91
+ meta = `the only hit of query ${this.props.query.number}, ` + meta;
92
+ }
93
+ else if (!this.props.showQueryCrumbs && this.props.showHitCrumbs) {
94
+ // Single query, multiple hits
95
+ meta = `hit ${this.props.hit.number}, ` + meta;
96
+ }
97
+
98
+ return <div className="section-header">
99
+ <h4>
100
+ <i className="fa fa-minus-square-o"></i>&nbsp;
101
+ <strong>{this.props.hit.id}</strong>&nbsp;
102
+ {this.props.hit.title}
103
+ </h4>
104
+ <span className="label label-reset pos-label">{meta}</span>
105
+ </div>;
106
+ },
107
+
108
+ contentJSX: function () {
109
+ return <div className="section-content" data-parent-hit={this.domID()}>
110
+ { this.hitLinks() }
111
+ <HSPOverview key={'kablammo' + this.props.query.id} query={this.props.query}
112
+ hit={this.props.hit} algorithm={this.props.algorithm}
113
+ showHSPCrumbs={this.props.hit.hsps.length > 1}
114
+ collapsed={this.props.veryBig} />
115
+ </div>;
116
+ },
117
+
118
+ hitLinks: function () {
119
+ var btns = [];
120
+ if (!this.props.imported_xml) {
121
+ btns = btns.concat([
122
+ this.viewSequenceButton(),
123
+ this.downloadFASTAButton()
124
+ ]);
125
+ }
126
+ btns.push(this.downloadAlignmentButton());
127
+
128
+ return (
129
+ <div className="hit-links">
130
+ <label>
131
+ <input type="checkbox" id={this.domID() + '_checkbox'}
132
+ value={this.accession()} onChange={function () {
133
+ this.props.selectHit(this.domID() + '_checkbox');
134
+ }.bind(this)} data-target={'#' + this.domID()}
135
+ /> Select
136
+ </label>
137
+ {
138
+ btns.map((btn) => {
139
+ return [<span className="line">|</span>, this.button(btn)];
140
+ })
141
+ }
142
+ {
143
+ this.props.hit.links.map((link) => {
144
+ return [<span className="line">|</span>, this.a(link)];
145
+ })
146
+ }
147
+ </div>
148
+ );
149
+ },
150
+
151
+ // Return JSX for view sequence button.
152
+ viewSequenceButton: function () {
153
+ if (this.hitLength() > 10000) {
154
+ return {
155
+ text: 'Sequence',
156
+ icon: 'fa-eye',
157
+ className: 'view-sequence',
158
+ title: 'Sequence too long',
159
+ };
160
+ }
161
+ else {
162
+ return {
163
+ text: 'Sequence',
164
+ icon: 'fa-eye',
165
+ className: 'view-sequence',
166
+ onClick: () => this.showSequenceViewer()
167
+ };
168
+
169
+ }
170
+ },
171
+
172
+ downloadFASTAButton: function () {
173
+ return {
174
+ text: 'FASTA',
175
+ icon: 'fa-download',
176
+ className: 'download-fa',
177
+ onClick: () => this.downloadFASTA()
178
+ };
179
+ },
180
+
181
+ downloadAlignmentButton: function () {
182
+ return {
183
+ text: 'Alignment',
184
+ icon: 'fa-download',
185
+ className: 'download-aln',
186
+ onClick: () => this.downloadAlignment()
187
+ };
188
+ },
189
+
190
+ button: function ({text, icon, title, className, onClick}) {
191
+ if (onClick) {
192
+ return <button className={`btn-link ${className}`}
193
+ title={title} onClick={onClick}><i className={`fa ${icon}`}></i> {text}
194
+ </button>;
195
+ }
196
+ else {
197
+ return <button className="btn-link view-sequence disabled"
198
+ title={title} disabled="true">
199
+ <i className={`fa ${icon}`}></i> {text}
200
+ </button>;
201
+ }
202
+ },
203
+
204
+ /**
205
+ * Render URL for sequence-viewer.
206
+ */
207
+ a: function (link) {
208
+ if (!link.title || !link.url) return;
209
+
210
+ let className = 'btn btn-link';
211
+ if (link.class) className = `${className} ${link.class}`;
212
+ return <a href={link.url} className={className} target='_blank'>
213
+ {link.icon && <i className={'fa ' + link.icon}></i>}
214
+ {' ' + link.title + ' '}
215
+ </a>;
216
+ }
217
+ });
@@ -166,10 +166,10 @@ class Graph {
166
166
  drawLegend(svg, options, width, height, hits) {
167
167
  var svg_legend = svg.append('g')
168
168
  .attr('transform',
169
- 'translate(0,' + (height - 1.88 * options.margin) + ')');
169
+ 'translate(0,' + (height - 1.75 * options.margin) + ')');
170
170
 
171
171
  svg_legend.append('rect')
172
- .attr('x', 7 * (width - 2 * options.margin) / 10)
172
+ .attr('x', 7.5 * (width - 2 * options.margin) / 10)
173
173
  .attr('width', 2 * (width - 4 * options.margin) / 10)
174
174
  .attr('height', options.legend)
175
175
  .attr('fill', 'url(#legend-grad)');
@@ -177,7 +177,7 @@ class Graph {
177
177
  svg_legend.append('text')
178
178
  .attr('class',' legend-text')
179
179
  .attr('transform', 'translate(0, ' +options.legend +')')
180
- .attr('x', 6 * (width - 2 * options.margin) / 10 - options.margin / 2)
180
+ .attr('x', 9.5 * (width - 2 * options.margin) / 10 + options.margin / 2)
181
181
  .text('Weaker hits');
182
182
  // .text(function() {
183
183
  // return Helpers.prettify_evalue(hits[hits.length-1].hitEvalue);
@@ -186,7 +186,7 @@ class Graph {
186
186
  svg_legend.append('text')
187
187
  .attr('class',' legend-text')
188
188
  .attr('transform', 'translate(0, ' + options.legend + ')')
189
- .attr('x', 9 * (width - 2 * options.margin) / 10 + options.margin / 2)
189
+ .attr('x', 6.7 * (width - 2 * options.margin) / 10 - options.margin / 2)
190
190
  .text('Stronger hits');
191
191
  // .text(function () {
192
192
  // return Helpers.prettify_evalue(hits[0].hitEvalue);
@@ -196,9 +196,9 @@ class Graph {
196
196
  .attr('id', 'legend-grad')
197
197
  .selectAll('stop')
198
198
  .data([
199
- {offset: '0%', color: '#ccc'},
200
- {offset: '50%', color: '#888'},
201
- {offset: '100%', color: '#000'}
199
+ {offset: '0%', color: '#000'},
200
+ {offset: '45%', color: '#c74f14'},
201
+ {offset: '100%', color: '#f6bea2'}
202
202
  ])
203
203
  .enter()
204
204
  .append('stop')
@@ -216,7 +216,7 @@ class Graph {
216
216
  * margin: Margin around the svg element.
217
217
  */
218
218
  var defaults = {
219
- barHeight: 3,
219
+ barHeight: 4,
220
220
  legend: inhits.length > 1 ? 3 : 0,
221
221
  margin: 20
222
222
  },
@@ -237,7 +237,8 @@ class Graph {
237
237
 
238
238
  var width = $graphDiv.width();
239
239
  var height = hits.length * (options.barHeight) +
240
- 2 * options.legend + 5 * options.margin;
240
+ 2 * options.legend + 4 * options.margin;
241
+
241
242
  // var height = $graphDiv.height();
242
243
 
243
244
  var SEQ_TYPES = {
@@ -310,7 +311,7 @@ class Graph {
310
311
  return d.hitEvalue;
311
312
  }))
312
313
  ])
313
- .range([40,150]);
314
+ .range([0,0.8]);
314
315
 
315
316
  svg.append('g')
316
317
  .attr('class', 'ghit')
@@ -330,9 +331,7 @@ class Graph {
330
331
  // Drawing the HSPs connector line using the same
331
332
  // color as that of the hit track (using lookahead).
332
333
  var yHspline = y(d.hitId) + options.barHeight / 2;
333
- var hsplineColor = d3.rgb(gradScale(v.hspEvalue),
334
- gradScale(v.hspEvalue),
335
- gradScale(v.hspEvalue));
334
+ var hsplineColor = d3.hsl(20, 0.82, gradScale(v.hspEvalue));
336
335
 
337
336
  if (j+1 < d.length) {
338
337
  if (d[j].hspEnd <= d[j+1].hspStart) {
@@ -17,16 +17,22 @@ export default class HSP extends React.Component {
17
17
  }
18
18
 
19
19
  domID() {
20
- return "Query_" + this.props.queryNumber + "_hit_" +
21
- this.props.hitNumber + "_" + this.hsp.number;
20
+ return 'Query_' + this.props.query.number + '_hit_' +
21
+ this.props.hit.number + '_' + this.props.hsp.number;
22
+ }
23
+
24
+ hitDOM_ID() {
25
+ return 'Query_' + this.props.query.number + '_hit_' + this.props.hit.number;
22
26
  }
23
27
 
24
28
  // Renders pretty formatted alignment.
25
29
  render () {
26
30
  return (
27
- <div className="hsp" id={this.domID()} ref="hsp">
31
+ <div className="hsp" id={this.domID()} ref="hsp"
32
+ data-parent-hit={this.hitDOM_ID()}>
28
33
  <pre className="pre-reset hsp-stats">
29
- {Helpers.toLetters(this.hsp.number) + "."}&nbsp;{this.hspStats()}
34
+ {this.props.showHSPNumbers && `${Helpers.toLetters(this.hsp.number)}. `}
35
+ {this.hspStats().map((s, i) => <span key={i}>{s}</span>)}
30
36
  </pre>
31
37
  {this.hspLines()}
32
38
  </div>
@@ -39,11 +45,17 @@ export default class HSP extends React.Component {
39
45
  }
40
46
 
41
47
  draw () {
42
- var $container = $(React.findDOMNode(this.refs.hsp));
43
- this.chars = Math.floor($container.width() / 7.5);
48
+ var charWidth = this.props.getCharacterWidth();
49
+ var containerWidth = $(React.findDOMNode(this.refs.hsp)).width();
50
+ this.chars = Math.floor((containerWidth - 4) / charWidth);
44
51
  this.forceUpdate();
45
52
  }
46
53
 
54
+ // See Query.shouldComponentUpdate. The same applies for hsp.
55
+ shouldComponentUpdate () {
56
+ return !this.props.hsp;
57
+ }
58
+
47
59
  /**
48
60
  * Returns an array of span elements or plain strings (React automatically
49
61
  * adds span tag around strings). This array is passed as it is to JSX to be
@@ -60,17 +72,17 @@ export default class HSP extends React.Component {
60
72
  line.push(`Score: ${Utils.inTwoDecimal(this.hsp.bit_score)} (${this.hsp.score}), `);
61
73
 
62
74
  // E value
63
- line.push(`E value: `); line.push(Utils.inExponential(this.hsp.evalue)); line.push(', ');
75
+ line.push('E value: '); line.push(Utils.inExponential(this.hsp.evalue)); line.push(', ');
64
76
 
65
77
  // Identity
66
- line.push([`Identities: ${Utils.inFraction(this.hsp.identity, this.hsp.length)} (${Utils.inPercentage(this.hsp.identity, this.hsp.length)}), `]);
78
+ line.push([`Identity: ${Utils.inFraction(this.hsp.identity, this.hsp.length)} (${Utils.inPercentage(this.hsp.identity, this.hsp.length)}), `]);
67
79
 
68
80
  // Positives (for protein alignment).
69
81
  if (this.props.algorithm === 'blastp' ||
70
82
  this.props.algorithm === 'blastx' ||
71
83
  this.props.algorithm === 'tblastn' ||
72
84
  this.props.algorithm === 'tblastx') {
73
- line.push(`Positives: ${Utils.inFraction(this.hsp.positives, this.hsp.length)} (${Utils.inPercentage(this.hsp.positives, this.hsp.length)}), `)
85
+ line.push(`Positives: ${Utils.inFraction(this.hsp.positives, this.hsp.length)} (${Utils.inPercentage(this.hsp.positives, this.hsp.length)}), `);
74
86
  }
75
87
 
76
88
  // Gaps
@@ -80,23 +92,27 @@ export default class HSP extends React.Component {
80
92
  //line.push(`Query coverage: ${this.hsp.qcovhsp}%, `)
81
93
 
82
94
  switch (this.props.algorithm) {
83
- case 'tblastx':
84
- line.push(`, Frame: ${Utils.inFraction(this.hsp.qframe, this.hsp.sframe)}`)
85
- break;
86
- case 'blastn':
87
- line.push(`, Strand: ${(this.hsp.qframe > 0 ? '+' : '-')} / ${(this.hsp.sframe > 0 ? '+' : '-')}`)
88
- break;
89
- case 'blastx':
90
- line.push(`, Query Frame: ${this.hsp.qframe}`)
91
- break;
92
- case 'tblastn':
93
- line.push(`, Hit Frame: ${this.hsp.sframe}`)
94
- break;
95
+ case 'tblastx':
96
+ line.push(`, Frame: ${Utils.inFraction(this.hsp.qframe, this.hsp.sframe)}`);
97
+ break;
98
+ case 'blastn':
99
+ line.push(`, Strand: ${(this.hsp.qframe > 0 ? '+' : '-')} / ${(this.hsp.sframe > 0 ? '+' : '-')}`);
100
+ break;
101
+ case 'blastx':
102
+ line.push(`, Query Frame: ${this.hsp.qframe}`);
103
+ break;
104
+ case 'tblastn':
105
+ line.push(`, Hit Frame: ${this.hsp.sframe}`);
106
+ break;
95
107
  }
96
108
 
97
109
  return line;
98
110
  }
99
111
 
112
+ /**
113
+ * Returns array of pre tags containing the three query, middle, and subject
114
+ * lines that together comprise one 'rendered line' of HSP.
115
+ */
100
116
  hspLines () {
101
117
  // Space reserved for showing coordinates
102
118
  var width = this.width();
@@ -115,7 +131,6 @@ export default class HSP extends React.Component {
115
131
  var nqseq = this.nqseq();
116
132
  var nsseq = this.nsseq();
117
133
  for (let i = 1; i <= lines; i++) {
118
- let line = [];
119
134
  let seq_start_index = chars * (i - 1);
120
135
  let seq_stop_index = seq_start_index + chars;
121
136
 
@@ -133,32 +148,37 @@ export default class HSP extends React.Component {
133
148
  this.sframe_unit() * this.sframe_sign();
134
149
  nsseq = lsend + this.sframe_unit() * this.sframe_sign();
135
150
 
136
- line.push(this.spanCoords('Query ' + this.formatCoords(lqstart, width) + ' '));
137
- line.push(lqseq);
138
- line.push(this.spanCoords(' ' + lqend));
139
- line.push(<br/>);
140
-
141
- line.push(this.formatCoords('', width + 8) + ' ');
142
- line.push(lmseq);
143
- line.push(<br/>);
144
-
145
- line.push(this.spanCoords('Subject ' + this.formatCoords(lsstart, width) + ' '));
146
- line.push(lsseq);
147
- line.push(this.spanCoords(' ' + lsend))
148
- line.push(<br/>);
149
-
150
- pp.push(<pre key={this.hsp.number + ',' + i}
151
- className="pre-reset hsp-lines">{line}</pre>);
151
+ pp.push(
152
+ <pre key={this.hsp.number + ',' + i} className="pre-reset hsp-lines">
153
+ <span className="hsp-coords">
154
+ {`Query ${this.formatCoords(lqstart, width)} `}
155
+ </span>
156
+ <span>{lqseq}</span>
157
+ <span className="hsp-coords">{` ${lqend}`}</span>
158
+ <br/>
159
+ <span className="hsp-coords">
160
+ {`${this.formatCoords('', width + 8)} `}
161
+ </span>
162
+ <span>{lmseq}</span>
163
+ <br/>
164
+ <span className="hsp-coords">
165
+ {`Subject ${this.formatCoords(lsstart, width)} `}
166
+ </span>
167
+ <span>{lsseq}</span>
168
+ <span className="hsp-coords">{` ${lsend}`}</span>
169
+ <br/>
170
+ </pre>);
152
171
  }
153
172
 
154
173
  return pp;
155
174
  }
156
175
 
157
- // Width of each line of alignment.
158
- width() {
176
+ // Width of the coordinate part of hsp lines. Essentially the length of
177
+ // the largest coordinate.
178
+ width () {
159
179
  return _.max(_.map([this.hsp.qstart, this.hsp.qend,
160
- this.hsp.sstart, this.hsp.send],
161
- (n) => { return n.toString().length }));
180
+ this.hsp.sstart, this.hsp.send],
181
+ (n) => { return n.toString().length; }));
162
182
  }
163
183
 
164
184
  // Alignment start coordinate for query sequence.
@@ -167,16 +187,16 @@ export default class HSP extends React.Component {
167
187
  // (translated) query sequence aligned.
168
188
  nqseq () {
169
189
  switch (this.props.algorithm) {
170
- case 'blastp':
171
- case 'blastx':
172
- case 'tblastn':
173
- case 'tblastx':
174
- return this.hsp.qframe >= 0 ? this.hsp.qstart : this.hsp.qend;
175
- case 'blastn':
176
- // BLASTN is a bit weird in that, no matter which direction the query
177
- // sequence aligned in, qstart is taken as alignment start coordinate
178
- // for query.
179
- return this.hsp.qstart;
190
+ case 'blastp':
191
+ case 'blastx':
192
+ case 'tblastn':
193
+ case 'tblastx':
194
+ return this.hsp.qframe >= 0 ? this.hsp.qstart : this.hsp.qend;
195
+ case 'blastn':
196
+ // BLASTN is a bit weird in that, no matter which direction the query
197
+ // sequence aligned in, qstart is taken as alignment start coordinate
198
+ // for query.
199
+ return this.hsp.qstart;
180
200
  }
181
201
  }
182
202
 
@@ -186,16 +206,16 @@ export default class HSP extends React.Component {
186
206
  // (translated) subject sequence aligned.
187
207
  nsseq () {
188
208
  switch (this.props.algorithm) {
189
- case 'blastp':
190
- case 'blastx':
191
- case 'tblastn':
192
- case 'tblastx':
193
- return this.hsp.sframe >= 0 ? this.hsp.sstart : this.hsp.send;
194
- case 'blastn':
195
- // BLASTN is a bit weird in that, no matter which direction the
196
- // subject sequence aligned in, sstart is taken as alignment
197
- // start coordinate for subject.
198
- return this.hsp.sstart
209
+ case 'blastp':
210
+ case 'blastx':
211
+ case 'tblastn':
212
+ case 'tblastx':
213
+ return this.hsp.sframe >= 0 ? this.hsp.sstart : this.hsp.send;
214
+ case 'blastn':
215
+ // BLASTN is a bit weird in that, no matter which direction the
216
+ // subject sequence aligned in, sstart is taken as alignment
217
+ // start coordinate for subject.
218
+ return this.hsp.sstart;
199
219
  }
200
220
  }
201
221
 
@@ -209,16 +229,16 @@ export default class HSP extends React.Component {
209
229
  // translated or not.
210
230
  qframe_unit () {
211
231
  switch (this.props.algorithm) {
212
- case 'blastp':
213
- case 'blastn':
214
- case 'tblastn':
215
- return 1;
216
- case 'blastx':
217
- // _Translated_ nucleotide query against protein database.
218
- case 'tblastx':
219
- // _Translated_ nucleotide query against translated
220
- // nucleotide database.
221
- return 3;
232
+ case 'blastp':
233
+ case 'blastn':
234
+ case 'tblastn':
235
+ return 1;
236
+ case 'blastx':
237
+ // _Translated_ nucleotide query against protein database.
238
+ case 'tblastx':
239
+ // _Translated_ nucleotide query against translated
240
+ // nucleotide database.
241
+ return 3;
222
242
  }
223
243
  }
224
244
 
@@ -232,17 +252,17 @@ export default class HSP extends React.Component {
232
252
  // translated or not.
233
253
  sframe_unit () {
234
254
  switch (this.props.algorithm) {
235
- case 'blastp':
236
- case 'blastx':
237
- case 'blastn':
238
- return 1;
239
- case 'tblastn':
240
- // Protein query against _translated_ nucleotide database.
241
- return 3;
242
- case 'tblastx':
243
- // Translated nucleotide query against _translated_
244
- // nucleotide database.
245
- return 3;
255
+ case 'blastp':
256
+ case 'blastx':
257
+ case 'blastn':
258
+ return 1;
259
+ case 'tblastn':
260
+ // Protein query against _translated_ nucleotide database.
261
+ return 3;
262
+ case 'tblastx':
263
+ // Translated nucleotide query against _translated_
264
+ // nucleotide database.
265
+ return 3;
246
266
  }
247
267
  }
248
268
 
@@ -283,7 +303,7 @@ export default class HSP extends React.Component {
283
303
  }
284
304
 
285
305
  spanCoords (text) {
286
- return <span className="hsp-coords">{text}</span>
306
+ return <span className="hsp-coords">{text}</span>;
287
307
  }
288
308
  }
289
309