aboutsummaryrefslogtreecommitdiff
path: root/wqflask
diff options
context:
space:
mode:
authorBonfaceKilz2021-01-06 01:18:20 +0300
committerGitHub2021-01-06 01:18:20 +0300
commit509bfdfe7eae156714229a6c964270006788ac4e (patch)
treee719a96ece0cbb096cf9c21778f6fc551e1af343 /wqflask
parent7474529d86fbd5e21a373a2f96bb2fffe4dea478 (diff)
parent7032f7b05deac0bbd2e868ffd3fe42dd1c97fb7d (diff)
downloadgenenetwork2-509bfdfe7eae156714229a6c964270006788ac4e.tar.gz
Merge pull request #404 from Uditgulati/snp_browser_changes
WIP: Snp browser changes
Diffstat (limited to 'wqflask')
-rw-r--r--wqflask/tests/unit/wqflask/test_server_side.py31
-rw-r--r--wqflask/wqflask/search_results.py16
-rw-r--r--wqflask/wqflask/server_side.py93
-rw-r--r--wqflask/wqflask/snp_browser/snp_browser.py54
-rw-r--r--wqflask/wqflask/static/new/css/snp_browser.css6
-rw-r--r--wqflask/wqflask/templates/search_result_page.html38
-rw-r--r--wqflask/wqflask/templates/snp_browser.html142
-rw-r--r--wqflask/wqflask/views.py42
8 files changed, 349 insertions, 73 deletions
diff --git a/wqflask/tests/unit/wqflask/test_server_side.py b/wqflask/tests/unit/wqflask/test_server_side.py
new file mode 100644
index 00000000..4f91d8ca
--- /dev/null
+++ b/wqflask/tests/unit/wqflask/test_server_side.py
@@ -0,0 +1,31 @@
+import unittest
+
+from wqflask.server_side import ServerSideTable
+
+
+class TestServerSideTableTests(unittest.TestCase):
+ """
+ Test the ServerSideTable class
+
+ test table:
+ first, second, third
+ 'd', 4, 'zz'
+ 'b', 2, 'aa'
+ 'c', 1, 'ss'
+ """
+
+ def test_get_page(self):
+ rows_count = 3
+ table_rows = [
+ {'first': 'd', 'second': 4, 'third': 'zz'},
+ {'first': 'b', 'second': 2, 'third': 'aa'},
+ {'first': 'c', 'second': 1, 'third': 'ss'},
+ ]
+ headers = ['first', 'second', 'third']
+ request_args = {'sEcho': '1', 'iSortCol_0': '1', 'iSortingCols': '1', 'sSortDir_0': 'asc', 'iDisplayStart': '0', 'iDisplayLength': '3'}
+
+ test_page = ServerSideTable(rows_count, table_rows, headers, request_args).get_page()
+ self.assertEqual(test_page['sEcho'], '1')
+ self.assertEqual(test_page['iTotalRecords'], 'nan')
+ self.assertEqual(test_page['iTotalDisplayRecords'], '3')
+ self.assertEqual(test_page['data'], [{'first': 'b', 'second': 2, 'third': 'aa'}, {'first': 'c', 'second': 1, 'third': 'ss'}, {'first': 'd', 'second': 4, 'third': 'zz'}])
diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py
index 0d0894a4..c4ea2921 100644
--- a/wqflask/wqflask/search_results.py
+++ b/wqflask/wqflask/search_results.py
@@ -28,9 +28,9 @@ class SearchResultPage(object):
#maxReturn = 3000
def __init__(self, kw):
- """This class gets invoked after hitting submit on the main menu (in
-views.py).
-
+ """
+ This class gets invoked after hitting submit on the main menu (in
+ views.py).
"""
###########################################
@@ -86,7 +86,6 @@ views.py).
else:
self.gen_search_result()
-
def gen_search_result(self):
"""
Get the info displayed in the search result table from the set of results computed in
@@ -161,7 +160,14 @@ views.py).
trait_dict[key] = trait_dict[key].decode('utf-8')
trait_list.append(trait_dict)
- self.trait_list = json.dumps(trait_list)
+ self.trait_list = trait_list
+
+ if this_trait.dataset.type == "ProbeSet":
+ self.header_data_names = ['index', 'display_name', 'symbol', 'description', 'location', 'mean', 'lrs_score', 'lrs_location', 'additive']
+ elif this_trait.dataset.type == "Publish":
+ self.header_data_names = ['index', 'display_name', 'description', 'mean', 'authors', 'pubmed_text', 'lrs_score', 'lrs_location', 'additive']
+ elif this_trait.dataset.type == "Geno":
+ self.header_data_names = ['index', 'display_name', 'location']
def search(self):
"""
diff --git a/wqflask/wqflask/server_side.py b/wqflask/wqflask/server_side.py
new file mode 100644
index 00000000..5f764767
--- /dev/null
+++ b/wqflask/wqflask/server_side.py
@@ -0,0 +1,93 @@
+# handles server side table processing
+
+
+
+class ServerSideTable(object):
+ """
+ This class is used to do server-side processing
+ on the DataTables table such as paginating, sorting,
+ filtering(not implemented) etc. This takes the load off
+ the client-side and reduces the size of data interchanged.
+
+ Usage:
+ ServerSideTable(table_data, request_values)
+ where,
+ `table_data` must have data members
+ `rows_count` as number of rows in the table,
+ `table_rows` as data rows of the table,
+ `header_data_names` as headers names of the table.
+
+ `request_values` must have request arguments values
+ including the DataTables server-side processing arguments.
+
+ Have a look at snp_browser_table() function in
+ wqflask/wqflask/views.py for reference use.
+ """
+
+ def __init__(self, rows_count, table_rows, header_data_names, request_values):
+ self.request_values = request_values
+ self.sEcho = self.request_values['sEcho']
+
+ self.rows_count = rows_count
+ self.table_rows = table_rows
+ self.header_data_names = header_data_names
+
+ self.sort_rows()
+ self.paginate_rows()
+
+ def sort_rows(self):
+ """
+ Sorts the rows taking in to account the column (or columns) that the
+ user has selected.
+ """
+ def is_reverse(str_direction):
+ """ Maps the 'desc' and 'asc' words to True or False. """
+ return True if str_direction == 'desc' else False
+
+ if (self.request_values['iSortCol_0'] != "") and (int(self.request_values['iSortingCols']) > 0):
+ for i in range(0, int(self.request_values['iSortingCols'])):
+ column_number = int(self.request_values['iSortCol_' + str(i)])
+ column_name = self.header_data_names[column_number - 1]
+ sort_direction = self.request_values['sSortDir_' + str(i)]
+ self.table_rows = sorted(self.table_rows,
+ key=lambda x: x[column_name],
+ reverse=is_reverse(sort_direction))
+
+ def paginate_rows(self):
+ """
+ Selects a subset of the filtered and sorted data based on if the table
+ has pagination, the current page and the size of each page.
+ """
+ def requires_pagination():
+ """ Check if the table is going to be paginated """
+ if self.request_values['iDisplayStart'] != "":
+ if int(self.request_values['iDisplayLength']) != -1:
+ return True
+ return False
+
+ if not requires_pagination():
+ return
+
+ start = int(self.request_values['iDisplayStart'])
+ length = int(self.request_values['iDisplayLength'])
+
+ # if search returns only one page
+ if len(self.table_rows) <= length:
+ # display only one page
+ self.table_rows = self.table_rows[start:]
+ else:
+ limit = -len(self.table_rows) + start + length
+ if limit < 0:
+ # display pagination
+ self.table_rows = self.table_rows[start:limit]
+ else:
+ # display last page of pagination
+ self.table_rows = self.table_rows[start:]
+
+ def get_page(self):
+ output = {}
+ output['sEcho'] = str(self.sEcho)
+ output['iTotalRecords'] = str(float('Nan'))
+ output['iTotalDisplayRecords'] = str(self.rows_count)
+ output['data'] = self.table_rows
+ return output
diff --git a/wqflask/wqflask/snp_browser/snp_browser.py b/wqflask/wqflask/snp_browser/snp_browser.py
index 6c3fcf53..a52399a2 100644
--- a/wqflask/wqflask/snp_browser/snp_browser.py
+++ b/wqflask/wqflask/snp_browser/snp_browser.py
@@ -4,7 +4,7 @@ import string
from PIL import (Image)
from utility.logger import getLogger
-logger = getLogger(__name__ )
+logger = getLogger(__name__)
from base import species
from base import webqtlConfig
@@ -14,21 +14,21 @@ class SnpBrowser(object):
def __init__(self, start_vars):
self.strain_lists = get_browser_sample_lists()
self.initialize_parameters(start_vars)
- self.limit_number = 10000
if self.first_run == "false":
self.filtered_results = self.get_browser_results()
+ self.table_rows = self.get_table_rows()
+ self.rows_count = len(self.table_rows)
- if len(self.filtered_results) <= self.limit_number:
- self.table_rows = self.get_table_rows()
- else:
- self.empty_columns = None
+ del self.filtered_results
+
+ if 'sEcho' not in start_vars:
self.table_rows = []
if self.limit_strains == "true":
- self.header_fields, self.empty_field_count = get_header_list(variant_type = self.variant_type, strains = self.chosen_strains, empty_columns = self.empty_columns)
+ self.header_fields, self.empty_field_count, self.header_data_names = get_header_list(variant_type = self.variant_type, strains = self.chosen_strains, empty_columns = self.empty_columns)
else:
- self.header_fields, self.empty_field_count = get_header_list(variant_type = self.variant_type, strains = self.strain_lists, species = self.species_name, empty_columns = self.empty_columns)
+ self.header_fields, self.empty_field_count, self.header_data_names = get_header_list(variant_type = self.variant_type, strains = self.strain_lists, species = self.species_name, empty_columns = self.empty_columns)
def initialize_parameters(self, start_vars):
if 'first_run' in start_vars:
@@ -368,19 +368,19 @@ class SnpBrowser(object):
#ZS: list of booleans representing which columns are entirely empty, so they aren't displayed on the page; only including ones that are sometimes empty (since there's always a location, etc)
self.empty_columns = {
- "snp_source": "false",
- "conservation_score": "false",
- "gene_name": "false",
- "transcript": "false",
- "exon": "false",
- "domain_2": "false",
- "function": "false",
- "function_details": "false"
- }
+ "snp_source": "false",
+ "conservation_score": "false",
+ "gene_name": "false",
+ "transcript": "false",
+ "exon": "false",
+ "domain_2": "false",
+ "function": "false",
+ "function_details": "false"
+ }
the_rows = []
for i, result in enumerate(self.filtered_results):
- this_row = []
+ this_row = {}
if self.variant_type == "SNP":
snp_name, rs, chr, mb, alleles, gene, transcript, exon, domain, function, function_details, snp_source, conservation_score, snp_id = result[:14]
allele_value_list = result[14:]
@@ -520,13 +520,10 @@ class SnpBrowser(object):
"source_name": str(source_name)
}
#this_row = [indel_name, indel_chr, indel_mb_s, indel_mb_e, indel_strand, indel_type, indel_size, indel_sequence, source_name]
- else:
- this_row = {}
the_rows.append(this_row)
return the_rows
-
def include_record(self, domain, function, snp_source, conservation_score):
""" Decide whether to add this record """
@@ -674,9 +671,13 @@ def get_header_list(variant_type, strains, species = None, empty_columns = None)
empty_field_count = 0 #ZS: This is an awkward way of letting the javascript know the index where the allele value columns start; there's probably a better way of doing this
header_fields = []
+ header_data_names = []
if variant_type == "SNP":
header_fields.append(['Index', 'SNP ID', 'Chr', 'Mb', 'Alleles', 'Source', 'ConScore', 'Gene', 'Transcript', 'Exon', 'Domain 1', 'Domain 2', 'Function', 'Details'])
+ header_data_names = ['index', 'snp_name', 'chr', 'mb_formatted', 'alleles', 'snp_source', 'conservation_score', 'gene_name', 'transcript', 'exon', 'domain_1', 'domain_2', 'function', 'function_details']
+
header_fields.append(strain_list)
+ header_data_names += strain_list
if empty_columns != None:
if empty_columns['snp_source'] == "false":
@@ -703,11 +704,16 @@ def get_header_list(variant_type, strains, species = None, empty_columns = None)
if empty_columns['function_details'] == "false":
empty_field_count += 1
header_fields[0].remove('Details')
+
+ for col in empty_columns.keys():
+ if empty_columns[col] == "false":
+ header_data_names.remove(col)
elif variant_type == "InDel":
header_fields = ['Index', 'ID', 'Type', 'InDel Chr', 'Mb Start', 'Mb End', 'Strand', 'Size', 'Sequence', 'Source']
+ header_data_names = ['index', 'indel_name', 'indel_type', 'indel_chr', 'indel_mb_s', 'indel_mb_e', 'indel_strand', 'indel_size', 'indel_sequence', 'source_name']
- return header_fields, empty_field_count
+ return header_fields, empty_field_count, header_data_names
def get_effect_details_by_category(effect_name = None, effect_value = None):
gene_list = []
@@ -868,8 +874,6 @@ def get_gene_id_name_dict(species_id, gene_name_list):
if len(results) > 0:
for item in results:
gene_id_name_dict[item[1]] = item[0]
- else:
- pass
return gene_id_name_dict
@@ -883,7 +887,7 @@ def check_if_in_gene(species_id, chr, mb):
query = """SELECT geneId,geneSymbol
FROM GeneList
WHERE chromosome = '{0}' AND
- (txStart < {1} AND txEnd > {1}); """.format(species_id, chr, mb)
+ (txStart < {1} AND txEnd > {1}); """.format(chr, mb)
result = g.db.execute(query).fetchone()
diff --git a/wqflask/wqflask/static/new/css/snp_browser.css b/wqflask/wqflask/static/new/css/snp_browser.css
index 30fe9a59..a7942d2a 100644
--- a/wqflask/wqflask/static/new/css/snp_browser.css
+++ b/wqflask/wqflask/static/new/css/snp_browser.css
@@ -6,6 +6,10 @@ table.dataTable thead th {
vertical-align: bottom;
}
+table.dataTable tbody tr.selected {
+ background-color: #ffee99;
+}
+
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
@@ -18,7 +22,7 @@ table.dataTable thead .sorting_desc_disabled {
table.dataTable thead th{
border-right: 1px solid white;
color: white;
- background-color: royalblue;
+ background-color: #369;
}
table.dataTable tbody td {
diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html
index f2334512..765c4ab8 100644
--- a/wqflask/wqflask/templates/search_result_page.html
+++ b/wqflask/wqflask/templates/search_result_page.html
@@ -160,6 +160,16 @@
<script type="text/javascript" charset="utf-8">
$(document).ready( function () {
+
+ var getParams = function(url) {
+ let parser = document.createElement('a');
+ parser.href = url;
+ let params = parser.search.substring(1);
+ if(params.length > 0) {
+ return ('?'+params);
+ }
+ return params;
+ };
$('#trait_table tr').click(function(event) {
if (event.target.type !== 'checkbox') {
@@ -225,7 +235,7 @@
'data': null,
'width': "25px",
'orderDataType': "dom-checkbox",
- 'orderSequence': [ "desc", "asc"],
+ 'orderable': false,
'render': function(data, type, row, meta) {
return '<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + data.hmac + '">'
}
@@ -373,15 +383,33 @@
}{% endif %}
],
"order": [[1, "asc" ]],
- 'sDom': "itirp",
+ {% if dataset.type != 'Geno' %}
+ buttons: [
+ {
+ extend: 'columnsToggle',
+ columns: function( idx, data, node ) {
+ if (idx != 0) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ postfixButtons: [ 'colvisRestore' ]
+ }
+ ],
+ 'sDom': "Bpitirp",
+ {% else %}
+ 'sDom': "pitirp",
+ {% endif %}
'iDisplayLength': 500,
'deferRender': true,
'paging': true,
'orderClasses': true,
'processing': true,
- 'language': {
- 'loadingRecords': '&nbsp;',
- 'processing': 'Loading...'
+ 'bServerSide': true,
+ 'sAjaxSource': '/search_table'+getParams(window.location.href),
+ 'infoCallback': function(settings, start, end, max, total, pre) {
+ return "Showing " + start + " to " + (start + this.api().data().length - 1) + " of " + total + " entries";
}
} );
diff --git a/wqflask/wqflask/templates/snp_browser.html b/wqflask/wqflask/templates/snp_browser.html
index a96b8e3b..b9aea570 100644
--- a/wqflask/wqflask/templates/snp_browser.html
+++ b/wqflask/wqflask/templates/snp_browser.html
@@ -15,7 +15,7 @@
<input type="hidden" name="first_run" value="{{ first_run }}">
<input type="hidden" name="chosen_strains_mouse" value="{{ chosen_strains_mouse|join(",") }}">
<input type="hidden" name="chosen_strains_rat" value="{{ chosen_strains_rat|join(",") }}">
- <div class="col-xs-4" style="padding-left: 0px;">
+ <div class="col-xs-4" style="width: 260px; padding-left: 30px; padding-right: 0px;">
<div class="form-group row" style="margin-bottom: 5px;">
<label for="snp_or_indel" style="text-align: right;" class="col-xs-4 col-form-label"><b>Type:</b></label>
<div class="col-xs-8">
@@ -74,7 +74,7 @@
</div>
</div>
</div>
- <div class="col-xs-4" style="padding-left: 0px;">
+ <div class="col-xs-4" style="width: 310px; padding-left: 0px; padding-right: 20px;">
<div class="form-group row" style="margin-bottom: 10px;">
<label for="strains" style="text-align: right;" class="col-xs-4 col-form-label"><b>Strains:</b></label>
<div class="col-xs-8">
@@ -107,8 +107,14 @@
</div>
</div>
</div>
+ <div class="form-group row">
+ <label class="col-xs-4 col-form-label"></label>
+ <div class="col-xs-8" style="margin-top: 65px;">
+ <input class="btn btn-primary" type="button" name="export_csv" value="Export to CSV">
+ </div>
+ </div>
</div>
- <div class="col-xs-4" style="padding-left: 20px;">
+ <div class="col-xs-4" style="width: 310px; padding-left: 20px;">
<div class="form-group row" style="margin-bottom: 5px;">
<label for="domain" style="text-align: right;" class="col-xs-4 col-form-label"><b>Domain:</b></label>
<div class="col-xs-8">
@@ -182,24 +188,21 @@
</div>
<div style="margin-top: 20px;">
- {% if filtered_results is defined %}
- {% if filtered_results|length > limit_number %}
- There are more than 10000 results. Consider limiting your search to a smaller range.
- {% else %}
+ {% if table_rows is defined %}
<table class="dataTable cell-border nowrap" id="results_table" style="float: left;">
<thead>
<tr>
<th></th>
{% if header_fields|length == 2 %}
{% for header in header_fields[0] %}
- <th data-export="{{ header }}">{{ header }}</th>
+ <th data-export="{{ header }}" name="{{ header }}">{{ header }}</th>
{% endfor %}
{% for strain in header_fields[1] %}
- <th data-export="{{ strain }}" style="align: center; text-align: center; line-height: 15px;">{% for letter in strain %}<div style="transform: rotate(90deg);">{{ letter }}</div>{% endfor %}</th>
+ <th data-export="{{ strain }}" name="{{ strain }}" style="align: center; text-align: center; line-height: 12px;">{% for letter in strain|reverse %}<div style="transform: rotate(270deg);">{{ letter }}</div>{% endfor %}</th>
{% endfor %}
{% else %}
{% for header in header_fields %}
- <th data-export="{{ header }}">{{ header }}</th>
+ <th data-export="{{ header }}" name="{{ header }}">{{ header }}</th>
{% endfor %}
{% endif %}
</tr>
@@ -208,7 +211,6 @@
<td colspan="100%" align="center"><br><b><font size="15">Loading...</font></b><br></td>
</tbody>
</table>
- {% endif %}
{% endif %}
</div>
</div>
@@ -222,12 +224,24 @@
<script language="javascript" type="text/javascript" src="/static/new/javascript/typeahead_rn6.json"></script>
<script type='text/javascript'>
- var json_rows = {{ table_rows|safe }};
var empty_columns = {{ empty_columns|safe }};
+
+ var remain_field_count = 15 - {{ empty_field_count|safe }};
+ var total_field_count = 15 - {{ empty_field_count|safe }} + {{ allele_list|safe|length }};
</script>
<script language="javascript">
+ var getParams = function(url) {
+ let parser = document.createElement('a');
+ parser.href = url;
+ let params = parser.search.substring(1);
+ if(params.length > 0) {
+ return ('?'+params);
+ }
+ return params;
+ };
+
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
@@ -260,19 +274,20 @@
source: substringMatcher(rat_genes)
});
- {% if filtered_results is defined %}
+ {% if table_rows is defined %}
$("#results_table").DataTable( {
- 'data': json_rows,
{% if variant_type == "SNP" %}
'columns': [
{
'data': null,
+ 'className': 'dt-body-center',
'orderable': false,
'render': function(data, type, row, meta) {
- return '<input type="checkbox" name="trait_check">'
+ return '<input type="checkbox" class="checkbox" id="variant_checkbox" onchange="onVarinatCheck(this)" name="trait_check">'
}
}, {
- 'data': 'index'
+ 'data': 'index',
+ 'className': 'dt-body-right'
}, {
'data': null,
'render': function(data, type, row, meta) {
@@ -283,27 +298,30 @@
}
}
}, {
- 'data': 'chr'
+ 'data': 'chr',
+ 'className': 'dt-body-center'
}, {
- 'data': 'mb_formatted'
+ 'data': 'mb_formatted',
+ 'className': 'dt-body-right'
}, {
'data': 'alleles'
}, {% if empty_columns['snp_source'] == "true" %}{
'data': null,
'render': function(data, type, row, meta) {
if (data.snp_source == "Sanger/UCLA") {
- return '<a href="' + data.source_urls[0] + '">Sanger</a><a href="' + data.source_urls[1] + '">UCLA</a>'
+ return '<a href="' + data.source_urls[0] + '">Sanger</a>, <a href="' + data.source_urls[1] + '">UCLA</a>'
} else {
return data.snp_source
}
}
}, {% endif %} {% if empty_columns['conservation_score'] == "true" %}{
- 'data': 'conservation_score'
+ 'data': 'conservation_score',
+ 'className': 'dt-body-right'
}, {% endif %} {% if empty_columns['gene_name'] == "true" %}{
'data': null,
'render': function(data, type, row, meta) {
if (data.gene_name != "") {
- return '<i>' + data.gene_name + '</i><br><a href="' + data.gene_link + '">NCBI</a>'
+ return '<i>' + data.gene_name + '</i>, <a href="' + data.gene_link + '">NCBI</a>'
} else {
return data.gene_name
}
@@ -330,6 +348,7 @@
}, {% endif %} {% for item in allele_list %} {
'data': null,
'orderable': false,
+ 'className': 'dt-body-center',
'render': function(data, type, row, meta) {
if (typeof data.allele_value_list[{{ loop.index - 1 }}][0] !== "undefined") {
return data.allele_value_list[{{ loop.index - 1 }}][0]
@@ -339,12 +358,9 @@
}
}{% if loop.index < allele_list|length %},{% endif %}{% endfor %}
],
- 'createdRow': function( row, data, dataIndex) {
- $('td', row).eq(0).attr("style", "text-align: center; padding: 4px 10px 2px 10px;");
- $('td', row).eq(1).attr("align", "right");
- for (i = {{ 15 - empty_field_count }}; i < ({{ 15 - empty_field_count }} + {{ allele_list|length }}); i++) {
+ 'createdRow': function(row, data, dataIndex) {
+ for (i = remain_field_count; i < total_field_count; i++) {
var this_allele = $('td', row).eq(i).text();
- $('td', row).eq(i).attr("style", "text-align: center; padding: 4px 10px 2px 10px;");
switch (this_allele) {
case "A":
$('td', row).eq(i).addClass('A_allele_color');
@@ -380,24 +396,29 @@
{
'data': null,
'render': function(data, type, row, meta) {
- return '<input type="checkbox" name="trait_check">'
+ return '<input type="checkbox" class="checkbox" id="variant_checkbox" onchange="onVarinatCheck(this)" name="trait_check">'
}
}, {
- 'data': 'index'
+ 'data': 'index',
+ 'className': 'dt-body-right'
}, {
'data': 'indel_name'
}, {
'data': 'indel_type'
}, {
- 'data': 'indel_chr'
+ 'data': 'indel_chr',
+ 'className': 'dt-body-center'
}, {
- 'data': 'indel_mb_s'
+ 'data': 'indel_mb_s',
+ 'className': 'dt-body-right'
}, {
- 'data': 'indel_mb_e'
+ 'data': 'indel_mb_e',
+ 'className': 'dt-body-right'
}, {
'data': 'indel_strand'
}, {
- 'data': 'indel_size'
+ 'data': 'indel_size',
+ 'className': 'dt-body-right'
}, {
'data': 'indel_sequence'
}, {
@@ -407,15 +428,28 @@
{% endif %}
'order': [[1, "asc" ]],
'sDom': "rtip",
- 'iDisplayLength': 500,
- 'processing': true,
- 'language': {
- 'loadingRecords': '&nbsp;',
- 'processing': 'Loading...'
+ 'iDisplayLength': 100,
+ 'bServerSide': true,
+ 'sAjaxSource': '/snp_browser_table'+getParams(window.location.href),
+ 'infoCallback': function(settings, start, end, max, total, pre) {
+ return "Showing " + start + " to " + (start + this.api().data().length - 1) + " of " + total + " entries";
}
});
{% endif %}
+ function onVarinatCheck(checkboxElem) {
+ if (checkboxElem.checked) {
+ if (!checkboxElem.parentElement.parentElement.classList.contains('selected')) {
+ checkboxElem.parentElement.parentElement.classList.add('selected')
+ }
+ }
+ else {
+ if (checkboxElem.parentElement.parentElement.classList.contains('selected')) {
+ checkboxElem.parentElement.parentElement.classList.remove('selected')
+ }
+ }
+ }
+
$("#species_select").change(function() {
this_species = $(this).val();
$("#strain_select").empty()
@@ -509,6 +543,40 @@
});
$("input[name=chosen_strains]").val(strain_list.join(","));
});
+
+
+
+ $("input[name=export_csv]").click(function() {
+ var csv = [];
+ var rows = document.querySelectorAll("table tr");
+
+ var headers = [];
+ var col_header = rows[0].querySelectorAll("th");
+ for(let i = 1; i < col_header.length; i++) {
+ headers.push(col_header[i].getAttribute("name"));
+ }
+ csv.push(headers.join(","));
+
+ for (let i = 1; i < rows.length; i++) {
+ var row = [], cols = rows[i].querySelectorAll("td");
+ var checkBox = rows[i].querySelector("input");
+
+ if(checkBox.checked == true) {
+ for (let j = 1; j < cols.length; j++)
+ row.push(cols[j].innerText);
+
+ csv.push(row.join(","));
+ }
+ }
+
+ var csvFile = new Blob([csv.join("\n")], {type: "text/csv"});
+ var downloadLink = document.createElement("a");
+ downloadLink.download = "variant_data.csv";
+ downloadLink.href = window.URL.createObjectURL(csvFile);
+ downloadLink.style.display = "none";
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+ });
</script>
{% endblock %}
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 25563e86..632e0d2d 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -26,6 +26,15 @@ import sqlalchemy
from wqflask import app
from flask import g, Response, request, make_response, render_template, send_from_directory, jsonify, redirect, url_for, send_file
+from wqflask import group_manager
+from wqflask import resource_manager
+from wqflask import search_results
+from wqflask import export_traits
+from wqflask import gsearch
+from wqflask import update_search_results
+from wqflask import docs
+from wqflask import news
+from wqflask import server_side
from wqflask.submit_bnw import get_bnw_input
from base.data_set import create_dataset, DataSet # Used by YAML in marker_regression
from wqflask.show_trait import show_trait
@@ -220,6 +229,26 @@ def search_page():
else:
return render_template("search_error.html")
+@app.route("/search_table", methods=('GET',))
+def search_page_table():
+ logger.info("in search_page table")
+ logger.info(request.url)
+
+ logger.info("request.args is", request.args)
+ the_search = search_results.SearchResultPage(request.args)
+
+ logger.info(type(the_search.trait_list))
+ logger.info(the_search.trait_list)
+
+ current_page = server_side.ServerSideTable(
+ len(the_search.trait_list),
+ the_search.trait_list,
+ the_search.header_data_names,
+ request.args,
+ ).get_page()
+
+ return flask.jsonify(current_page)
+
@app.route("/gsearch", methods=('GET',))
def gsearchact():
logger.info(request.url)
@@ -896,6 +925,19 @@ def db_info_page():
return render_template("info_page.html", **template_vars.__dict__)
+@app.route("/snp_browser_table", methods=('GET',))
+def snp_browser_table():
+ logger.info(request.url)
+ snp_table_data = snp_browser.SnpBrowser(request.args)
+ current_page = server_side.ServerSideTable(
+ snp_table_data.rows_count,
+ snp_table_data.table_rows,
+ snp_table_data.header_data_names,
+ request.args,
+ ).get_page()
+
+ return flask.jsonify(current_page)
+
@app.route("/tutorial/WebQTLTour", methods=('GET',))
def tutorial_page():
#ZS: Currently just links to GN1