sequenceserver 1.1.0.beta8 → 1.1.0.beta10
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/lib/sequenceserver/blast/hsp.rb +2 -174
- data/lib/sequenceserver/blast/report.rb +4 -3
- data/lib/sequenceserver/job.rb +2 -1
- data/lib/sequenceserver/version.rb +1 -1
- data/public/css/grapher.css +50 -35
- data/public/css/sequenceserver.css +25 -16
- data/public/css/sequenceserver.min.css +3 -3
- data/public/js/circos.js +1 -1
- data/public/js/grapher.js +8 -3
- data/public/js/hits_overview.js +4 -6
- data/public/js/hsp.js +283 -0
- data/public/js/kablammo.js +3 -3
- data/public/js/report.js +668 -850
- data/public/js/search.js +208 -180
- data/public/js/utils.js +77 -0
- data/public/sequenceserver-report.min.js +14 -14
- data/public/sequenceserver-search.min.js +2 -2
- metadata +4 -2
data/public/js/search.js
CHANGED
@@ -7,6 +7,23 @@ import React from 'react';
|
|
7
7
|
*/
|
8
8
|
$.webshims.polyfill('forms');
|
9
9
|
|
10
|
+
var Page = React.createClass({
|
11
|
+
render: function () {
|
12
|
+
return (
|
13
|
+
<div>
|
14
|
+
<DnD ref="dnd"/>
|
15
|
+
<Form ref="form"/>
|
16
|
+
</div>
|
17
|
+
);
|
18
|
+
},
|
19
|
+
|
20
|
+
componentDidMount: function () {
|
21
|
+
this.refs.dnd.setState({
|
22
|
+
query: this.refs.form.refs.query
|
23
|
+
})
|
24
|
+
}
|
25
|
+
});
|
26
|
+
|
10
27
|
/** Drag n drop widget.
|
11
28
|
*/
|
12
29
|
var DnD = React.createClass({
|
@@ -171,6 +188,135 @@ var DnD = React.createClass({
|
|
171
188
|
}
|
172
189
|
});
|
173
190
|
|
191
|
+
/**
|
192
|
+
* Search form.
|
193
|
+
*
|
194
|
+
* Top level component that initialises and holds all other components, and
|
195
|
+
* facilitates communication between them.
|
196
|
+
*/
|
197
|
+
var Form = React.createClass({
|
198
|
+
|
199
|
+
getInitialState: function () {
|
200
|
+
return {
|
201
|
+
databases: {},
|
202
|
+
preDefinedOpts: {
|
203
|
+
'blastn': ['-task blastn', '-evalue 1e-5'],
|
204
|
+
'blastp': ['-evalue 1e-5'],
|
205
|
+
'blastx': ['-evalue 1e-5'],
|
206
|
+
'tblastx': ['-evalue 1e-5'],
|
207
|
+
'tblastn': ['-evalue 1e-5']
|
208
|
+
}
|
209
|
+
};
|
210
|
+
},
|
211
|
+
|
212
|
+
componentDidMount: function () {
|
213
|
+
$.getJSON("searchdata.json", _.bind(function(data) {
|
214
|
+
this.setState({
|
215
|
+
databases: data["database"], preDefinedOpts: $.extend({},
|
216
|
+
this.state.preDefinedOpts, data["options"])
|
217
|
+
});
|
218
|
+
}, this));
|
219
|
+
|
220
|
+
$(document).bind("keydown", _.bind(function (e) {
|
221
|
+
if (e.ctrlKey && e.keyCode === 13 &&
|
222
|
+
!$('#method').is(':disabled')) {
|
223
|
+
$(this.getDOMNode()).trigger('submit');
|
224
|
+
}
|
225
|
+
}, this));
|
226
|
+
},
|
227
|
+
|
228
|
+
determineBlastMethod: function () {
|
229
|
+
var database_type = this.databaseType;
|
230
|
+
var sequence_type = this.sequenceType;
|
231
|
+
|
232
|
+
if (this.refs.query.isEmpty()) {
|
233
|
+
return [];
|
234
|
+
}
|
235
|
+
|
236
|
+
//database type is always known
|
237
|
+
switch (database_type) {
|
238
|
+
case 'protein':
|
239
|
+
switch (sequence_type) {
|
240
|
+
case undefined:
|
241
|
+
return ['blastp', 'blastx'];
|
242
|
+
case 'protein':
|
243
|
+
return ['blastp'];
|
244
|
+
case 'nucleotide':
|
245
|
+
return ['blastx'];
|
246
|
+
}
|
247
|
+
break;
|
248
|
+
case 'nucleotide':
|
249
|
+
switch (sequence_type) {
|
250
|
+
case undefined:
|
251
|
+
return ['tblastn', 'blastn', 'tblastx'];
|
252
|
+
case 'protein':
|
253
|
+
return ['tblastn'];
|
254
|
+
case 'nucleotide':
|
255
|
+
return ['blastn', 'tblastx'];
|
256
|
+
}
|
257
|
+
break;
|
258
|
+
}
|
259
|
+
|
260
|
+
return [];
|
261
|
+
},
|
262
|
+
|
263
|
+
handleSequenceTypeChanged: function (type) {
|
264
|
+
this.sequenceType = type;
|
265
|
+
this.refs.button.setState({
|
266
|
+
hasQuery: !this.refs.query.isEmpty(),
|
267
|
+
hasDatabases: !!this.databaseType,
|
268
|
+
methods: this.determineBlastMethod()
|
269
|
+
});
|
270
|
+
},
|
271
|
+
|
272
|
+
handleDatabaseTypeChanaged: function (type) {
|
273
|
+
this.databaseType = type;
|
274
|
+
this.refs.button.setState({
|
275
|
+
hasQuery: !this.refs.query.isEmpty(),
|
276
|
+
hasDatabases: !!this.databaseType,
|
277
|
+
methods: this.determineBlastMethod()
|
278
|
+
});
|
279
|
+
},
|
280
|
+
|
281
|
+
handleAlgoChanged: function (algo) {
|
282
|
+
if (this.state.preDefinedOpts.hasOwnProperty(algo)) {
|
283
|
+
this.refs.opts.setState({
|
284
|
+
preOpts: this.state.preDefinedOpts[algo].join(" ")
|
285
|
+
});
|
286
|
+
}
|
287
|
+
else {
|
288
|
+
this.refs.opts.setState({preOpts: ""});
|
289
|
+
}
|
290
|
+
},
|
291
|
+
|
292
|
+
render: function () {
|
293
|
+
return (
|
294
|
+
<div
|
295
|
+
className="container">
|
296
|
+
<form
|
297
|
+
id="blast" method="post" className="form-horizontal">
|
298
|
+
<div
|
299
|
+
className="form-group query-container">
|
300
|
+
<Query ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged}/>
|
301
|
+
</div>
|
302
|
+
<div
|
303
|
+
className="notifications" id="notifications">
|
304
|
+
<NucleotideNotification/>
|
305
|
+
<ProteinNotification/>
|
306
|
+
<MixedNotification/>
|
307
|
+
</div>
|
308
|
+
<Databases ref="databases" onDatabaseTypeChanged={this.handleDatabaseTypeChanaged} databases={this.state.databases}/>
|
309
|
+
<div
|
310
|
+
className="form-group">
|
311
|
+
<Options ref="opts"/>
|
312
|
+
<SearchButton ref="button" onAlgoChanged={this.handleAlgoChanged}/>
|
313
|
+
</div>
|
314
|
+
</form>
|
315
|
+
</div>
|
316
|
+
);
|
317
|
+
}
|
318
|
+
});
|
319
|
+
|
174
320
|
/**
|
175
321
|
* Query widget.
|
176
322
|
*/
|
@@ -487,42 +633,70 @@ var Databases = React.createClass({
|
|
487
633
|
},
|
488
634
|
|
489
635
|
handleClick: function (database) {
|
490
|
-
var type = this.nselected() ? database.type : ''
|
636
|
+
var type = this.nselected() ? database.type : '';
|
491
637
|
this.setState({type: type});
|
492
638
|
},
|
493
639
|
|
640
|
+
handleToggle: function (toggleState, type) {
|
641
|
+
switch (toggleState) {
|
642
|
+
case '[Select all]':
|
643
|
+
$(`.${type} .database input:not(:checked)`).click();
|
644
|
+
break;
|
645
|
+
case '[Deselect all]':
|
646
|
+
$(`.${type} .database input:checked`).click();
|
647
|
+
break;
|
648
|
+
}
|
649
|
+
},
|
650
|
+
|
494
651
|
render: function () {
|
495
652
|
return (
|
496
|
-
<div
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
653
|
+
<div className="form-group databases-container">
|
654
|
+
{ _.map(this.categories(), this.renderDatabases) }
|
655
|
+
</div>
|
656
|
+
);
|
657
|
+
},
|
658
|
+
|
659
|
+
renderDatabases: function (category) {
|
660
|
+
// Panel name and column width.
|
661
|
+
var panelTitle = category[0].toUpperCase() +
|
662
|
+
category.substring(1).toLowerCase() + " databases";
|
663
|
+
var columnClass = this.categories().length === 1 ? 'col-md-12' :
|
664
|
+
'col-md-6';
|
665
|
+
|
666
|
+
// Toggle button.
|
667
|
+
var toggleState = '[Select all]';
|
668
|
+
var toggleClass = 'btn btn-link';
|
669
|
+
var toggleShown = this.databases(category).length > 1 ;
|
670
|
+
var toggleDisabled = this.state.type && this.state.type !== category;
|
671
|
+
if (toggleShown && toggleDisabled) toggleClass += ' disabled';
|
672
|
+
if (!toggleShown) toggleClass += ' hidden';
|
673
|
+
if (this.nselected() === this.databases(category).length) {
|
674
|
+
toggleState = '[Deselect all]';
|
675
|
+
}
|
676
|
+
|
677
|
+
// JSX.
|
678
|
+
return (
|
679
|
+
<div className={columnClass}>
|
680
|
+
<div className="panel panel-default">
|
681
|
+
<div className="panel-heading">
|
682
|
+
<h4 style={{display: "inline"}}>{panelTitle}</h4>
|
683
|
+
<button type="button" className={toggleClass} disabled={toggleDisabled}
|
684
|
+
onClick={ function () { this.handleToggle(toggleState, category) }.bind(this) }>
|
685
|
+
{toggleState}
|
686
|
+
</button>
|
687
|
+
</div>
|
688
|
+
<ul className={"list-group databases " + category}>
|
689
|
+
{
|
690
|
+
_.map(this.databases(category), _.bind(function (database) {
|
691
|
+
return (
|
692
|
+
<li className="list-group-item">
|
693
|
+
{ this.renderDatabase(database) }
|
694
|
+
</li>
|
695
|
+
);
|
696
|
+
}, this))
|
697
|
+
}
|
698
|
+
</ul>
|
699
|
+
</div>
|
526
700
|
</div>
|
527
701
|
);
|
528
702
|
},
|
@@ -547,9 +721,9 @@ var Databases = React.createClass({
|
|
547
721
|
);
|
548
722
|
},
|
549
723
|
|
550
|
-
shouldComponentUpdate: function (props, state) {
|
551
|
-
return !(state.type && state.type === this.state.type);
|
552
|
-
},
|
724
|
+
//shouldComponentUpdate: function (props, state) {
|
725
|
+
//return !(state.type && state.type === this.state.type);
|
726
|
+
//},
|
553
727
|
|
554
728
|
componentDidUpdate: function () {
|
555
729
|
if (this.databases() && this.databases().length === 1) {
|
@@ -788,150 +962,4 @@ var SearchButton = React.createClass({
|
|
788
962
|
}
|
789
963
|
});
|
790
964
|
|
791
|
-
/**
|
792
|
-
* Search form.
|
793
|
-
*
|
794
|
-
* Top level component that initialises and holds all other components, and
|
795
|
-
* facilitates communication between them.
|
796
|
-
*/
|
797
|
-
var Form = React.createClass({
|
798
|
-
|
799
|
-
getInitialState: function () {
|
800
|
-
return {
|
801
|
-
databases: {},
|
802
|
-
preDefinedOpts: {
|
803
|
-
'blastn': ['-task blastn', '-evalue 1e-5'],
|
804
|
-
'blastp': ['-evalue 1e-5'],
|
805
|
-
'blastx': ['-evalue 1e-5'],
|
806
|
-
'tblastx': ['-evalue 1e-5'],
|
807
|
-
'tblastn': ['-evalue 1e-5']
|
808
|
-
}
|
809
|
-
};
|
810
|
-
},
|
811
|
-
|
812
|
-
componentDidMount: function () {
|
813
|
-
$.getJSON("searchdata.json", _.bind(function(data) {
|
814
|
-
this.setState({
|
815
|
-
databases: data["database"], preDefinedOpts: $.extend({},
|
816
|
-
this.state.preDefinedOpts, data["options"])
|
817
|
-
});
|
818
|
-
}, this));
|
819
|
-
|
820
|
-
$(document).bind("keydown", _.bind(function (e) {
|
821
|
-
if (e.ctrlKey && e.keyCode === 13 &&
|
822
|
-
!$('#method').is(':disabled')) {
|
823
|
-
$(this.getDOMNode()).trigger('submit');
|
824
|
-
}
|
825
|
-
}, this));
|
826
|
-
},
|
827
|
-
|
828
|
-
determineBlastMethod: function () {
|
829
|
-
var database_type = this.databaseType;
|
830
|
-
var sequence_type = this.sequenceType;
|
831
|
-
|
832
|
-
if (this.refs.query.isEmpty()) {
|
833
|
-
return [];
|
834
|
-
}
|
835
|
-
|
836
|
-
//database type is always known
|
837
|
-
switch (database_type) {
|
838
|
-
case 'protein':
|
839
|
-
switch (sequence_type) {
|
840
|
-
case undefined:
|
841
|
-
return ['blastp', 'blastx'];
|
842
|
-
case 'protein':
|
843
|
-
return ['blastp'];
|
844
|
-
case 'nucleotide':
|
845
|
-
return ['blastx'];
|
846
|
-
}
|
847
|
-
break;
|
848
|
-
case 'nucleotide':
|
849
|
-
switch (sequence_type) {
|
850
|
-
case undefined:
|
851
|
-
return ['tblastn', 'blastn', 'tblastx'];
|
852
|
-
case 'protein':
|
853
|
-
return ['tblastn'];
|
854
|
-
case 'nucleotide':
|
855
|
-
return ['blastn', 'tblastx'];
|
856
|
-
}
|
857
|
-
break;
|
858
|
-
}
|
859
|
-
|
860
|
-
return [];
|
861
|
-
},
|
862
|
-
|
863
|
-
handleSequenceTypeChanged: function (type) {
|
864
|
-
this.sequenceType = type;
|
865
|
-
this.refs.button.setState({
|
866
|
-
hasQuery: !this.refs.query.isEmpty(),
|
867
|
-
hasDatabases: !!this.databaseType,
|
868
|
-
methods: this.determineBlastMethod()
|
869
|
-
});
|
870
|
-
},
|
871
|
-
|
872
|
-
handleDatabaseTypeChanaged: function (type) {
|
873
|
-
this.databaseType = type;
|
874
|
-
this.refs.button.setState({
|
875
|
-
hasQuery: !this.refs.query.isEmpty(),
|
876
|
-
hasDatabases: !!this.databaseType,
|
877
|
-
methods: this.determineBlastMethod()
|
878
|
-
});
|
879
|
-
},
|
880
|
-
|
881
|
-
handleAlgoChanged: function (algo) {
|
882
|
-
if (this.state.preDefinedOpts.hasOwnProperty(algo)) {
|
883
|
-
this.refs.opts.setState({
|
884
|
-
preOpts: this.state.preDefinedOpts[algo].join(" ")
|
885
|
-
});
|
886
|
-
}
|
887
|
-
else {
|
888
|
-
this.refs.opts.setState({preOpts: ""});
|
889
|
-
}
|
890
|
-
},
|
891
|
-
|
892
|
-
render: function () {
|
893
|
-
return (
|
894
|
-
<div
|
895
|
-
className="container">
|
896
|
-
<form
|
897
|
-
id="blast" method="post" className="form-horizontal">
|
898
|
-
<div
|
899
|
-
className="form-group query-container">
|
900
|
-
<Query ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged}/>
|
901
|
-
</div>
|
902
|
-
<div
|
903
|
-
className="notifications" id="notifications">
|
904
|
-
<NucleotideNotification/>
|
905
|
-
<ProteinNotification/>
|
906
|
-
<MixedNotification/>
|
907
|
-
</div>
|
908
|
-
<Databases ref="databases" onDatabaseTypeChanged={this.handleDatabaseTypeChanaged} databases={this.state.databases}/>
|
909
|
-
<div
|
910
|
-
className="form-group">
|
911
|
-
<Options ref="opts"/>
|
912
|
-
<SearchButton ref="button" onAlgoChanged={this.handleAlgoChanged}/>
|
913
|
-
</div>
|
914
|
-
</form>
|
915
|
-
</div>
|
916
|
-
);
|
917
|
-
}
|
918
|
-
});
|
919
|
-
|
920
|
-
var Page = React.createClass({
|
921
|
-
render: function () {
|
922
|
-
return (
|
923
|
-
<div>
|
924
|
-
<DnD ref="dnd"/>
|
925
|
-
<Form ref="form"/>
|
926
|
-
</div>
|
927
|
-
);
|
928
|
-
},
|
929
|
-
|
930
|
-
componentDidMount: function () {
|
931
|
-
this.refs.dnd.setState({
|
932
|
-
query: this.refs.form.refs.query
|
933
|
-
})
|
934
|
-
}
|
935
|
-
});
|
936
|
-
|
937
965
|
React.render(<Page/>, document.getElementById('view'));
|
data/public/js/utils.js
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
var Utils = {
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Render URL for sequence-viewer.
|
7
|
+
*/
|
8
|
+
a: function (link) {
|
9
|
+
if (link.title && link.url)
|
10
|
+
{
|
11
|
+
return (
|
12
|
+
<a href={link.url} className={link.class} target='_blank'>
|
13
|
+
{link.icon && <i className={"fa " + link.icon}></i>}
|
14
|
+
{" " + link.title + " "}
|
15
|
+
</a>
|
16
|
+
);
|
17
|
+
}
|
18
|
+
},
|
19
|
+
|
20
|
+
|
21
|
+
/***********************************
|
22
|
+
* Formatters for hits & hsp table *
|
23
|
+
***********************************/
|
24
|
+
|
25
|
+
// Formats an array of two elements as "first (last)".
|
26
|
+
format_2_tuple: function (tuple) {
|
27
|
+
return (tuple[0] + " (" + tuple[tuple.length - 1] + ")");
|
28
|
+
},
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Returns fraction as percentage
|
32
|
+
*/
|
33
|
+
inPercentage: function (num , den) {
|
34
|
+
return `${(num * 100.0 / den).toFixed(2)}%`;
|
35
|
+
},
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Returns fractional representation as String.
|
39
|
+
*/
|
40
|
+
inFraction: function (num , den) {
|
41
|
+
return num + "/" + den;
|
42
|
+
},
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Returns given Float as String formatted to two decimal places.
|
46
|
+
*/
|
47
|
+
inTwoDecimal: function (num) {
|
48
|
+
return num.toFixed(2)
|
49
|
+
},
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Returns zero if num is zero. Returns two decimal representation of num
|
53
|
+
* if num is between [1..10). Returns num in scientific notation otherwise.
|
54
|
+
*/
|
55
|
+
inExponential: function (num) {
|
56
|
+
// Nothing to do if num is 0.
|
57
|
+
if (num === 0) {
|
58
|
+
return 0
|
59
|
+
}
|
60
|
+
|
61
|
+
// Round to two decimal places if in the rane [1..10).
|
62
|
+
if (num >= 1 && num < 10)
|
63
|
+
{
|
64
|
+
return this.inTwoDecimal(num)
|
65
|
+
}
|
66
|
+
|
67
|
+
// Return numbers in the range [0..1) and [10..Inf] in
|
68
|
+
// scientific format.
|
69
|
+
var exp = num.toExponential(2);
|
70
|
+
var parts = exp.split("e");
|
71
|
+
var base = parts[0];
|
72
|
+
var power = parts[1];
|
73
|
+
return <span>{base} × 10<sup>{power}</sup></span>;
|
74
|
+
}
|
75
|
+
};
|
76
|
+
|
77
|
+
export default Utils;
|