Skip to content

DataTables load using AJAX, Search by metadata on studies page #1006

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Apr 1, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
616300f
add get_info to Studies object
squirrelo Mar 19, 2015
d595016
expand tests
squirrelo Mar 19, 2015
90313f7
use get_info in listing handler
squirrelo Mar 19, 2015
fb5eef3
condense view studies pages
squirrelo Mar 19, 2015
1ffd4bc
clean up naming, add form elements
squirrelo Mar 19, 2015
16257c4
load study tables through ajax
squirrelo Mar 19, 2015
9ccc098
add message for zero results
squirrelo Mar 19, 2015
abeb46f
add search and retrieve to page
squirrelo Mar 20, 2015
259bec2
fix bug in joins
squirrelo Mar 20, 2015
b8472a8
add full ajax search support
squirrelo Mar 20, 2015
cf6ca77
add error handling to datatables
squirrelo Mar 20, 2015
cd4f66e
alter/add tests for handlers to reflect changes
squirrelo Mar 20, 2015
ce4d334
fix tests
squirrelo Mar 20, 2015
58b28cd
implement Jess' suggestions
squirrelo Mar 21, 2015
1872555
Merge branch 'master' of https://github.com/biocore/qiita into cart
squirrelo Mar 22, 2015
4b7ffcf
remove unneeded format loop, format datatables better
squirrelo Mar 24, 2015
515f0f6
update tests
squirrelo Mar 24, 2015
21b17d6
Merge branch 'master' of https://github.com/biocore/qiita into cart
squirrelo Mar 24, 2015
04f87f4
specific ordering for PMIDs for testing
squirrelo Mar 24, 2015
ee4c9e2
flake8
squirrelo Mar 24, 2015
ef16d18
address issues
squirrelo Mar 26, 2015
fbe7c74
address comments
squirrelo Mar 26, 2015
e0f22d0
merge upstream/master
squirrelo Mar 27, 2015
0df6666
fix merge issues
squirrelo Mar 27, 2015
5085c35
more enhancements
squirrelo Mar 27, 2015
ea69fe8
address comments
squirrelo Mar 28, 2015
3a12f87
update tests
squirrelo Mar 28, 2015
25ae8f2
update schema and html files
squirrelo Mar 29, 2015
e01e224
fix search disabled and clearing table search
squirrelo Apr 1, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
load study tables through ajax
  • Loading branch information
squirrelo committed Mar 19, 2015
commit 16257c45d233e25fc840cd8cfbd36140a813490c
4 changes: 2 additions & 2 deletions qiita_pet/handlers/study_handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# -----------------------------------------------------------------------------

from .listing_handlers import (ListStudiesHandler, StudyApprovalList,
ShareStudyAJAX)
ShareStudyAJAX, SearchStudiesAJAX)
from .edit_handlers import StudyEditHandler, CreateStudyAJAX
from .description_handlers import (StudyDescriptionHandler,
PreprocessingSummaryHandler)
Expand All @@ -18,4 +18,4 @@
__all__ = ['ListStudiesHandler', 'StudyApprovalList', 'ShareStudyAJAX',
'StudyEditHandler', 'CreateStudyAJAX', 'StudyDescriptionHandler',
'PreprocessingSummaryHandler', 'EBISubmitHandler',
'MetadataSummaryHandler', 'VAMPSHandler']
'MetadataSummaryHandler', 'VAMPSHandler', 'SearchStudiesAJAX']
133 changes: 115 additions & 18 deletions qiita_pet/handlers/study_handlers/listing_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from qiita_core.exceptions import IncompetentQiitaDeveloperError
from qiita_db.user import User
from qiita_db.study import Study, StudyPerson
from qiita_db.search import QiitaStudySearch
from qiita_pet.handlers.base_handlers import BaseHandler
from qiita_pet.handlers.util import study_person_linkifier, pubmed_linkifier

Expand All @@ -35,15 +36,24 @@ def _get_shared_links_for_study(study):
return ", ".join(shared)


def _build_study_info(studytype, user=None):
def _build_study_info(studytype, user, studies=None):
"""builds list of namedtuples for study listings"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the docstring (namedtuples no longer apply). Although this function is private, I think having a bit of numpy doc will help future developers...

if studytype == "standard":
if studytype not in {"standard", "shared"}:
raise IncompetentQiitaDeveloperError("Must use private, shared, "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message doesn't seem to correspond to the conditional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woops, forgot to update. fixed.

"or public!")
# get list of studies for table
if studies:
# filter info to given studies
if studytype == "standard":
studylist = (user.user_studies |
Study.get_by_status('public')).intersection(studies)
elif studytype == "shared":
studylist = user.shared_studies.intersection(studies)
elif studytype == "standard":
studylist = user.user_studies | Study.get_by_status('public')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if possible, it would be nice to stick with either operators (this line), or methods (line 53), to improve consistency within the method being worked on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will stick with methods, since they are more universal.

elif studytype == "shared":
studylist = user.shared_studies
else:
raise IncompetentQiitaDeveloperError("Must use private, shared, "
"or public!")

if not studylist:
return []

Expand Down Expand Up @@ -85,22 +95,10 @@ class ListStudiesHandler(BaseHandler):
def get(self):
self.write(self.render_string('waiting.html'))
self.flush()
user = self.current_user
user_studies = yield Task(self._get_standard, user)
shared_studies = yield Task(self._get_shared, user)
all_emails_except_current = yield Task(self._get_all_emails)
all_emails_except_current.remove(self.current_user.id)
self.render('list_studies.html',
user_studies=user_studies, shared_studies=shared_studies,
all_emails_except_current=all_emails_except_current,
query='')

def _get_standard(self, user, callback):
callback(_build_study_info("standard", user))

def _get_shared(self, user, callback):
"""builds list of tuples for studies that are shared with user"""
callback(_build_study_info("shared", user))
all_emails_except_current=all_emails_except_current)

def _get_all_emails(self, callback):
callback(list(User.iter()))
Expand Down Expand Up @@ -154,3 +152,102 @@ def get(self):
users, links = yield Task(self._get_shared_for_study, study)

self.write(dumps({'users': users, 'links': links}))


class SearchStudiesAJAX(BaseHandler):
def _get_standard(self, user, callback):
callback(_build_study_info("standard", user))

def _get_shared(self, user, callback):
"""builds list of tuples for studies that are shared with user"""
callback(_build_study_info("shared", user))

@authenticated
def get(self, ignore):
search_type = self.get_argument('type')
user = self.get_argument('user')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you passing the user as an argument? It doesn't look like is needed as...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 211

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I'm saying... it's not needed, just use self.current_user...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's to prevent URL hacking

query = self.get_argument('query')
echo = int(self.get_argument('sEcho'))

res = None
if query != "":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if query

# Search for samples matching the query
search = QiitaStudySearch()
res, meta = search(query)
info = _build_study_info(search_type, self.current_user,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this call only needs to be done once and can be done outside of the conditions if res is set to None in the else block

studies=search.keys())
else:
# show everything
info = _build_study_info(search_type, self.current_user)

# build the table json
results = {
"sEcho": echo,
"iTotalRecords": len(info),
"iTotalDisplayRecords": len(info),
"aaData": []
}
if search_type == "standard":
for row, s in enumerate(info):
# build the HTML elements needed for table cell
meta_complete = "ok" if s.meta_complete else "remove"
share = "Not Available" if s.status == 'public' else \
("<span id='shared_html_{0}'>{1}</span><br/>"
"<a class='btn btn-primary' data-toggle='modal' "
"data-target='#share-study-modal-view' "
"onclick='modify_sharing({0});'>Modify</a>".format(
s.id, s.shared))
# add study to table
results['aaData'].append([
"<input type='checkbox' value='%s'>" % s.id,

"<a href='#'' data-toggle='modal' "
"data-target='#study-abstract-modal' "
"onclick='fillAbstract(\"user-studies-table\", {0})'>"
"<span class=\'glyphicon glyphicon-file\' "
"aria-hidden=\'true\'></span></a> | "
"<a href=\'/study/description/{1}\' "
"id=\'study{0}-title\'>{2}</a>".format(
str(row), str(s.id), s.title),

s.abstract,
s.id,
"<span class='glyphicon glyphicon-%s'></span>" %
meta_complete,
s.num_samples_collected,
s.num_raw_data,
share,
s.pi,
s.pmids,
s.status
])
elif search_type == "shared":
for row, s in enumerate(info):
# build the HTML elements needed for table cell
meta_complete = "ok" if s.meta_complete else "remove"
# add study to table
results['aaData'].append([
"<input type='checkbox' value='%s'>" % s.id,

"<a href='#'' data-toggle='modal' "
"data-target='#study-abstract-modal' "
"onclick='fillAbstract(\"shared-studies-table\", {0})'>"
"<span class=\'glyphicon glyphicon-file\' "
"aria-hidden=\'true\'></span></a> | "
"<a href=\'/study/description/{1}\' "
"id=\'study{0}-title\'>{2}</a>".format(
str(row), str(s.id), s.title),

s.abstract,
s.id,
s.owner,
"<span class='glyphicon glyphicon-%s'></span>" %
meta_complete,
s.num_samples_collected,
s.num_raw_data,
s.pi,
s.pmids
])

# return the json
self.write(dumps(results))
2 changes: 1 addition & 1 deletion qiita_pet/static/js/qiita.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ function bootstrapAlert(message, severity){
}

function fillAbstract(table, row) {
$('#title-text-area').text($('#study' + row + "-title").text());
$('#title-text-area').text($('#' + table).find('#study' + row + "-title").text());
$('#abstract-text-area').text($('#'+table).dataTable().fnGetData(row, 2));
}
110 changes: 16 additions & 94 deletions qiita_pet/templates/list_studies.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,29 @@

<script type="text/javascript">
var current_study;

var query = "";
$(document).ready(function() {
var randomnumber=Math.floor(Math.random()*11);
$('#user-studies-table').dataTable({
order: [[10, "asc"], [ 1, "asc" ]],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this sort first by status and then by title? I would think sorting just by title would be a user's expectation, but I am not sure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, status then title. Figured it was best to have all public then all your own stuff separated in some form at first glance.

columnDefs: [{type:'natural', targets:[3,4,5,9]}, {"targets": [ 2 ],"visible": false}],
"oLanguage": {
"sSearch": "Refine search results:"
}
"sSearch": "Refine search results:",
"sLoadingRecords": "Loading table data"
},
"ajax": {
"url": "/study/search/?type=standard&user={{current_user.id}}&query=" + query + "&sEcho=" + randomnumber
}
});
$('#shared-studies-table').dataTable({
order: [[ 1, "asc" ]],
columnDefs: [{type:'natural', targets:[4,5,6]}, {"targets": [ 2 ],"visible": false}],
"oLanguage": {
"sSearch": "Refine search results:"
"sSearch": "Refine search results:",
"sLoadingRecords": "Loading table data"
},
"ajax": {
"url": "/study/search/?type=shared&user={{current_user.id}}&query=" + query + "&sEcho=" + randomnumber
}
});
$('#waiting').hide();
Expand Down Expand Up @@ -61,17 +70,16 @@
<h1>Search</h1>
<p><a href="#" data-toggle="modal" data-target="#searchexample">Search help</a></p>
<form id="search-form" name="search-form" class="form-inline" method="POST" action="/study/search/">
<input type="textbox" id="searchbox" name="searchbox" class="form-control" style="width:80%;white-space:nowrap;" value='{{query}}' />
<input type="textbox" id="searchbox" name="searchbox" class="form-control" style="width:80%;white-space:nowrap;" />
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
<div class="col-sm-12" id="searchmsg" name="searchmsg"></div>
</div>
<!--User Studies-->
<div class="row">
<div class="col-sm-12">
<div class="col-sm-12" id="user-studies-div">
<h1>Your Studies</h1>
{% if len(user_studies) %}
<table id="user-studies-table" class="display table-bordered table-hover">
<thead>
<tr>
Expand All @@ -89,65 +97,13 @@ <h1>Your Studies</h1>
</tr>
</thead>
<tbody>
{% set row = 0 %}
{% for s in user_studies %}
<tr>
<td><input type="checkbox" value="{{s.id}}"></td>
{# Title #}
<td><a href="#" data-toggle="modal" data-target="#study-abstract-modal" onclick="fillAbstract('user-studies-table', {{row}})"><span class="glyphicon glyphicon-file" aria-hidden="true"></span></a> | <a href="/study/description/{{ s.id }}" id="study{{row}}-title">{{ s.title }}</a></td>
{# Abstract (hidden on page) #}
<td>{% raw s.abstract %}</td>
{# Study ID #}
<td>{% raw s.id %}</td>
{# Metadata Comlete #}
{% if s.meta_complete %}
<td><span class="glyphicon glyphicon-ok"></span></td>
{% else %}
<td><span class="glyphicon glyphicon-remove"></span></td>
{% end %}
{# Samples #}
<td>{{ s.num_samples_collected }}</td>
{# Sequence Files #}
{% if s.num_raw_data %}
<td>{{ s.num_raw_data }}</td>
{% else %}
<td><span class="glyphicon glyphicon-remove"></span></td>
{% end %}
{# Shared #}
{% if s.status == 'public' %}
<td>Not available</td>
{% else %}
<td><span id="shared_html_{{ s.id }}">{% raw s.shared %}</span>
<br/>
<a class="btn btn-primary" data-toggle="modal" data-target="#share-study-modal-view" onclick="modify_sharing({{ s.id }});">Modify</a>
</td>
{% end %}
{# Principal Investigator #}
<td>{% raw s.pi %}</td>
{# Pubmed ID(s) #}
<td>{% raw s.pmids %}</td>
<td>{{ s.status }}</td>
</tr>
{% set row = row + 1 %}
{% end %}
</tbody>
</table>
{% else %}
<div id="jumbotron" class="jumbotron">
<h1><span class="glyphicon glyphicon-thumbs-down"></span> There are no studies available.</h1>
<p>
This means that you have not yet created a study. <a href="/study/create/">Create a study</a>.
</p>
</div>
</div>
</div>
{% end %}
<!--Shared Studies-->
<form id="study-sel-form" name="study-sel-form">
<div class="row">
<div class="col-sm-12">
<div class="col-sm-12" id="user-studies-div">
<h1>Shared Studies</h1>
{% if len(shared_studies) %}
<table id="shared-studies-table" class="display table-bordered table-hover">
<thead>
<tr>
Expand All @@ -164,42 +120,8 @@ <h1>Shared Studies</h1>
</tr>
</thead>
<tbody>
{% set row = 0 %}
{% for s in shared_studies %}
<tr>
<td><input type="checkbox" value="{{s.id}}"></td>
<td><a href="#" data-toggle="modal" data-target="#study-abstract-modal" onclick="fillAbstract('shared-studies-table', {{row}})"><span class="glyphicon glyphicon-file" aria-hidden="true"></span></a> | <a href="/study/description/{{ s.id }}" id="study{{row}}-title">{{ s.title }}</a></td>
<td>{% raw s.abstract %}</a></td>
<td>{% raw s.id %}</td>
<td>{% raw s.owner %}</td>
{% if s.meta_complete %}
<td><span class="glyphicon glyphicon-ok"></span></td>
{% else %}
<td><span class="glyphicon glyphicon-remove"></span></td>
{% end %}
<td>{{ s.num_samples_collected }}</td>
{% if s.num_raw_data %}
<td>{{ s.num_raw_data }}</td>
{% else %}
<td><span class="glyphicon glyphicon-remove"></span></td>
{% end %}
<td>{% raw s.pi %}</td>
<td>{% raw s.pmids %}</td>
</tr>
{% set row = row + 1 %}
{% end %}
</tbody>
</table>
{% else %}
<div id="jumbotron" class="jumbotron">
<h1><span class="glyphicon glyphicon-thumbs-down"></span> There are no studies available.</h1>
<p>
This means that no one has shared any studies with you yet.
</p>
</div>
</div>
</div>
{% end %}
<!--Abstract Modal-->
<div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true" id="study-abstract-modal">
<div class="modal-dialog modal-med">
Expand Down
3 changes: 2 additions & 1 deletion qiita_pet/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
SelectCommandsHandler, AnalysisWaitHandler, AnalysisResultsHandler,
ShowAnalysesHandler, SearchStudiesHandler, ResultsHandler)
from qiita_pet.handlers.study_handlers import (
StudyEditHandler, ListStudiesHandler,
StudyEditHandler, ListStudiesHandler, SearchStudiesAJAX,
StudyDescriptionHandler, MetadataSummaryHandler, EBISubmitHandler,
CreateStudyAJAX, ShareStudyAJAX, StudyApprovalList,
PreprocessingSummaryHandler, VAMPSHandler)
Expand Down Expand Up @@ -74,6 +74,7 @@ def __init__(self):
(r"/study/create/", StudyEditHandler),
(r"/study/edit/(.*)", StudyEditHandler),
(r"/study/list/", ListStudiesHandler),
(r"/study/search/(.*)", SearchStudiesAJAX),
(r"/study/add_files_to_raw_data", AddFilesToRawData),
(r"/study/unlink_all_files", UnlinkAllFiles),
(r"/study/preprocess", PreprocessHandler),
Expand Down