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
@@ -17,6 +17,8 @@ class Page extends Component {
17
17
  this.showErrorModal = this.showErrorModal.bind(this);
18
18
  this.getCharacterWidth = this.getCharacterWidth.bind(this);
19
19
  this.hspChars = createRef();
20
+ this.sequenceModal = createRef();
21
+ this.errorModal = createRef();
20
22
  }
21
23
  componentDidMount() {
22
24
  var job_id = location.pathname.split('/').pop();
@@ -24,11 +26,11 @@ class Page extends Component {
24
26
  }
25
27
 
26
28
  showSequenceModal(url) {
27
- this.refs.sequenceModal.show(url);
29
+ this.sequenceModal.current.show(url);
28
30
  }
29
31
 
30
32
  showErrorModal(errorData, beforeShow) {
31
- this.refs.errorModal.show(errorData, beforeShow);
33
+ this.errorModal.current.show(errorData, beforeShow);
32
34
  }
33
35
 
34
36
  getCharacterWidth() {
@@ -41,9 +43,9 @@ class Page extends Component {
41
43
  render() {
42
44
  return (
43
45
  <div>
44
- {/* Provide bootstrap .container element inside the #view for
46
+ {/* Provide tailwind element inside the #view for
45
47
  the Report component to render itself in. */}
46
- <div className="container">
48
+ <div className="mx-auto">
47
49
  <Report
48
50
  showSequenceModal={(_) => this.showSequenceModal(_)}
49
51
  getCharacterWidth={() => this.getCharacterWidth()}
@@ -52,7 +54,7 @@ class Page extends Component {
52
54
  </div>
53
55
 
54
56
  {/* Add a hidden span tag containing chars used in HSPs */}
55
- <pre className="pre-reset hsp-lines" ref={this.hspChars} hidden>
57
+ <pre className="m-0 p-0 rounded-none border-0 bg-inherit whitespace-pre-wrap break-keep mt-1 pre-text tracking-wider" ref={this.hspChars} hidden>
56
58
  ABCDEFGHIJKLMNOPQRSTUVWXYZ +-
57
59
  </pre>
58
60
 
@@ -60,11 +62,11 @@ class Page extends Component {
60
62
  <canvas id="png-exporter" hidden></canvas>
61
63
 
62
64
  <SequenceModal
63
- ref="sequenceModal"
65
+ ref={this.sequenceModal}
64
66
  showErrorModal={(...args) => this.showErrorModal(...args)}
65
67
  />
66
68
 
67
- <ErrorModal ref="errorModal" />
69
+ <ErrorModal ref={this.errorModal} />
68
70
  </div>
69
71
  );
70
72
  }
@@ -72,4 +74,4 @@ class Page extends Component {
72
74
 
73
75
 
74
76
  const root = createRoot(document.getElementById('view'));
75
- root.render(<Page />);
77
+ root.render(<Page />);
data/public/js/search.js CHANGED
@@ -1,32 +1,52 @@
1
- import "./jquery_world";
2
- import React, { Component } from "react";
3
- import { createRoot } from "react-dom/client";
4
- import { DnD } from "./dnd";
5
- import { Form } from "./form";
6
- import { SearchHeaderPlugin } from "search_header_plugin";
1
+ import './jquery_world';
2
+ import React, { Component } from 'react';
3
+ import { createRoot } from 'react-dom/client';
4
+ import { DnD } from './dnd';
5
+ import { Form } from './form';
6
+ import { SearchHeaderPlugin } from 'search_header_plugin';
7
7
 
8
8
  /**
9
9
  * Clear sessionStorage on reload.
10
10
  */
11
- if (performance.navigation.type == performance.navigation.TYPE_RELOAD) {
11
+ const navigationEntry = performance.getEntriesByType('navigation')[0];
12
+ if (navigationEntry && navigationEntry.type === 'reload') {
12
13
  sessionStorage.clear();
13
14
  history.replaceState(null, "", location.href.split("?")[0]);
14
15
  }
15
16
 
16
17
  class Page extends Component {
18
+ constructor(props) {
19
+ super(props);
20
+ this.dnd = React.createRef();
21
+ this.form = React.createRef();
22
+ }
23
+
17
24
  componentDidMount() {
18
- this.refs.dnd.setState({ query: this.refs.form.refs.query });
25
+ this.dnd.current.setState({ query: this.form.current.query.current })
19
26
  }
27
+
20
28
  render() {
21
29
  return (
22
30
  <div>
23
31
  <SearchHeaderPlugin />
24
- <DnD ref="dnd" />
25
- <Form ref="form" />
32
+ <DnD ref={this.dnd} />
33
+ <Form ref={this.form} />
26
34
  </div>
27
35
  );
28
36
  }
29
37
  }
30
38
 
31
- const root = createRoot(document.getElementById("view"));
39
+ const root = createRoot(document.getElementById('view'));
32
40
  root.render(<Page />);
41
+
42
+ document.addEventListener('DOMContentLoaded', () => {
43
+ const closeButton = document.querySelector('button.advanced-modal-close');
44
+ const modal = document.querySelector('dialog.advanced-modal');
45
+ modal.addEventListener('close', () => { document.body.classList.remove("overflow-hidden") });
46
+
47
+ if (closeButton) {
48
+ closeButton.addEventListener('click', function() {
49
+ modal.close();
50
+ });
51
+ }
52
+ });
@@ -1,6 +1,5 @@
1
1
  import React, { Component, createRef } from 'react';
2
2
  import _ from 'underscore';
3
- import './jquery_world';
4
3
 
5
4
  /**
6
5
  * SearchButton widget.
@@ -13,23 +12,31 @@ export class SearchButton extends Component {
13
12
  hasQuery: false,
14
13
  hasDatabases: false,
15
14
  dropdownVisible: false,
15
+ messageTooltip: "",
16
+ titleTooltip: "",
17
+ hideMessageTooltip: false
16
18
  };
17
- this.inputGroup = this.inputGroup.bind(this);
18
- this.submitButton = this.submitButton.bind(this);
19
- this.initTooltip = this.initTooltip.bind(this);
20
- this.showTooltip = this.showTooltip.bind(this);
21
- this.hideTooltip = this.hideTooltip.bind(this);
22
19
  this.changeAlgorithm = this.changeAlgorithm.bind(this);
23
20
  this.decorate = this.decorate.bind(this);
21
+ this.setMessageTooltipButton = this.setMessageTooltipButton.bind(this);
22
+ this.setTitleTooltipButton = this.setTitleTooltipButton.bind(this);
23
+ this.renderButton = this.renderButton.bind(this);
24
+ this.inputGroup = this.inputGroup.bind(this);
25
+ this.handleMouseOver = this.handleMouseOver.bind(this)
26
+ this.handeMouseOut = this.handeMouseOut.bind(this)
24
27
  this.inputGroupRef = createRef();
25
- this.submitButtonRef = createRef();
26
28
  }
29
+
27
30
  componentDidMount() {
28
- this.initTooltip();
31
+ this.timeout = setTimeout(() => this.setState({ hideMessageTooltip: true}), 3000)
29
32
  }
30
33
 
31
34
  shouldComponentUpdate(props, state) {
32
- return !_.isEqual(state.methods, this.state.methods) || state.dropdownVisible !== this.state.dropdownVisible;
35
+ return !_.isEqual(state.methods, this.state.methods) ||
36
+ state.dropdownVisible !== this.state.dropdownVisible ||
37
+ state.messageTooltip !== this.state.messageTooltip ||
38
+ state.titleTooltip !== this.state.titleTooltip ||
39
+ state.hideMessageTooltip !== this.state.hideMessageTooltip;
33
40
  }
34
41
 
35
42
  componentDidUpdate(_prevProps, prevState) {
@@ -42,66 +49,11 @@ export class SearchButton extends Component {
42
49
  }
43
50
  }
44
51
  }
45
- // Internal helpers. //
46
52
 
47
- /**
48
- * Returns jquery wrapped input group.
49
- */
50
53
  inputGroup() {
51
54
  return $(this.inputGroupRef.current);
52
55
  }
53
56
 
54
- /**
55
- * Returns jquery wrapped submit button.
56
- */
57
- submitButton() {
58
- return $(this.submitButtonRef.current);
59
- }
60
-
61
- /**
62
- * Initialise tooltip on input group and submit button.
63
- */
64
- initTooltip() {
65
- this.inputGroup().tooltip({
66
- trigger: 'manual',
67
- title: _.bind(function () {
68
- if (!this.state.hasQuery && !this.state.hasDatabases) {
69
- return 'You must enter a query sequence and select one or more databases above before you can run a search!';
70
- } else if (this.state.hasQuery && !this.state.hasDatabases) {
71
- return 'You must select one or more databases above before you can run a search!';
72
- } else if (!this.state.hasQuery && this.state.hasDatabases) {
73
- return 'You must enter a query sequence above before you can run a search!';
74
- }
75
- }, this),
76
- });
77
-
78
- this.submitButton().tooltip({
79
- title: _.bind(function () {
80
- var title = 'Click to BLAST or press Ctrl+Enter.';
81
- if (this.state.methods.length > 1) {
82
- title +=
83
- ' Click dropdown button on the right for other' +
84
- ' BLAST algorithms that can be used.';
85
- }
86
- return title;
87
- }, this),
88
- });
89
- }
90
-
91
- /**
92
- * Show tooltip on input group.
93
- */
94
- showTooltip() {
95
- this.inputGroup()._tooltip('show');
96
- }
97
-
98
- /**
99
- * Hide tooltip on input group.
100
- */
101
- hideTooltip() {
102
- this.inputGroup()._tooltip('hide');
103
- }
104
-
105
57
  /**
106
58
  * Change selected algorithm.
107
59
  *
@@ -140,26 +92,49 @@ export class SearchButton extends Component {
140
92
  dropdownVisible: !prevState.dropdownVisible
141
93
  }));
142
94
  }
95
+
96
+ setMessageTooltipButton() {
97
+ if (!this.state.hasQuery && !this.state.hasDatabases) {
98
+ this.setState({ messageTooltip: 'You must enter a query sequence and select one or more databases above before you can run a search!' });
99
+ } else if (this.state.hasQuery && !this.state.hasDatabases) {
100
+ this.setState({ messageTooltip: 'You must select one or more databases above before you can run a search!' });
101
+ } else if (!this.state.hasQuery && this.state.hasDatabases) {
102
+ this.setState({ messageTooltip: 'You must enter a query sequence above before you can run a search!' });
103
+ }
104
+ }
143
105
 
144
- render() {
145
- var methods = this.state.methods;
146
- var method = methods[0];
147
- var multi = methods.length > 1;
106
+ setTitleTooltipButton() {
107
+ var title = 'Click to BLAST or press Ctrl+Enter.';
108
+
109
+ this.setState({ titleTooltip: title });
110
+
111
+ if (this.state.methods.length > 1) {
112
+ this.setState({
113
+ titleTooltip: title +=
114
+ ' Click dropdown button on the right for other' +
115
+ ' BLAST algorithms that can be used.'
116
+ });
117
+ }
118
+ }
119
+
120
+ handeMouseOut() {
121
+ clearTimeout(this.timeout);
122
+ this.setState({ hideMessageTooltip: false });
123
+ }
148
124
 
125
+ handleMouseOver() {
126
+ this.setMessageTooltipButton();
127
+ this.setTitleTooltipButton();
128
+ this.timeout = setTimeout(() => this.setState({ hideMessageTooltip: true}), 3000)
129
+ }
130
+
131
+ renderButton(method, multi, methods) {
149
132
  return (
150
- <div
151
- // className={multi ? 'flex' : 'flex'}
152
- className="my-4 md:my-2 flex justify-end w-full md:w-auto relative"
153
- id="methods"
154
- ref={this.inputGroupRef}
155
- onMouseOver={this.showTooltip}
156
- onMouseOut={this.hideTooltip}
157
- >
133
+ <div className="flex">
158
134
  <button
159
135
  type="submit"
160
- className="uppercase w-full md:w-auto flex text-xl justify-center py-2 px-16 border border-transparent rounded-md shadow-sm text-white bg-seqblue hover:bg-seqorange focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-seqorange"
136
+ className={`${method ? 'hover:bg-seqorange focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-seqorange ' : 'opacity-50 cursor-not-allowed '}uppercase w-full md:w-auto flex text-xl justify-center py-2 px-16 border border-transparent rounded-md shadow-sm text-white bg-seqblue`}
161
137
  id="method"
162
- ref={this.submitButtonRef}
163
138
  name="method"
164
139
  value={method}
165
140
  disabled={!method}
@@ -201,6 +176,38 @@ export class SearchButton extends Component {
201
176
  </div>
202
177
  )}
203
178
  </div>
179
+ )
180
+ }
181
+
182
+ render() {
183
+ var methods = this.state.methods;
184
+ var method = methods[0];
185
+ var multi = methods.length > 1;
186
+
187
+ return (
188
+ <div
189
+ className="my-4 md:my-2 flex justify-end w-full md:w-auto relative"
190
+ id="methods"
191
+ ref={this.inputGroupRef}
192
+ onMouseOver={this.handleMouseOver}
193
+ onMouseOut={this.handeMouseOut}
194
+ >
195
+ <div className="relative flex flex-col items-center group">
196
+ <div className="flex items-center w-full">
197
+ {this.renderButton(method, multi, methods)}
198
+ { !this.state.dropdownVisible &&
199
+ <div className="absolute hidden bottom-11 items-center flex-col-reverse group-hover:flex w-full">
200
+ <div className="w-0 h-0 border-y-8 border-r-8 border-t-transparent border-b-transparent border-r-black -mt-1 -mr-px tooltip-arrow-rotate"></div>
201
+ <span className="relative z-10 p-2 search-button-text leading-4 text-center text-white whitespace-no-wrap bg-black shadow-lg rounded-md">
202
+ { !this.state.hasQuery || !this.state.hasDatabases ? (
203
+ this.state.hideMessageTooltip ? <>{this.state.titleTooltip}</> : this.state.messageTooltip
204
+ ) : <> {this.state.titleTooltip}</>}
205
+ </span>
206
+ </div>
207
+ }
208
+ </div>
209
+ </div>
210
+ </div>
204
211
  );
205
212
  }
206
213
  }