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 @@
+
+
+
+
+
+
+
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 ? '' : '' ) + LABKEY.Utils.encodeHtml(row.name) + ' | ' + (editLock ? '' : '' );
- qcMetricsTable += '' + (row.PrecursorScoped ? 'Precursor' : 'Run') + ' | ';
+ qcMetricsTable += '' + (row.PrecursorScoped ? 'Precursor' : 'Replicate') + ' | ';
if (row.EffectiveStatus === 'NoData') {
qcMetricsTable += 'No data in this folder | ';
@@ -59,11 +63,14 @@
});
qcMetricsTable += '
' +
- '' +
- '' +
- '' +
- '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
'' +
+ '
' +
'
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($('