diff --git a/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java b/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java index 73d10893e..3a9000ac8 100644 --- a/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java +++ b/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java @@ -33,6 +33,7 @@ public class QCMetricConfiguration implements Comparable private String _yAxisLabel; private Double _upperBound; private Double _lowerBound; + private String _annotationName; public int getId() { @@ -164,6 +165,16 @@ public void setLowerBound(Double lowerBound) _lowerBound = lowerBound; } + public String getAnnotationName() + { + return _annotationName; + } + + public void setAnnotationName(String annotationName) + { + _annotationName = annotationName; + } + public JSONObject toJSON(){ JSONObject jsonObject = new JSONObject(); jsonObject.put("id", _id); @@ -195,6 +206,9 @@ public JSONObject toJSON(){ if (_upperBound != null) { jsonObject.put("upperBound", _upperBound); } + if (_annotationName != null) { + jsonObject.put("annotationName", _annotationName); + } return jsonObject; } diff --git a/resources/queries/targetedms/qcMetricsConfig.sql b/resources/queries/targetedms/qcMetricsConfig.sql index d1f1248ef..bb5d5082d 100644 --- a/resources/queries/targetedms/qcMetricsConfig.sql +++ b/resources/queries/targetedms/qcMetricsConfig.sql @@ -30,7 +30,8 @@ SELECT qmc.MaxTimeValue, qmc.TimeValueOption, qmc.TraceName, - qmc.YAxisLabel + qmc.YAxisLabel, + qmc.AnnotationName FROM qcmetricconfiguration qmc FULL JOIN qcenabledmetrics qem diff --git a/resources/schemas/dbscripts/postgresql/targetedms-26.007-26.008.sql b/resources/schemas/dbscripts/postgresql/targetedms-26.007-26.008.sql new file mode 100644 index 000000000..c00cd2a66 --- /dev/null +++ b/resources/schemas/dbscripts/postgresql/targetedms-26.007-26.008.sql @@ -0,0 +1 @@ +ALTER TABLE targetedms.QCMetricConfiguration ADD COLUMN AnnotationName VARCHAR(200); diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml index 8fddaf6bf..0676c5526 100644 --- a/resources/schemas/targetedms.xml +++ b/resources/schemas/targetedms.xml @@ -1358,6 +1358,7 @@ + diff --git a/resources/views/configureQCMetric.html b/resources/views/configureQCMetric.html index 9a608a2c5..d8dbc8b0c 100644 --- a/resources/views/configureQCMetric.html +++ b/resources/views/configureQCMetric.html @@ -59,11 +59,14 @@ }); qcMetricsTable += '

' + - '' + - '' + - '' + - '' + + '
' + + '' + + '' + + '' + + '' + + '' + '' + + '
' + '
Edits to queries backing existing custom metrics require a manual cache clearing to display the updated results.

'; jQuery('#qcMetricsTable').html(qcMetricsTable); @@ -80,7 +83,10 @@ LABKEY.internal.ConfigureQCMetrics.addNewMetric('custom'); }); jQuery('#createNewTraceMetricButton').click(function() { - LABKEY.internal.ConfigureQCMetrics.addNewMetric('trace') + LABKEY.internal.ConfigureQCMetrics.addNewMetric('trace'); + }); + jQuery('#createNewAnnotationMetricButton').click(function() { + LABKEY.internal.ConfigureQCMetrics.addNewMetric('annotation'); }); jQuery('#clearCacheButton').click(function() { jQuery('#qcMetricsError').text('Clearing cached metrics...'); @@ -169,7 +175,10 @@ const op = 'update'; if (clickedQcMetricConfig.TraceName) { - LABKEY.internal.ConfigureQCMetrics.showTraceMetricWindow(op, clickedQcMetricConfig) + LABKEY.internal.ConfigureQCMetrics.showTraceMetricWindow(op, clickedQcMetricConfig); + } + else if (clickedQcMetricConfig.AnnotationName) { + LABKEY.internal.ConfigureQCMetrics.showAnnotationMetricWindow(op, clickedQcMetricConfig); } else { LABKEY.internal.ConfigureQCMetrics.showCustomMetricWindow(op, clickedQcMetricConfig); @@ -242,13 +251,27 @@ }); }, + showAnnotationMetricWindow: function (op, clickedMetric) { + const windowConfig = { + parent: this, + operation: op + }; + if (clickedMetric) { + windowConfig.metric = clickedMetric; + } + Panorama.Window.AddAnnotationMetricWindow.show(windowConfig); + }, + addNewMetric: function (metricType) { const op = 'insert'; if (metricType === 'custom') { this.showCustomMetricWindow(op); } else if (metricType === 'trace') { - this.showTraceMetricWindow(op) + this.showTraceMetricWindow(op); + } + else if (metricType === 'annotation') { + this.showAnnotationMetricWindow(op); } }, diff --git a/resources/views/configureQCMetric.view.xml b/resources/views/configureQCMetric.view.xml index d10619e34..69c26d2dd 100644 --- a/resources/views/configureQCMetric.view.xml +++ b/resources/views/configureQCMetric.view.xml @@ -7,6 +7,7 @@ + \ No newline at end of file diff --git a/resources/web/PanoramaPremium/window/AddNewAnnotationMetricWindow.js b/resources/web/PanoramaPremium/window/AddNewAnnotationMetricWindow.js new file mode 100644 index 000000000..fc762eed0 --- /dev/null +++ b/resources/web/PanoramaPremium/window/AddNewAnnotationMetricWindow.js @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2025 LabKey Corporation. All rights reserved. No portion of this work may be reproduced in + * any form or by any electronic or mechanical means without written permission from LabKey Corporation. + */ + +(function($) { + window.Panorama = window.Panorama || {}; + window.Panorama.Window = window.Panorama.Window || {}; + + const DIALOG_ID = 'lk-annotation-metric-dialog'; + let _config = null; + let _allAnnotations = []; + + function closeDialog() { + $('#' + DIALOG_ID).remove(); + } + + function showError(msg) { + $('#lk-annotation-metric-error').text(msg).show(); + } + + function clearErrors() { + $('#lk-annotation-metric-error').hide().text(''); + $('#lk-annotation-metric-name, #lk-annotation-metric-ylabel, #lk-annotation-name-select') + .css('border-color', ''); + } + + function markInvalid($field) { + $field.css('border-color', 'red'); + } + + function validate() { + clearErrors(); + let isValid = true; + + if (!$('#lk-annotation-metric-name').val().trim()) { + markInvalid($('#lk-annotation-metric-name')); + isValid = false; + } + if (!$('#lk-annotation-metric-ylabel').val().trim()) { + markInvalid($('#lk-annotation-metric-ylabel')); + isValid = false; + } + if (!$('#lk-annotation-name-select').val()) { + markInvalid($('#lk-annotation-name-select')); + isValid = false; + } + + if (!isValid) { + showError('Please fill in all required fields.'); + } + return isValid; + } + + function getAnnotationTarget() { + return $('input[name="annotationType"]:checked').val() === 'precursor' + ? 'precursor_result' + : 'replicate'; + } + + function getFilteredAnnotations() { + const target = getAnnotationTarget(); + const seen = {}; + const result = []; + _allAnnotations.forEach(function(row) { + const targets = (row['Targets'] || '').split(',').map(function(s) { return s.trim(); }); + if (targets.indexOf(target) >= 0 && !seen[row['Name']]) { + seen[row['Name']] = true; + result.push(row['Name']); + } + }); + result.sort(); + return result; + } + + function refreshAnnotationsSelect() { + const $select = $('#lk-annotation-name-select'); + const currentVal = $select.val(); + $select.empty().append($('