Skip to content
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

Enhance pl #87

Merged
merged 4 commits into from
Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
# microsetta-admin
# microsetta-adminname: microsetta-admin CI

conda create --name test-microsetta-admin python=3.7
conda activate test-microsetta-admin
conda install --yes --file ci/conda_requirements.txt
pip install -r ci/pip_requirements.txt
make install
conda activate test-microsetta-admin

before make test, add:
conda install -c conda-forge nodejs

make test
160 changes: 144 additions & 16 deletions microsetta_admin/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ def _get_projects(include_stats, is_active):
status, projects_output = APIRequest.get(projects_uri)

if status >= 400:
result = {'error_message': "Unable to load project list."}
result = {'error_message': f"Unable to load project list: "
f"{projects_uri}"}
else:
cleaned_projects = [_translate_nones(x, True) for x in
projects_output]
Expand Down Expand Up @@ -328,47 +329,174 @@ def urlify_account_id(id_):

@app.route('/per_sample_summary', methods=['GET', 'POST'])
def per_sample_summary():
# get a list of all projects in the system
_, result = _get_projects(include_stats=False, is_active=True)
projects = result.get('projects')

# filter out any projects that don't belong to Microsetta
projects = [x for x in projects if x['is_microsetta'] is True]

# build a list of dictionaries with just the project id and the project
# name.
projects = [{'project_name': x['project_name'],
'project_id': x['project_id']} for x in projects]

# determine if user wants sample ids stripped
strip_sampleid = request.form.get('strip_sampleid', 'off')
strip_sampleid = strip_sampleid.lower() == 'on'

if request.method == 'GET':
# If user arrived via GET then they are either here w/out
# querying and they simply need the default webpage, or they are
# querying with either a list of barcodes, or with a project id.

# look for both parameters to determine which state we are in.
sample_barcode = request.args.get('sample_barcode')
if sample_barcode is None:
project_id = request.args.get('project_id')

if sample_barcode is None and project_id is None:
# user just wants the default page.
return render_template('per_sample_summary.html',
resource=None,
projects=projects,
**build_login_variables())

if project_id is not None:
# user wants to get summaries on all samples in a project.
payload = {'project_id': project_id}
status, result = APIRequest.post('/api/admin/account_barcode_summa'
'ry?strip_sampleid=False',
json=payload)

if status == 200:
if result['partial_result'] is True:
unprocessed_barcodes = result['unprocessed_barcodes']
else:
unprocessed_barcodes = None

resource = pd.DataFrame(result['samples'])
order = ['sampleid', 'project', 'account-email',
'source-email', 'source-type', 'site-sampled',
'sample-status', 'sample-received', 'ffq-taken',
'ffq-complete', 'vioscreen_username']
order.extend(sorted(set(resource.columns) - set(order)))
resource = resource[order]
if unprocessed_barcodes:
return render_template('per_sample_summary.html',
resource=resource,
projects=projects,
error_message="Too many barcodes. S"
"erver processed only"
" the first 1000.",
**build_login_variables())
else:
return render_template('per_sample_summary.html',
resource=resource,
projects=projects,
**build_login_variables())

else:
return render_template('per_sample_summary.html',
resource=None,
projects=projects,
error_message=result,
**build_login_variables())

# if we are here then the user is querying using barcodes and we
# simply need to set up the query below to perform.
sample_barcodes = [sample_barcode, ]
elif request.method == 'POST':
sample_barcodes, upload_err = upload_util.parse_request_csv_col(
request,
'file',
'sample_name'
)
if upload_err is not None:
else:
# assume POST, since there are only two methods defined in route.
# if we are here, it is because the user is querying using an uploaded
# file containing sample names.
sample_barcodes, err = upload_util.parse_request_csv_col(request,
'file',
'sample_name')
if err is not None:
# there was an error. abort early.
return render_template('per_sample_summary.html',
resource=None,
projects=projects,
**build_login_variables(),
search_error=[{'error': upload_err}])
search_error=[{'error': err}])

# perform the main query.
payload = {'sample_barcodes': sample_barcodes}
status, result = APIRequest.post('/api/admin/account_barcode_summary?'
'strip_sampleid=%s' % str(strip_sampleid),
status, result = APIRequest.post('/api/admin/account_barcode_summary?stri'
'p_sampleid=%s' % str(strip_sampleid),
json=payload)
if status != 200:

if status == 200:
if result['partial_result'] is True:
unprocessed_barcodes = result['unprocessed_barcodes']
else:
unprocessed_barcodes = None
resource = pd.DataFrame(result['samples'])
order = ['sampleid', 'project', 'account-email', 'source-email',
'source-type', 'site-sampled', 'sample-status',
'sample-received', 'ffq-taken', 'ffq-complete',
'vioscreen_username']
order.extend(sorted(set(resource.columns) - set(order)))
resource = resource[order]

if unprocessed_barcodes:
return render_template('per_sample_summary.html',
resource=resource,
projects=projects,
error_message="Too many barcodes. S"
"erver processed only"
" the first 1000.",
**build_login_variables())
else:
return render_template('per_sample_summary.html',
resource=resource,
projects=projects,
**build_login_variables())
else:
return render_template('per_sample_summary.html',
resource=None,
projects=projects,
error_message=result,
**build_login_variables())
else:
resource = pd.DataFrame(result)


def _get_by_sample_barcode(sample_barcodes, strip_sampleid, projects):
payload = {'sample_barcodes': sample_barcodes}
status, result = APIRequest.post('/api/admin/account_barcode_summary?'
'strip_sampleid=%s' % str(strip_sampleid),
json=payload)
if status == 200:
if result['partial_result'] is True:
unprocessed_barcodes = result['unprocessed_barcodes']
else:
unprocessed_barcodes = None

resource = pd.DataFrame(result['samples'])
order = ['sampleid', 'project', 'account-email', 'source-email',
'source-type', 'site-sampled', 'sample-status',
'sample-received', 'ffq-taken', 'ffq-complete',
'vioscreen_username']
order.extend(sorted(set(resource.columns) - set(order)))
resource = resource[order]

if unprocessed_barcodes:
return render_template('per_sample_summary.html',
resource=resource,
projects=projects,
error_message="Too many barcodes. S"
"erver processed only"
" the first 1000.",
**build_login_variables())
else:
return render_template('per_sample_summary.html',
resource=resource,
projects=projects,
**build_login_variables())
else:
return render_template('per_sample_summary.html',
resource=resource,
resource=None,
projects=projects,
error_message=result,
**build_login_variables())


Expand Down
85 changes: 63 additions & 22 deletions microsetta_admin/templates/per_sample_summary.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,80 @@
</script>
{% endblock %}
{% block content %}
<h3>Microsetta Per Sample Summary</h3>
<div style="height: 400px;">
<h3>Sample Summaries</h3>
<hr class="dashed">
<h5>Retrieve summary for a single sample</h5>
<div style="height: 400px; width: 400px">
{% if error_message %}
<p style="color:red">
{{ error_message |e }}
</p>
{% endif %}
<form name="search_form" id="search_form" method="GET">
<table>
<table width=100% border=0>
<tr>
<td colspan=3 style="text-align:left;padding:4px"> <input type="text" name="sample_barcode" placeholder="Enter barcode here" id="sample_barcode" {% if info %} value="{{info.barcode}}"{% endif %}> </td>
<td colspan=1 style="text-align:right;padding:4px"> <input type="submit" value="Retrieve"> </td>
</tr>
</table>
<script> document.search_form.sample_barcode.focus() </script>
</form>
<hr class="dashed">
<h5>Retrieve summaries using a csv file</h5>
<form name="upload_csv" id="search_csv" method="POST" enctype="multipart/form-data" onsubmit="return remove_error_messages()">
<table width=100% border=0>
<tr>
<td><label for="sample_barcode">Barcode: </label></td>
<td><input type="text" name="sample_barcode" id="sample_barcode" {% if info %} value="{{info.barcode}}"{% endif %}></td>
<td></td>
<td><input type="submit" value="Retrieve Summary"></td>
<td colspan=3 style="text-align:left;padding:4px"><input type=file name=file></td>
<td colspan=1 style="text-align:right;padding:4px"><input type=submit value=Upload></td>
</tr>
<tr><td colspan=4></td></tr>
<tr><td colspan=4 style="text-align:center;padding:4px"><i>Barcodes must be listed in column "sample_name"<br>(max: 1000)</i> </td></tr>
<tr><td colspan=4></td></tr>
<tr>
<td colspan=3> <label for="strip_sampleid"> Check to remove sample IDs from summary</label> </td>
<td colspan=1 style="text-align:center;padding:4px"> <input type="checkbox" id="strip_sampleid" name="strip_sampleid"> </td>
</tr>
</table>
<script>
document.search_form.sample_barcode.focus()
</script>
</form>
<br/>
Or upload a file of barcodes listed in the column "sample_name" (max: 1000)
<br/>
<form name="upload_csv" id="search_csv" method="POST" enctype="multipart/form-data" onsubmit="return remove_error_messages()">
<input type="checkbox" id="strip_sampleid" name="strip_sampleid">
<label for="strip_sampleid"> Check to remove sample IDs from summary</label><br/>
<input type=file name=file><br/>
<input type=submit value=Upload>
<hr class="dashed">
<h5>Retrieve status for all samples in a project</h5>
<form name="cc_form" id="cc_form" method="GET">
<table width=100% border=0>
<tr>
<td colspan=3>
<select id="project_id" name="project_id">
{% if projects %}
{% for p in projects %}
<option value='{{p['project_id']}}'>{{p['project_name']}}</option>
{% endfor %}
{% endif %}
</select>
</td>
<td colspan=1 style="text-align:right;padding:4px"><input type="submit" value="Retrieve"></td>
</tr>
</table>
<!--
<table width=100% border=0>
<tr>
<td colspan=3>
<select id="project_ids" name="project_ids" class="chosen-select" size=5>
{% if projects %}
{% for p in projects %}
<option value='{{p['project_id']}}'>{{p['project_name']}}</option>
{% endfor %}
{% endif %}
</select>
</td>
<td colspan=1 style="text-align:right;padding:4px"><input type="submit" value="Retrieve"></td>
</tr>
</table>
<script> document.cc_form.project_ids.focus() </script>
-->
</form>
{% if resource is not none %}
<div class="result_container">
{{resource.to_html(table_id="search_results", classes=["display"], render_links=True, escape=False) |safe}}
</div>
</div>
{% if resource is not none %}
<div class="result_container">
{{resource.to_html(table_id="search_results", classes=["display"], render_links=True, escape=False) |safe}}
</div>
{% endif %}
{{blob}}
Expand Down
2 changes: 1 addition & 1 deletion microsetta_admin/templates/sitebase.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ <h3>Microsetta Utilities</h3>
<li><a href="/create_kits">Create Kit</a></li>
<li><a href="/scan">Scan Barcode</a></li>
<li><a href="/metadata_pulldown">Retrieve Metadata</a></li>
<li><a href="/per_sample_summary">Per sample summary</a></li>
<li><a href="/per_sample_summary">Sample Summaries</a></li>
<li><a href="/submit_daklapack_order">Submit Daklapack Order</a></li>
<li><a href="{{authrocket_url}}/logout?redirect_uri={{endpoint}}/logout">Log Out <br> ({{email |e}})</a></li>
{% else %}
Expand Down
4 changes: 2 additions & 2 deletions microsetta_admin/tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def test_manage_projects_fail(self):
# the api call failed, but the admin page call succeeds--in returning
# a page reporting the error :)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Unable to load project list.', response.data)
self.assertIn(b'Unable to load project list', response.data)

def test_get_submit_daklapack_order_success(self):
# server side issues two GETs to the API
Expand Down Expand Up @@ -477,7 +477,7 @@ def test_get_submit_daklapack_order_fail_projects(self):
response = self.app.get('/submit_daklapack_order',
follow_redirects=True)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Unable to load project list.', response.data)
self.assertIn(b'Unable to load project list', response.data)

def _test_post_submit_daklapack_order(self, addresses_filename=None):
if addresses_filename is None:
Expand Down