Skip to content

Commit

Permalink
improved assay submission form & bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinDo committed Feb 21, 2024
1 parent 27f05ea commit fc489f3
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 105 deletions.
2 changes: 1 addition & 1 deletion src/annotation_service/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def get_default_job_config():
'do_taskforce_domains': True,
'do_coldspots': True,
'do_litvar': True,
'do_auto_class': True
'do_auto_class': False

# outdated
#'do_arup': True,
Expand Down
10 changes: 5 additions & 5 deletions src/common/db_IO.py
Original file line number Diff line number Diff line change
Expand Up @@ -2280,7 +2280,7 @@ def get_variant(self, variant_id,
criterium_evidence = criterium_raw[4]
criterium_strength = criterium_raw[7]
strength_display_name = criterium_raw[8]
is_selected = True
is_selected = criterium_raw[9]
criterium = models.HerediVarCriterium(id = criterium_id, name = criterium_name, type=criterium_type, evidence = criterium_evidence, strength = criterium_strength, strength_display_name = strength_display_name, is_selected = is_selected)
criteria.append(criterium)
scheme = models.Scheme(id = scheme_id, display_name = scheme_display_name, type = scheme_type, criteria = criteria, reference = reference, selected_class = scheme_class, is_active = is_active, is_default = is_default)
Expand Down Expand Up @@ -2624,14 +2624,14 @@ def get_assay_metadata(self, assay_id):
return res

def get_assay_metadata_type(self, assay_metadata_type_id):
#!!!! SELECT MUST BE EQUAL TO the one in get_assay_metadata_types
#!!!! SELECT columns MUST BE EQUAL TO the one in get_assay_metadata_types
command = "SELECT id, title, display_title, assay_type_id, value_type, is_deleted, is_required FROM assay_metadata_type WHERE id= %s AND is_deleted = 0"
self.cursor.execute(command, (assay_metadata_type_id, ))
res = self.cursor.fetchone()
return res

def get_assay_metadata_types(self, assay_type_id, format = "dict"):
#!!!! SELECT MUST BE EQUAL TO the one in get_assay_metadata_type
#!!!! SELECT columns MUST BE EQUAL TO the one in get_assay_metadata_type
command = "SELECT id, title, display_title, assay_type_id, value_type, is_deleted, is_required FROM assay_metadata_type WHERE assay_type_id = %s AND is_deleted = 0"
self.cursor.execute(command, (assay_type_id, ))
assay_metadata_types_raw = self.cursor.fetchall()
Expand All @@ -2656,8 +2656,8 @@ def insert_assay_metadata(self, assay_id, assay_metadata_type_id, value):
self.conn.commit()

def convert_assay_metadata_type(self, assay_metadata_type_raw):
is_deleted = assay_metadata_type_raw[5] == "1"
is_required = assay_metadata_type_raw[6] == "1"
is_deleted = assay_metadata_type_raw[5] == 1
is_required = assay_metadata_type_raw[6] == 1
assay_metadata_type = models.Assay_Metadata_Type(id = int(assay_metadata_type_raw[0]), title = assay_metadata_type_raw[1], display_title = assay_metadata_type_raw[2], assay_type_id = int(assay_metadata_type_raw[3]),
value_type = assay_metadata_type_raw[4], is_deleted = is_deleted, is_required = is_required)
return assay_metadata_type
Expand Down
2 changes: 1 addition & 1 deletion src/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ def to_vcf(self, prefix = True):
new_element = "~2B".join([assay_metadata.metadata_type.display_title, assay_metadata.value])
metadata_parts.append(new_element)
metadata = "~24".join(metadata_parts)
info = '~7C'.join([self.type_title, str(self.date), self.link, metadata]) # sep: |
info = '~7C'.join([self.type_title, str(self.date), str(self.link), metadata]) # sep: |
if prefix:
info = 'assays~1Y' + info
return info
Expand Down
2 changes: 1 addition & 1 deletion src/frontend_celery/webapp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

oauth = OAuth()
sess = Session()
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL)
celery = Celery(__name__, backend="redis", broker=Config.CELERY_BROKER_URL)
mail = Mail()
#heredicare = Heredicare_Flask()

Expand Down
112 changes: 76 additions & 36 deletions src/frontend_celery/webapp/io/variant_io_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,50 +256,90 @@ def get_assertion_criteria(scheme_type, assertion_criteria_source):
@variant_io_blueprint.route('/submit_assay/<int:variant_id>', methods=['GET', 'POST'])
@require_permission(['edit_resources'])
def submit_assay(variant_id):
conn = get_connection()

assay_type2id = conn.get_assay_type_id_dict()
possible_assays = {}
for assay_type in assay_type2id:
assay_type_id = assay_type2id[assay_type]
metadata_types = conn.get_assay_metadata_types(assay_type_id, format = "dict")
possible_assays[assay_type] = {"id": assay_type_id, "metadata_types": metadata_types}

do_redirect = False
if request.method == 'POST':
assay_type = request.form.get('assay_type')
assay_report = request.files.get('report')
assay_score = request.form.get('score')

#print(assay_type)
#print(assay_report)
#print(assay_score)
selected_assay = possible_assays.get(assay_type)


if not assay_type or not assay_report or not assay_score or not assay_report.filename:
flash('All fields are required!', 'alert-danger')
if selected_assay is None:
flash("Assay type is not supported!", "alert-danger")
else:

assay_report.filename = secure_filename(assay_report.filename)

# the buffer is required as larger files (>500kb) are saved to /tmp upon upload
# this file must be read back in
buffer = io.BytesIO()
for line in assay_report:
buffer.write(line)
buffer.seek(0)

b_64_assay_report = functions.buffer_to_base64(buffer)

if len(b_64_assay_report) > 16777215: # limited by the database mediumblob
current_app.logger.error(session['user']['preferred_username'] + " attempted uploading an assay which excedes the maximum filesize. The filesize was: " + str(len(b_64_assay_report)))
abort(500, "The uploaded file is too large. Please upload a smaller file.")

conn = get_connection()
conn.insert_assay(variant_id, assay_type, b_64_assay_report, assay_report.filename, assay_score, functions.get_today())

flash("Successfully uploaded a new assay for variant " + str(variant_id), "alert-success")

current_app.logger.info(session['user']['preferred_username'] + " successfully uploaded a new assay for variant " + str(variant_id))

do_redirect = True


#functions.base64_to_file(b_64_assay_report, '/mnt/users/ahdoebm1/HerediVar/src/frontend/webapp/io/test.pdf')
# extract selected data from request and make sure that only valid data is submitted
metadata_types = selected_assay["metadata_types"]
status = "success"
extracted_data = {}
for metadata_type_name in metadata_types:
metadata_type = metadata_types[metadata_type_name]

current_data = request.form.get(str(metadata_type.id))
if metadata_type.is_required and metadata_type.value_type != 'bool' and (current_data is None or current_data.strip() == ''):
flash("The metadata " + str(metadata_type.display_title) + " is required", "alert-danger")
status = "error"
break
if metadata_type.value_type == "text" and current_data.strip() != '':
extracted_data[metadata_type.id] = str(current_data)
elif metadata_type.value_type == "bool":
current_data = str(current_data)
if current_data not in ["None", "on"]:
flash("The value " + str(current_data) + " is not allowed for a boolean input field", "alert-danger")
status = "error"
break
extracted_data[metadata_type.id] = current_data == "on"
elif metadata_type.value_type == "float" and current_data.strip() != '':
try:
extracted_data[metadata_type.id] = float(current_data)
except:
flash("The metadata " + str(metadata_type.display_title) + " is not numeric.", "alert-danger")
status = "error"
break
elif "ENUM" in metadata_type.value_type:
possible_values = metadata_type.value_type.split(':')[1].split(',')
if current_data not in possible_values:
flash("The value " + str(current_data) + " is not allowed for field " + str(metadata_type.display_title), "alert-danger")
status = "error"
break
extracted_data[metadata_type.id] = current_data

if status == "success":
# extract report
assay_report = request.files.get("report")
if not assay_report or not assay_report.filename:
flash("No assay report provided or filename is missing", "alert-danger")
status = "error"

if status == "success":
assay_report.filename = secure_filename(assay_report.filename)
# the buffer is required as larger files (>500kb) are saved to /tmp upon upload
# this file must be read back in
buffer = io.BytesIO()
for line in assay_report:
buffer.write(line)
buffer.seek(0)

b_64_assay_report = functions.buffer_to_base64(buffer)

if len(b_64_assay_report) > 16777215: # limited by the database mediumblob
current_app.logger.error(session['user']['preferred_username'] + " attempted uploading an assay which excedes the maximum filesize. The filesize was: " + str(len(b_64_assay_report)))
abort(500, "The uploaded file is too large. Please upload a smaller file.")

assay_id = conn.insert_assay(variant_id, assay_type_id, b_64_assay_report, assay_report.filename, link = None, date = functions.get_today(), user_id = session["user"]["user_id"])
for metadata_type_id in extracted_data:
conn.insert_assay_metadata(assay_id, metadata_type_id, extracted_data[metadata_type_id])

flash("Successfully inserted new assay for variant " + str(variant_id), "alert-success")
do_redirect = True


if do_redirect :
return redirect(url_for('variant_io.submit_assay', variant_id = variant_id))
return render_template('variant_io/submit_assay.html')
return render_template('variant_io/submit_assay.html', possible_assays = possible_assays)
27 changes: 20 additions & 7 deletions src/frontend_celery/webapp/static/js/classify.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,6 @@ $(document).ready(function() {
$('#scheme').change(function () {
scheme_select_action();
});
//$('#select_criterium_check').change(function () {
// select_criterium(this);
//});
$('#blank_row_button').click(function () {
create_literature_select(document.getElementById('selectedLiteratureList'));
});
Expand Down Expand Up @@ -1078,8 +1075,8 @@ function create_user_acmg_details_table() {
header_row.appendChild(create_sortable_header('User', 'width_small'))
header_row.appendChild(create_sortable_header('Affiliation', 'width_small'))
header_row.appendChild(create_sortable_header('Strength', 'width_small'))
var evidence_header = create_sortable_header('Evidence')
header_row.appendChild(evidence_header)
header_row.appendChild(create_sortable_header('Evidence'))
header_row.appendChild(create_sortable_header('Selected', 'width_very_small'))
header_row.appendChild(create_sortable_header('Date', 'width_small'))
header_row.appendChild(create_non_sortable_header('Copy', 'width_minimal'))
header.appendChild(header_row)
Expand Down Expand Up @@ -1131,12 +1128,13 @@ function create_subcaption(text) {
}

// this returns one row of the user scheme table shown when making a consensus classification
function create_row_user_acmg_details(user, affiliation, strength, evidence, date) {
function create_row_user_acmg_details(user, criterium_id, affiliation, strength, evidence, date, is_selected) {
var new_row = document.createElement('tr')
new_row.appendChild(create_table_data(user))
new_row.appendChild(create_table_data(affiliation))
new_row.appendChild(create_table_data(criterium_strength_to_description(strength)))
new_row.appendChild(create_table_data(evidence))
new_row.appendChild(create_table_data(is_selected))
new_row.appendChild(create_table_data(date))
//new_row.appendChild(create_table_data('copy...')) // TODO
var copy_evidence_td = document.createElement('td')
Expand All @@ -1152,6 +1150,8 @@ function create_row_user_acmg_details(user, affiliation, strength, evidence, dat
`
copy_evidence_text.value = evidence
copy_evidence_text.setAttribute('strength', strength)
copy_evidence_text.setAttribute('is_selected', is_selected)
copy_evidence_text.setAttribute('criterium_id', criterium_id)
copy_evidence_text.onclick = function() {copy_evidence(this)}
copy_evidence_text.classList.add('clickable')
copy_evidence_td.appendChild(copy_evidence_text)
Expand All @@ -1169,6 +1169,18 @@ function copy_evidence(obj) {
if (radio_to_select != null) {
radio_to_select.click()
}

const is_selected = obj.getAttribute('is_selected')
const criterium_id = obj.getAttribute('criterium_id')

var state = "unchecked"
if (is_selected == "true") {
state = "selected"
} else if (is_selected == "false") {
state = "unselected"
}
select_criterium_check.value = state
set_criterium(criterium_id, state, is_intermediate = true)
}

function add_user_acmg_classification_details(criterium_id) {
Expand All @@ -1192,8 +1204,9 @@ function add_user_acmg_classification_details(criterium_id) {
var current_criterium_id = criterium['name']
var current_strength = criterium['type']
var current_evidence = criterium['evidence']
var is_selected = criterium['is_selected']
if (current_criterium_id === criterium_id) {
var new_row = create_row_user_acmg_details(user, affiliation, current_strength, current_evidence, current_date)
var new_row = create_row_user_acmg_details(user, criterium_id, affiliation, current_strength, current_evidence, current_date, is_selected)
document.getElementById('user_acmg_details').appendChild(new_row)
}
}
Expand Down
15 changes: 5 additions & 10 deletions src/frontend_celery/webapp/static/js/submit_assay.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ $(document).ready(function(){
$('#assay_type').click(function() {
select_assay_type_action(this.value);
});

});


Expand All @@ -19,18 +20,12 @@ function on_page_load() {




function select_assay_type_action(assay_type) {
var score_label_obj = document.getElementById('score_label')
$('form').each(function(i, obj) {
obj.hidden=true;
})

if (assay_type !== '') {
document.getElementById('report_form_group').hidden=false
document.getElementById('score_form_group').hidden=false
}

if (assay_type === 'functional') {
score_label_obj.innerText = "Functional assay score"
} else if (assay_type === 'splicing') {
score_label_obj.innerText = "Splicing assay score"
document.getElementById(assay_type + '_assay').hidden=false
}
}
7 changes: 3 additions & 4 deletions src/frontend_celery/webapp/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ <h4>Changelog</h4>
<li>Added gnomAD non-cancer scores</li>
<li>Added HerediCaRe center specific classifications</li>
<li>Added CSpec BRCA1/2 assays to annotation algorithm</li>
<li>Copy evidence of user classifications to a consensus classification now also automatically selects the state correctly</li>
</ul>
Bugfixes:
<ul>
Expand All @@ -116,10 +117,8 @@ <h4>Changelog</h4>
<li>Fixed a bug when updating ClinVar submissions</li>
<li>When selecting any BP criterium the enigma BRCA1/2 schemes would instantly switch to class 2. This is now fixed and only BP1_str triggers class 2</li>
<li>Previously the class by scheme was not updated correctly when the user updated their classification</li>
</ul>
Known issues:
<ul>
<li>HerediCare priors, log likelihood, segregation & cooccurance are not available</li>
<li>Fixed a bug in the criteria column of the automatic classification results that would shrink to width zero when the screen is resized</li>
<li>Fixed a bug where unselected criteria where shown as selected for consensus classifications</li>
</ul>
</div>
<a class="btn btn-outline-primary" href="{{url_for('main.changelog')}}">previous changelog</a>
Expand Down
Loading

0 comments on commit fc489f3

Please sign in to comment.