sequenceserver 1.1.0.beta6 → 1.1.0.beta7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da64719d5d51834a93bd4071cf01f7dd9f45dfce
4
- data.tar.gz: 071fa0cfc1d276a53d613b43fab36e85180a3e8e
3
+ metadata.gz: 596bc25ee4cb80b8fad1de7a63b7677f291a0743
4
+ data.tar.gz: bb5bda8278682fb5a79526542023d3addfce8933
5
5
  SHA512:
6
- metadata.gz: 6f5486cf92ad273fd348fcd3d2dbcf4656b6bccd70f4040e2013e787b4ee68cd9c2d0c26473eb21a4ba2c0eb0d240d6f2f955110cd3f1442ddb37df94a8f5fc9
7
- data.tar.gz: bdaf284e009ee77987e2c89652fcdbe3501d783c5cdb0a568dac29535072dde48db28c716b6371ee6eb2dd31cdac364954033a58d886ce2b9eb9d30f941943cf
6
+ metadata.gz: e71d8f58e66599466f5149043f20e1ab5e1490857a557a71005889fdbd70ac46ac3649b18ff5b04037154f9b49ca98a3f37bbccfcef614786c3fc963222424b5
7
+ data.tar.gz: faaa045609c7009b1dae99175da8da6adbf8bb7a01439a2f2dbbd21d6a64f353ac76a5fe5c276051d96c7dbf9f9041c2ae2f90355ea002d1c37b112bfa8a772e
@@ -1,4 +1,4 @@
1
1
  # Define version number.
2
2
  module SequenceServer
3
- VERSION = '1.1.0.beta6'
3
+ VERSION = '1.1.0.beta7'
4
4
  end
data/public/js/circos.js CHANGED
@@ -316,10 +316,10 @@ class Graph {
316
316
  .attr('transform','translate('+this.svgContainer.width() / 2+','+this.svgContainer.height()/2+')')
317
317
  .append('text')
318
318
  .attr('text-anchor','start')
319
- .attr('dy','0.75em')
320
- .attr('x', -50)
321
- .attr('y', 2)
322
- .text('Sorry no Circos generated')
319
+ .attr('dy','-0.25em')
320
+ .attr('x', -175)
321
+ .style("font-size", "14px")
322
+ .text('Circos looks great with less than 16 queries')
323
323
  }
324
324
 
325
325
  layoutReset() {
File without changes
@@ -388,5 +388,5 @@ class Graph {
388
388
  }
389
389
  }
390
390
 
391
- var AlignmentOverview = Grapher(Graph);
392
- export default AlignmentOverview;
391
+ var HitsOverview = Grapher(Graph);
392
+ export default HitsOverview;
@@ -19,11 +19,11 @@ import * as Helpers from './visualisation_helpers';
19
19
 
20
20
  class Graph {
21
21
  static name() {
22
- return 'query against hit';
22
+ return 'Matching region(s)';
23
23
  }
24
24
 
25
25
  static className() {
26
- return 'kablammo collapse in';
26
+ return 'kablammo';
27
27
  }
28
28
 
29
29
  static collapseId(props) {
@@ -9,7 +9,7 @@ import * as Helpers from './visualisation_helpers';
9
9
 
10
10
  class Graph {
11
11
  static name() {
12
- return 'Length Distribution';
12
+ return 'Length distribution of hits';
13
13
  }
14
14
 
15
15
  static className() {
data/public/js/report.js CHANGED
@@ -1,18 +1,16 @@
1
- import SequenceServer from './sequenceserver';
2
- import showErrorModal from './errormodal';
3
-
4
- import _ from 'underscore';
1
+ import './sequenceserver' // for custom $.tooltip function
5
2
  import React from 'react';
6
- import d3 from 'd3';
3
+ import _ from 'underscore';
7
4
 
8
- import * as Helpers from './visualisation_helpers';
9
- import GraphicalOverview from './alignmentsoverview';
10
- //import Kablammo from './kablammo';
11
- import './sequence';
12
- import AlignmentExporter from './alignment_exporter';
13
- import LengthDistribution from './lengthdistribution';
14
5
  import Circos from './circos';
6
+ import HitsOverview from './hits_overview';
7
+ import LengthDistribution from './length_distribution'; // length distribution of hits
8
+ import HSPOverview from './kablammo';
9
+ import AlignmentExporter from './alignment_exporter'; // to download textual alignment
10
+ import './sequence';
15
11
 
12
+ import * as Helpers from './visualisation_helpers'; // for toLetters
13
+ import showErrorModal from './error_modal';
16
14
 
17
15
  /**
18
16
  * Dynamically create form and submit.
@@ -514,9 +512,9 @@ var Hit = React.createClass({
514
512
  }, this))
515
513
  }
516
514
  </div>
517
- {/*
518
- <Kablammo key={"Kablammo"+this.props.query.id} query={this.props.query} hit={this.props.hit} algorithm={this.props.algorithm}/>
519
- */}
515
+ <HSPOverview key={"kablammo"+this.props.query.id}
516
+ query={this.props.query} hit={this.props.hit}
517
+ algorithm={this.props.algorithm}/>
520
518
  <table
521
519
  className="table hsps">
522
520
  <tbody>
@@ -678,7 +676,7 @@ var Query = React.createClass({
678
676
  {this.numhits() &&
679
677
  (
680
678
  <div className="section-content">
681
- <GraphicalOverview key={"GO_"+this.props.query.number} query={this.props.query} program={this.props.data.program} collapsed={this.props.data.veryBig}/>
679
+ <HitsOverview key={"GO_"+this.props.query.number} query={this.props.query} program={this.props.data.program} collapsed={this.props.data.veryBig}/>
682
680
  <LengthDistribution key={"LD_"+this.props.query.id} query={this.props.query} algorithm={this.props.data.program} collapsed="true"/>
683
681
  <HitsTable key={"HT_"+this.props.query.number} query={this.props.query}/>
684
682
  <div
@@ -921,65 +919,12 @@ var SideBar = React.createClass({
921
919
  */
922
920
  var Report = React.createClass({
923
921
 
924
- // Kind of public API //
925
-
926
- /**
927
- * Event-handler when hit is selected
928
- * Adds glow to hit component.
929
- * Updates number of Fasta that can be downloaded
930
- */
931
- selectHit: function (id) {
932
-
933
- var checkbox = $("#" + id);
934
- var num_checked = $('.hit-links :checkbox:checked').length;
935
-
936
- if (!checkbox || !checkbox.val()) {
937
- return;
938
- }
939
-
940
- var $hit = $(checkbox.data('target'));
941
-
942
- // Highlight selected hit and sync checkboxes if sequence viewer is open.
943
- if (checkbox.is(":checked")) {
944
- $hit
945
- .addClass('glow')
946
- .find(":checkbox").not(checkbox).check();
947
- var $a = $('.download-fasta-of-selected');
948
- var $b = $('.download-alignment-of-selected');
949
- $b.enable()
950
- var $n = $a.find('span');
951
- $a
952
- .enable()
953
- }
954
-
955
- else {
956
- $hit
957
- .removeClass('glow')
958
- .find(":checkbox").not(checkbox).uncheck();
959
- }
960
-
961
- if (num_checked >= 1)
962
- {
963
- var $a = $('.download-fasta-of-selected');
964
- var $b = $('.download-alignment-of-selected');
965
- $a.find('.text-bold').html(num_checked);
966
- $b.find('.text-bold').html(num_checked);
967
- }
968
-
969
- if (num_checked == 0) {
970
- var $a = $('.download-fasta-of-selected');
971
- var $b = $('.download-alignment-of-selected');
972
- $a.addClass('disabled').find('.text-bold').html('');
973
- $b.addClass('disabled').find('.text-bold').html('');
974
- }
975
- },
976
-
977
-
978
- // Internal helpers. //
979
-
980
- // Life-cycle methods. //
922
+ // Model //
981
923
 
982
924
  getInitialState: function () {
925
+ this.fetchResults();
926
+ this.updateCycle = 0;
927
+
983
928
  return {
984
929
  search_id: '',
985
930
  program: '',
@@ -991,17 +936,77 @@ var Report = React.createClass({
991
936
  };
992
937
  },
993
938
 
994
- render: function () {
995
- return (this.isResultAvailable() ? this.resultsJSX() : this.loadingJSX());
939
+ /**
940
+ * Fetch results.
941
+ */
942
+ fetchResults: function () {
943
+ var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
944
+
945
+ (function poll (comp) {
946
+ $.getJSON(location.pathname + '.json')
947
+ .complete(function (jqXHR) {
948
+ switch (jqXHR.status) {
949
+ case 202:
950
+ var interval;
951
+ if (intervals.length === 1) {
952
+ interval = intervals[0];
953
+ }
954
+ else {
955
+ interval = intervals.shift;
956
+ }
957
+ setTimeout(poll, interval);
958
+ break;
959
+ case 200:
960
+ comp.updateState(jqXHR.responseJSON);
961
+ break;
962
+ case 404:
963
+ case 400:
964
+ case 500:
965
+ showErrorModal(jqXHR.responseJSON);
966
+ break;
967
+ }
968
+ });
969
+ }(this));
996
970
  },
997
971
 
998
972
  /**
999
- * Returns true if results have been fetched.
1000
- *
1001
- * A holding message is shown till results are fetched.
973
+ * Incrementally update state so that the rendering process is
974
+ * not overwhelemed when there are too many queries.
1002
975
  */
1003
- isResultAvailable: function () {
1004
- return this.state.queries.length >= 1;
976
+ updateState: function(responseJSON) {
977
+ var queries = responseJSON.queries;
978
+
979
+ // Render results for first 50 queries and set flag if total queries is
980
+ // more than 250.
981
+ var numHits = 0;
982
+ responseJSON.veryBig = queries.length > 250;
983
+ //responseJSON.veryBig = !_.every(queries, (query) => {
984
+ //numHits += query.hits.length;
985
+ //return (numHits <= 500);
986
+ //});
987
+ responseJSON.queries = queries.splice(0, 50);
988
+ this.setState(responseJSON);
989
+
990
+ // Render results for remaining queries.
991
+ var update = function () {
992
+ if (queries.length > 0) {
993
+ this.setState({
994
+ queries: this.state.queries.concat(queries.splice(0, 50))
995
+ });
996
+ setTimeout(update.bind(this), 500);
997
+ }
998
+ else {
999
+ this.componentFinishedUpdating();
1000
+ }
1001
+ };
1002
+ setTimeout(update.bind(this), 500);
1003
+ },
1004
+
1005
+
1006
+ // View //
1007
+ render: function () {
1008
+ return this.isResultAvailable() ?
1009
+ this.resultsJSX() : this.loadingJSX();
1005
1010
  },
1006
1011
 
1007
1012
  /**
@@ -1049,9 +1054,11 @@ var Report = React.createClass({
1049
1054
  }
1050
1055
  <div className={this.shouldShowSidebar() ?
1051
1056
  'col-md-9' : 'col-md-12'}>
1052
- { this.overview() }
1053
- <Circos queries={this.state.queries}
1054
- program={this.state.program} collapsed="true"/>
1057
+ { this.overviewJSX() }
1058
+ { this.isHitsAvailable()
1059
+ ? <Circos queries={this.state.queries}
1060
+ program={this.state.program} collapsed="true"/>
1061
+ : <span></span> }
1055
1062
  {
1056
1063
  _.map(this.state.queries, _.bind(function (query) {
1057
1064
  return (
@@ -1065,31 +1072,10 @@ var Report = React.createClass({
1065
1072
  );
1066
1073
  },
1067
1074
 
1068
- /**
1069
- * Returns true if sidebar should be shown.
1070
- *
1071
- * Sidebar is not shown if there is only one query and there are no hits
1072
- * corresponding to the query.
1073
- */
1074
- shouldShowSidebar: function () {
1075
- return !(this.state.queries.length == 1 &&
1076
- this.state.queries[0].hits.length == 0);
1077
- },
1078
-
1079
- /**
1080
- * Returns true if index should be shown in the sidebar.
1081
- *
1082
- * Index is not shown in the sidebar if there are more than eight queries
1083
- * in total.
1084
- */
1085
- shouldShowIndex: function () {
1086
- return this.state.queries.length <= 8;
1087
- },
1088
-
1089
1075
  /**
1090
1076
  * Renders report overview.
1091
1077
  */
1092
- overview: function () {
1078
+ overviewJSX: function () {
1093
1079
  return (
1094
1080
  <div
1095
1081
  className="overview">
@@ -1104,8 +1090,8 @@ var Report = React.createClass({
1104
1090
  }).join(", ")
1105
1091
  }
1106
1092
  <br/>
1107
- Total: {this.state.stats.nsequences} sequences, {this.state
1108
- .stats.ncharacters} characters
1093
+ Total: {this.state.stats.nsequences} sequences,
1094
+ {this.state.stats.ncharacters} characters
1109
1095
  <br/>
1110
1096
  <br/>
1111
1097
  {
@@ -1118,106 +1104,86 @@ var Report = React.createClass({
1118
1104
  );
1119
1105
  },
1120
1106
 
1121
- componentDidMount: function () {
1122
- this.fetchResults();
1123
- },
1107
+
1108
+ // Controller //
1124
1109
 
1125
1110
  /**
1126
- * Fetch results.
1111
+ * Returns true if results have been fetched.
1112
+ *
1113
+ * A holding message is shown till results are fetched.
1127
1114
  */
1128
- fetchResults: function () {
1129
- var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
1130
-
1131
- $.getJSON(location.pathname + '.json')
1132
- .complete(_.bind(function (jqXHR) {
1133
- switch (jqXHR.status) {
1134
- case 202:
1135
- var interval;
1136
- if (intervals.length === 1) {
1137
- interval = intervals[0];
1138
- }
1139
- else {
1140
- interval = intervals.shift;
1141
- }
1142
- setTimeout(this.fetchResults, interval);
1143
- break;
1144
- case 200:
1145
- this.updatePage(jqXHR.responseJSON);
1146
- break;
1147
- case 404:
1148
- case 400:
1149
- case 500:
1150
- showErrorModal(jqXHR.responseJSON);
1151
- break;
1152
- }
1153
- }, this));
1115
+ isResultAvailable: function () {
1116
+ return this.state.queries.length >= 1;
1154
1117
  },
1155
1118
 
1156
- updatePage: function(responseJSON) {
1157
- var queries = responseJSON.queries;
1119
+ isHitsAvailable: function () {
1120
+ var cnt = 0;
1121
+ _.each(this.state.queries, function (query) {
1122
+ if(query.hits.length == 0) cnt++;
1123
+ });
1124
+ return !(cnt == this.state.queries.length);
1125
+ },
1158
1126
 
1159
- // Render results for first 50 queries and set flag if total queries is
1160
- // more than 250.
1161
- var numHits = 0;
1162
- responseJSON.veryBig = queries.length > 250;
1163
- //responseJSON.veryBig = !_.every(queries, (query) => {
1164
- //numHits += query.hits.length;
1165
- //return (numHits <= 500);
1166
- //});
1167
- responseJSON.queries = queries.splice(0, 50);
1168
- this.setState(responseJSON);
1127
+ /**
1128
+ * Returns true if sidebar should be shown.
1129
+ *
1130
+ * Sidebar is not shown if there is only one query and there are no hits
1131
+ * corresponding to the query.
1132
+ */
1133
+ shouldShowSidebar: function () {
1134
+ return !(this.state.queries.length == 1 &&
1135
+ this.state.queries[0].hits.length == 0);
1136
+ },
1169
1137
 
1170
- // Render results for remaining queries.
1171
- var update = function () {
1172
- if (queries.length > 0) {
1173
- this.setState({
1174
- queries: this.state.queries.concat(queries.splice(0, 50))
1175
- });
1176
- setTimeout(update.bind(this), 500);
1177
- }
1178
- else {
1179
- this.componentFinishedUpdating();
1180
- }
1181
- };
1182
- setTimeout(update.bind(this), 500);
1138
+ /**
1139
+ * Returns true if index should be shown in the sidebar.
1140
+ *
1141
+ * Index is not shown in the sidebar if there are more than eight queries
1142
+ * in total.
1143
+ */
1144
+ shouldShowIndex: function () {
1145
+ return this.state.queries.length <= 8;
1183
1146
  },
1184
1147
 
1185
1148
  /**
1186
- * Locks Sidebar in its position, prevents folding of hits during
1187
- * text-selection, etc.
1149
+ * Called after first call to render. The results may not be available at
1150
+ * this stage and thus results DOM cannot be scripted here, unless using
1151
+ * delegated events bound to the window, document, or body.
1188
1152
  */
1189
- componentFinishedUpdating: function () {
1190
- this.affixSidebar();
1191
- this.shouldShowIndex() && this.setupScrollSpy();
1192
- this.setupHitSelection();
1193
- this.setupDownloadLinks();
1153
+ componentDidMount: function () {
1154
+ // This sets up an event handler which enables users to select text
1155
+ // from hit header without collapsing the hit.
1156
+ this.preventCollapseOnSelection();
1194
1157
  },
1195
1158
 
1196
1159
  /**
1197
- * Affixes the sidebar.
1198
- *
1199
- * TODO: can't this be done with CSS?
1160
+ * Called after each state change. Only a part of results DOM may be
1161
+ * available after a state change.
1200
1162
  */
1201
- affixSidebar: function () {
1202
- var $sidebar = $('.sidebar');
1203
- $sidebar.affix({
1204
- offset: {
1205
- top: $sidebar.offset().top
1206
- }
1207
- });
1163
+ componentDidUpdate: function () {
1164
+ // We track the number of updates to the component.
1165
+ this.updateCycle += 1;
1166
+
1167
+ // Lock sidebar in its position on first update of
1168
+ // results DOM.
1169
+ if (this.updateCycle === 1 ) this.affixSidebar();
1208
1170
  },
1209
1171
 
1210
1172
  /**
1211
- * For the query in viewport, highlights corresponding entry in the index.
1173
+ * Prevents folding of hits during text-selection, etc.
1212
1174
  */
1213
- setupScrollSpy: function () {
1214
- $('body').scrollspy({target: '.sidebar'});
1175
+
1176
+ /**
1177
+ * Called after all results have been rendered.
1178
+ */
1179
+ componentFinishedUpdating: function () {
1180
+ this.shouldShowIndex() && this.setupScrollSpy();
1215
1181
  },
1216
1182
 
1217
1183
  /**
1218
1184
  * Prevents folding of hits during text-selection.
1219
1185
  */
1220
- setupHitSelection: function () {
1186
+ preventCollapseOnSelection: function () {
1221
1187
  $('body').on('mousedown', ".hit > .section-header > h4", function (event) {
1222
1188
  var $this = $(this);
1223
1189
  $this.on('mouseup mousemove', function handler(event) {
@@ -1234,28 +1200,74 @@ var Report = React.createClass({
1234
1200
  });
1235
1201
  },
1236
1202
 
1237
- // Download links.
1238
- //
1239
- // Handles downloading files referenced by links with class 'download'.
1240
- setupDownloadLinks: function () {
1241
- $(document).on('click', '.download', function (event) {
1242
- event.preventDefault();
1243
- event.stopPropagation();
1203
+ /**
1204
+ * Affixes the sidebar.
1205
+ */
1206
+ affixSidebar: function () {
1207
+ var $sidebar = $('.sidebar');
1208
+ $sidebar.affix({
1209
+ offset: {
1210
+ top: $sidebar.offset().top
1211
+ }
1212
+ });
1213
+ },
1244
1214
 
1245
- var $anchor = $(this);
1215
+ /**
1216
+ * For the query in viewport, highlights corresponding entry in the index.
1217
+ */
1218
+ setupScrollSpy: function () {
1219
+ $('body').scrollspy({target: '.sidebar'});
1220
+ },
1246
1221
 
1247
- if ($anchor.is(':disabled')) return;
1222
+ /**
1223
+ * Event-handler when hit is selected
1224
+ * Adds glow to hit component.
1225
+ * Updates number of Fasta that can be downloaded
1226
+ */
1227
+ selectHit: function (id) {
1248
1228
 
1249
- var url = $anchor.attr('href');
1229
+ var checkbox = $("#" + id);
1230
+ var num_checked = $('.hit-links :checkbox:checked').length;
1250
1231
 
1251
- $.get(url)
1252
- .done(function (data) {
1253
- window.location.href = url;
1254
- })
1255
- .fail(function (jqXHR, status, error) {
1256
- SequenceServer.showErrorModal(jqXHR, function () {});
1257
- });
1258
- });
1232
+ if (!checkbox || !checkbox.val()) {
1233
+ return;
1234
+ }
1235
+
1236
+ var $hit = $(checkbox.data('target'));
1237
+
1238
+ // Highlight selected hit and sync checkboxes if sequence viewer is open.
1239
+ if (checkbox.is(":checked")) {
1240
+ $hit
1241
+ .addClass('glow')
1242
+ .find(":checkbox").not(checkbox).check();
1243
+ var $a = $('.download-fasta-of-selected');
1244
+ var $b = $('.download-alignment-of-selected');
1245
+ $b.enable()
1246
+ var $n = $a.find('span');
1247
+ $a
1248
+ .enable()
1249
+ }
1250
+
1251
+ else {
1252
+ $hit
1253
+ .removeClass('glow')
1254
+ .find(":checkbox").not(checkbox).uncheck();
1255
+ }
1256
+
1257
+ if (num_checked >= 1)
1258
+ {
1259
+ var $a = $('.download-fasta-of-selected');
1260
+ var $b = $('.download-alignment-of-selected');
1261
+ $a.find('.text-bold').html(num_checked);
1262
+ $b.find('.text-bold').html(num_checked);
1263
+ }
1264
+
1265
+ if (num_checked == 0) {
1266
+ var $a = $('.download-fasta-of-selected');
1267
+ var $b = $('.download-alignment-of-selected');
1268
+ $a.addClass('disabled').find('.text-bold').html('');
1269
+ $b.addClass('disabled').find('.text-bold').html('');
1270
+ }
1259
1271
  },
1260
1272
  });
1261
1273