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

Add Barcodes to Existing Kits #110

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
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
132 changes: 118 additions & 14 deletions microsetta_admin/server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import csv
import tempfile
import jwt
from flask import render_template, Flask, request, session, send_file, url_for
import secrets
Expand Down Expand Up @@ -539,35 +540,64 @@ def new_kits():
**build_login_variables())

elif request.method == 'POST':
num_kits = int(request.form['num_kits'])
num_samples = int(request.form['num_samples'])
num_kits = int(request.form['number_of_kits'])
num_samples = int(request.form['number_of_samples'])
prefix = request.form['prefix']
selected_project_ids = request.form.getlist('project_ids')
payload = {'number_of_kits': num_kits,
'number_of_samples': num_samples,
'project_ids': selected_project_ids}

barcodes_container = []

# Determine if each sample slot was provided or is to be generated.
# We default to generating novel barcodes.
for i in range(1, num_samples+1):
barcode_file = request.files.get(f'upload_csv_{i}')
if barcode_file:
# Barcodes provided for this slot
barcodes = _read_csv_file(barcode_file)
else:
# Generate barcodes for this slot
barcodes = []

# Add this slot's barcodes (or empty list) to the container
barcodes_container.append(barcodes)

payload = {
'number_of_kits': num_kits,
'number_of_samples': num_samples,
'project_ids': selected_project_ids,
'barcodes': barcodes_container
}

if prefix:
payload['kit_id_prefix'] = prefix

status, result = APIRequest.post(
'/api/admin/create/kits',
json=payload)
status, result = APIRequest.post('/api/admin/create/kits',
json=payload)

if status != 201:
start_index = result.find("Key")
if start_index != -1:
error_message = result[start_index:]
error_message = error_message[:44]
else:
error_message = result

return render_template('create_kits.html',
error_message='Failed to create kits',
error_message=error_message,
projects=projects,
**build_login_variables())

# StringIO/BytesIO based off https://stackoverflow.com/a/45111660
buf = io.StringIO()
payload = io.BytesIO()

# explicitly expand out the barcode detail
kits = pd.DataFrame(result['created'])
for i in range(num_samples):
kits['barcode_%d' % (i+1)] = [r['sample_barcodes'][i]
for _, r in kits.iterrows()]

for kit_index, row in kits.iterrows():
sample_barcodes = row['sample_barcodes']
for sample_index in range(len(sample_barcodes)):
kits.at[kit_index, f'barcode_{sample_index + 1}'] = \
sample_barcodes[sample_index]

kits.drop(columns='sample_barcodes', inplace=True)

kits.to_csv(buf, sep=',', index=False, header=True)
Expand All @@ -583,6 +613,80 @@ def new_kits():
mimetype='text/csv')


def _read_csv_file(file):
content = file.read().decode('utf-8-sig')
return [row[0] for row in csv.reader(io.StringIO(content),
skipinitialspace=True)
if row]


@app.route('/add_barcode_to_kit', methods=['GET', 'POST'])
def new_barcode_kit():
if request.method == 'GET':
return render_template('add_barcode_to_kit.html',
**build_login_variables())

elif request.method == 'POST':
if 'add_single_barcode' in request.form:
kit_ids = [request.form['kit_id']]
if 'user_barcode' in request.form:
# User provided a barcode
barcodes = [request.form['user_barcode']]
generate_barcodes = False
else:
# Generate barcode
barcodes = []
generate_barcodes = True

elif 'add_multiple_barcodes' in request.form:
kit_ids_file = request.files['kit_ids']
kit_ids = _read_csv_file(kit_ids_file)
if 'barcodes_file' in request.files:
# User provided barcodes
barcodes = _read_csv_file(request.files['barcodes_file'])
generate_barcodes = False
else:
# Generate barcodes
barcodes = []
generate_barcodes = True

payload = {
'kit_ids': kit_ids,
'barcodes': barcodes,
'generate_barcodes': generate_barcodes
}

status, result = APIRequest.post(
'/api/admin/add_barcodes_to_kits',
json=payload
)

if status != 201:
return render_template(
'add_barcode_to_kit.html',
error_message=result,
**build_login_variables()
)

with tempfile.NamedTemporaryFile(
mode='w',
delete=False,
newline=''
) as file:
writer = csv.writer(file)
writer.writerow(['Kit ID', 'Barcode'])
writer.writerows(result)
filename = file.name

timestamp = datetime.now().strftime('%d%b%Y-%H%M')
fname = f'kit_ids-barcodes-{timestamp}.csv'
return send_file(
filename,
as_attachment=True,
download_name=fname
)


def _check_sample_status(extended_barcode_info):
warning = None
in_microsetta_project = any(
Expand Down
221 changes: 221 additions & 0 deletions microsetta_admin/templates/add_barcode_to_kit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
{% extends "sitebase.html" %}
{% block head %}
<script src="/static/vendor/js/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.3.0/jquery.form.min.js"></script>
<script>
$(document).ready(function(){
// Initialize form validation
$("form[name='single_kit_form']").validate({
rules: {
kit_id: "required",
user_barcode: {
required: function(element) {
return !$("#generate_barcode_single").is(':checked');
}
}
},
submitHandler: function (form) {
form.submit();
}
});

$("form[name='multiple_kits_form']").validate({
rules: {
kit_ids: "required",
barcodes_file: {
required: function(element) {
return !$("#generate_barcodes_multiple").is(':checked');
}
}
},
submitHandler: function (form) {
form.submit();
}
});

function clearMessages() {
$('#error_message').text("");
$('#success_message').empty();
}

// Toggle form visibility
$('#single_kit_button').click(function() {
$('#single_kit_form').show();
$('#multiple_kits_form').hide();
clearMessages();
});
$('#multiple_kits_button').click(function() {
$('#multiple_kits_form').show();
$('#single_kit_form').hide();
clearMessages();
});

$("#generate_barcode_single").on("change", function() {
if ($(this).is(":checked")) {
$("#user_barcode").prop("disabled", true);
} else {
$("#user_barcode").prop("disabled", false);
}
});

$("#user_barcode").on("input", function() {
if ($(this).val().length > 0) {
$("#generate_barcode_single").prop("disabled", true);
} else {
$("#generate_barcode_single").prop("disabled", false);
}
});

$("#generate_barcodes_multiple").on("change", function() {
if ($(this).is(":checked")) {
$("#barcodes_file").prop("disabled", true);
} else {
$("#barcodes_file").prop("disabled", false);
}
});

$("#barcodes_file").on("change", function() {
if ($(this).val()) {
$("#generate_barcodes_multiple").prop("disabled", true);
} else {
$("#generate_barcodes_multiple").prop("disabled", false);
}
});

$("input[type='submit']").click(function() {
$("input[type='submit']", $(this).parents("form")).removeAttr("clicked");
$(this).attr("clicked", "true");
});
});
</script>
{% endblock %}

{% block content %}
<h3>Add Barcode(s) to Kit(s)</h3>
<div style="height: 335px;">
{% if error_message %}
<p id="error_message" style="color:red">
{{ error_message |e }}
</p>
{% endif %}

<button id="single_kit_button">Add Barcode to Single Kit</button>
<button id="multiple_kits_button">Add Barcodes to Multiple Kits</button>

<form name="single_kit_form" id="single_kit_form" method="POST" style="display:none;">
<table>
<tr>
<td colspan="2">
<br />
</td>
</tr>
<tr>
<td>
<label for="kit_id" style="margin: auto;">Kit ID: </label>
</td>
<td>
<input type="text" name="kit_id" id="kit_id" required>
</td>
</tr>
<tr>
<td colspan="2">
<hr style="width: 100%" />
</td>
</tr>
<tr>
<td>
<label for="user_barcode" style="margin: auto;">Enter Barcode: </label>
</td>
<td>
<input type="text" name="user_barcode" id="user_barcode">
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center; padding-top: 10px; padding-bottom: 10px;">
-- OR --
</td>
</tr>
<tr>
<td>
<label for="generate_barcode_single" style="margin: auto;">Generate Barcode: </label>
</td>
<td>
<input type="checkbox" name="generate_barcode_single" id="generate_barcode_single">
</td>
</tr>
<tr>
<td colspan="2">
<hr style="width: 100%" />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center;">
<input type="submit" name="add_single_barcode" value="Add Barcode">
</td>
</tr>
</table>
</form>
{% if barcodes %}
<ul id="success_message">
{% for barcode in barcodes %}
<span>You added {{ barcode }} to Kit ID {{ kit_id }}</span><br>
{% endfor %}
</ul>
{% endif %}
<form name="multiple_kits_form" id="multiple_kits_form" method="POST" enctype="multipart/form-data" style="display:none;">
<table>
<tr>
<td colspan="2">
<br />
Upload comma-separated value file(s) (CSV)
<br /><br />
</td>
</tr>
<tr>
<td>
<label for="kit_ids" style="margin: auto;">Kit IDs: </label>
</td>
<td>
<input type="file" name="kit_ids" id="kit_ids" required>
</td>
</tr>
<tr>
<td colspan="2">
<hr style="width: 100%" />
</td>
</tr>
<tr>
<td>
<label for="barcodes_file" style="margin: auto;">Barcodes: </label>
</td>
<td>
<input type="file" name="barcodes_file" id="barcodes_file">
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center; padding-top: 10px; padding-bottom: 10px;">
-- OR --
</td>
</tr>
<tr>
<td>
<label for="generate_barcodes_multiple" style="margin: auto;">Generate Barcodes: </label>
</td>
<td>
<input type="checkbox" name="generate_barcodes_multiple" id="generate_barcodes_multiple">
</td>
</tr>
<tr>
<td colspan="2">
<hr style="width: 100%" />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center;">
<input type="submit" name="add_multiple_barcodes" value="Add Barcodes">
</td>
</tr>
</table>
</form>
</div>
{% endblock %}
Loading
Loading