diff --git a/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java b/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java index 948521bce..e96fae1df 100644 --- a/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java +++ b/api-src/org/labkey/api/targetedms/model/QCMetricConfiguration.java @@ -39,6 +39,7 @@ public enum TimeValueOption private String _yAxisLabel; private Double _upperBound; private Double _lowerBound; + private String _annotationName; public int getId() { @@ -185,6 +186,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); @@ -216,6 +227,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/protein/Sequences.query.xml b/resources/queries/protein/Sequences.query.xml new file mode 100644 index 000000000..92a99ef3a --- /dev/null +++ b/resources/queries/protein/Sequences.query.xml @@ -0,0 +1,13 @@ + + + + + + + 200 + + +
+
+
+
diff --git a/resources/queries/targetedms/Protein/.qview.xml b/resources/queries/targetedms/Protein/.qview.xml new file mode 100644 index 000000000..5659c1ee6 --- /dev/null +++ b/resources/queries/targetedms/Protein/.qview.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + 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.006-26.007.sql b/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql new file mode 100644 index 000000000..3ee9b11ed --- /dev/null +++ b/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql @@ -0,0 +1,34 @@ +ALTER TABLE targetedms.Runs ADD COLUMN ProteinCount INT; + +UPDATE targetedms.Runs r +SET ProteinCount = ( + SELECT COUNT(*) + FROM targetedms.Protein p + JOIN targetedms.PeptideGroup pg ON p.PeptideGroupId = pg.Id + WHERE pg.RunId = r.Id +); + +-- Redefine PeptideGroupCount: groups containing at least one peptide +UPDATE targetedms.Runs r +SET PeptideGroupCount = ( + SELECT COUNT(DISTINCT pg.Id) + FROM targetedms.PeptideGroup pg + JOIN targetedms.GeneralMolecule gm ON gm.PeptideGroupId = pg.Id + JOIN targetedms.Peptide p ON p.Id = gm.Id + WHERE pg.RunId = r.Id +); + +ALTER TABLE targetedms.Runs ADD COLUMN MoleculeGroupCount INT; + +UPDATE targetedms.Runs r +SET MoleculeGroupCount = ( + SELECT COUNT(DISTINCT pg.Id) + FROM targetedms.PeptideGroup pg + JOIN targetedms.GeneralMolecule gm ON gm.PeptideGroupId = pg.Id + JOIN targetedms.Molecule m ON m.Id = gm.Id + WHERE pg.RunId = r.Id +); + +ALTER TABLE targetedms.Runs ALTER COLUMN MoleculeGroupCount SET NOT NULL; +ALTER TABLE targetedms.Runs ALTER COLUMN PeptideGroupCount SET NOT NULL; +ALTER TABLE targetedms.Runs ALTER COLUMN ProteinCount SET NOT NULL; 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..17c40a29a --- /dev/null +++ b/resources/schemas/dbscripts/postgresql/targetedms-26.007-26.008.sql @@ -0,0 +1 @@ +ALTER TABLE targetedms.QCMetricConfiguration ADD COLUMN AnnotationName VARCHAR(255); diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml index 8fddaf6bf..1ba2d107b 100644 --- a/resources/schemas/targetedms.xml +++ b/resources/schemas/targetedms.xml @@ -179,15 +179,23 @@ #,### - /targetedms-showPrecursorList.view?id=${Id} + /targetedms-showPeptideGroupList.view?id=${Id} + + + #,### + /targetedms-showMoleculeGroupList.view?id=${Id} + + + #,### + /targetedms-showProteinList.view?id=${Id} #,### - /targetedms-showPrecursorList.view?id=${Id} + /targetedms-showPeptideList.view?id=${Id} #,### - /targetedms-showPrecursorList.view?id=${Id} + /targetedms-showMoleculeList.view?id=${Id} #,### @@ -1358,6 +1366,7 @@ + diff --git a/resources/views/configureQCMetric.html b/resources/views/configureQCMetric.html index 9a608a2c5..0d65d3017 100644 --- a/resources/views/configureQCMetric.html +++ b/resources/views/configureQCMetric.html @@ -12,6 +12,10 @@ LABKEY.internal = {}; LABKEY.internal.ConfigureQCMetrics = new function () { + const METRIC_TYPE_CUSTOM = 'custom'; + const METRIC_TYPE_TRACE = 'trace'; + const METRIC_TYPE_ANNOTATION = 'annotation'; + let qcMetrics; let qcMetricsTable =''; let configRows = []; @@ -36,7 +40,7 @@ qcMetricsTable += ''; qcMetricsTable += '' + (editLock ? '' : '' ); - qcMetricsTable += ''; + qcMetricsTable += ''; if (row.EffectiveStatus === 'NoData') { qcMetricsTable += ''; @@ -59,11 +63,14 @@ }); qcMetricsTable += '
' + (editLock ? '' : '' ) + LABKEY.Utils.encodeHtml(row.name) + '' + (row.PrecursorScoped ? 'Precursor' : 'Run') + '' + (row.PrecursorScoped ? 'Precursor' : 'Replicate') + 'No data in this folder

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

'; jQuery('#qcMetricsTable').html(qcMetricsTable); @@ -77,10 +84,13 @@ LABKEY.internal.ConfigureQCMetrics.resetQCMetrics(); }); jQuery('#createNewCustomMetricButton').click(function() { - LABKEY.internal.ConfigureQCMetrics.addNewMetric('custom'); + LABKEY.internal.ConfigureQCMetrics.addNewMetric(METRIC_TYPE_CUSTOM); }); jQuery('#createNewTraceMetricButton').click(function() { - LABKEY.internal.ConfigureQCMetrics.addNewMetric('trace') + LABKEY.internal.ConfigureQCMetrics.addNewMetric(METRIC_TYPE_TRACE); + }); + jQuery('#createNewAnnotationMetricButton').click(function() { + LABKEY.internal.ConfigureQCMetrics.addNewMetric(METRIC_TYPE_ANNOTATION); }); jQuery('#clearCacheButton').click(function() { jQuery('#qcMetricsError').text('Clearing cached metrics...'); @@ -169,7 +179,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); @@ -181,22 +194,11 @@ }, showCustomMetricWindow: function (op, clickedMetric) { - LABKEY.Query.getSchemas({ - scope: this, - containerPath: LABKEY.container.id, - success: function(schemasInfo) { - const windowConfig = { - parent: this, - schemas: schemasInfo.schemas, - operation: op - }; - - if (clickedMetric) { - windowConfig.metric = clickedMetric; - } - Ext4.create('Panorama.Window.AddCustomMetricWindow', windowConfig).show(); - } - }); + const windowConfig = { operation: op }; + if (clickedMetric) { + windowConfig.metric = clickedMetric; + } + Panorama.Window.AddCustomMetricWindow.show(windowConfig); }, showTraceMetricWindow: function (op, clickedMetric) { @@ -242,13 +244,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') { + if (metricType === METRIC_TYPE_CUSTOM) { this.showCustomMetricWindow(op); } - else if (metricType === 'trace') { - this.showTraceMetricWindow(op) + else if (metricType === METRIC_TYPE_TRACE) { + this.showTraceMetricWindow(op); + } + else if (metricType === METRIC_TYPE_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..2d036864b --- /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($('