-
Notifications
You must be signed in to change notification settings - Fork 4
Easy metrics based on replicate and precursor annotation values #1210
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
Merged
Merged
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
0f8edc7
Easy metrics based on replicate and precursor annotation values
ankurjuneja 68ac525
add missing sql script
ankurjuneja ebfb488
fix name
ankurjuneja 1ccb5fd
transform away from extjs
ankurjuneja bfccdfc
fix configureQCMetric alignment and other namespace warnings
ankurjuneja e5aea27
test anno backed metric
ankurjuneja 664597c
fix test
ankurjuneja 6effe18
update metric name
ankurjuneja 37aff17
fix test and update sql script version
ankurjuneja f3d58e4
merge branch 'release26.3-SNAPSHOT' into 26.3_fb_Item1046'
ankurjuneja 3b75a24
Update resources/schemas/dbscripts/postgresql/targetedms-26.007-26.00…
ankurjuneja 1840340
Update resources/web/PanoramaPremium/window/AddNewAnnotationMetricWin…
ankurjuneja 9ba3d5d
make query metric dialog consistent with annotation backed metric dialog
ankurjuneja 2213508
code review comment
ankurjuneja b1839dc
adjust test for modified Add new custom metric dialog
ankurjuneja 177538b
add constants for metric types
ankurjuneja f51196a
fix test
ankurjuneja 541ba1d
Update AddNewAnnotationMetricWindow.js
ankurjuneja File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
resources/schemas/dbscripts/postgresql/targetedms-26.007-26.008.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ALTER TABLE targetedms.QCMetricConfiguration ADD COLUMN AnnotationName VARCHAR(200); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,11 +59,14 @@ | |
| }); | ||
|
|
||
| qcMetricsTable += '</table><br>' + | ||
| '<button type="button" class="labkey-button primary" id="saveButton" style="margin-right: 20px;">Save</button>' + | ||
| '<button type="button" class="labkey-button" id="cancelButton" style="margin-right: 20px;">Cancel</button>' + | ||
| '<button type="button" class="labkey-button" id="createNewCustomMetricButton" style="margin-right: 20px;">Add New Custom Metric</button>' + | ||
| '<button type="button" class="labkey-button" id="createNewTraceMetricButton" style="margin-right: 20px;">Add New Trace Metric</button>' + | ||
| '<div style="display:flex;flex-wrap:wrap;gap:8px;">' + | ||
| '<button type="button" class="labkey-button primary" id="saveButton">Save</button>' + | ||
| '<button type="button" class="labkey-button" id="cancelButton">Cancel</button>' + | ||
| '<button type="button" class="labkey-button" id="createNewCustomMetricButton">Add New Custom Metric</button>' + | ||
| '<button type="button" class="labkey-button" id="createNewTraceMetricButton">Add New Trace Metric</button>' + | ||
| '<button type="button" class="labkey-button" id="createNewAnnotationMetricButton">Add Annotation-Backed Metric</button>' + | ||
| '<button type="button" class="labkey-button" id="clearCacheButton">Clear Cached Metric Values</button>' + | ||
| '</div>' + | ||
| '</form><br>Edits to queries backing existing custom metrics require a manual cache clearing to display the updated results.</p>'; | ||
|
|
||
| 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'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use a constant here and other places, including for |
||
| }); | ||
| 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); | ||
| } | ||
| }, | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
226 changes: 226 additions & 0 deletions
226
resources/web/PanoramaPremium/window/AddNewAnnotationMetricWindow.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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'; | ||
|
labkey-jeckels marked this conversation as resolved.
|
||
| } | ||
|
|
||
| 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($('<option>').val('').text('-- Select annotation --')); | ||
| getFilteredAnnotations().forEach(function(name) { | ||
| $select.append($('<option>').val(name).text(name)); | ||
| }); | ||
|
|
||
| const metric = _config && _config.metric; | ||
| if (_config.operation === 'update' && metric && metric.AnnotationName) { | ||
| $select.val(metric.AnnotationName); | ||
| } else if (currentVal) { | ||
| $select.val(currentVal); | ||
| } | ||
| } | ||
|
|
||
| function checkMetricNameExists(metricName, callback) { | ||
| const filterArray = [LABKEY.Filter.create('Name', metricName, LABKEY.Filter.Types.EQUAL)]; | ||
| if (_config.operation === 'update' && _config.metric) { | ||
| filterArray.push(LABKEY.Filter.create('id', _config.metric.id, LABKEY.Filter.Types.NOT_EQUAL)); | ||
| } | ||
| LABKEY.Query.selectRows({ | ||
| containerPath: LABKEY.container.id, | ||
|
ankurjuneja marked this conversation as resolved.
Outdated
|
||
| schemaName: 'targetedms', | ||
| queryName: 'qcmetricconfiguration', | ||
| filterArray: filterArray, | ||
| success: function(data) { callback(data.rows.length > 0); }, | ||
| failure: function() { callback(false); } | ||
| }); | ||
| } | ||
|
|
||
| function save() { | ||
| if (!validate()) return; | ||
|
|
||
| const metricName = $('#lk-annotation-metric-name').val().trim(); | ||
| checkMetricNameExists(metricName, function(exists) { | ||
| if (exists) { | ||
| showError('A metric with the name "' + LABKEY.Utils.encodeHtml(metricName) + '" already exists. Please choose a different name.'); | ||
| markInvalid($('#lk-annotation-metric-name')); | ||
| return; | ||
| } | ||
|
|
||
| const newMetric = { | ||
| Name: metricName, | ||
| QueryName: 'QCAnnotationMetric', | ||
| YAxisLabel: $('#lk-annotation-metric-ylabel').val().trim(), | ||
| PrecursorScoped: $('input[name="annotationType"]:checked').val() === 'precursor', | ||
| AnnotationName: $('#lk-annotation-name-select').val() | ||
| }; | ||
| if (_config.operation === 'update') { | ||
| newMetric.id = _config.metric.id; | ||
| } | ||
|
|
||
| LABKEY.Query.saveRows({ | ||
| containerPath: LABKEY.container.id, | ||
| commands: [{ schemaName: 'targetedms', queryName: 'qcmetricconfiguration', command: _config.operation, rows: [newMetric] }], | ||
| method: 'POST', | ||
| success: function() { window.location.reload(); }, | ||
| failure: function(response) { | ||
| showError((response && (response.exception || response.message)) || 'Error saving metric'); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| function deleteMetric() { | ||
| if (!confirm('This will delete the "' + _config.metric.name + '" metric. Are you sure?')) return; | ||
|
|
||
| LABKEY.Query.saveRows({ | ||
| containerPath: LABKEY.container.id, | ||
|
ankurjuneja marked this conversation as resolved.
Outdated
|
||
| commands: [ | ||
| { schemaName: 'targetedms', queryName: 'qcenabledmetrics', command: 'delete', rows: [{ metric: _config.metric.id }] }, | ||
| { schemaName: 'targetedms', queryName: 'qcmetricconfiguration', command: 'delete', rows: [{ id: _config.metric.id }] } | ||
| ], | ||
| method: 'POST', | ||
| success: function() { window.location.reload(); }, | ||
| failure: function(response) { | ||
| showError((response && (response.exception || response.message)) || 'Error deleting metric'); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| function buildDialogHtml() { | ||
| const op = _config.operation; | ||
| const metric = _config.metric || {}; | ||
| const isPrecursor = op === 'update' && metric.PrecursorScoped; | ||
| const title = op === 'insert' ? 'Add Annotation-Backed Metric' : 'Edit Annotation-Backed Metric'; | ||
|
|
||
| return '<div id="' + DIALOG_ID + '" style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9999;display:flex;align-items:center;justify-content:center;">' | ||
|
labkey-jeckels marked this conversation as resolved.
|
||
| + '<div class="x4-window x4-window-default" style="min-width:480px;max-width:580px;">' | ||
| + '<div class="x4-window-header x4-window-header-default x4-window-header-default-top" style="padding:4px 8px;border:none;">' | ||
| + '<p class="x4-window-header-text-container-default" style="font-size:14px;margin:0;">' + LABKEY.Utils.encodeHtml(title) + '</p>' | ||
| + '</div>' | ||
| + '<div class="x4-window-body" style="background:white;padding:10px 12px;">' | ||
| + '<table style="border-collapse:collapse;width:100%;">' | ||
| + '<tr><td style="padding:5px 10px 5px 0;white-space:nowrap;"><label for="lk-annotation-metric-name">Metric Name *</label></td>' | ||
| + '<td style="padding:5px 0;"><input type="text" id="lk-annotation-metric-name" style="width:100%;box-sizing:border-box;" value="' + LABKEY.Utils.encodeHtml(metric.name || '') + '"/></td></tr>' | ||
| + '<tr><td style="padding:5px 10px 5px 0;white-space:nowrap;"><label for="lk-annotation-metric-ylabel">Y-Axis Label *</label></td>' | ||
| + '<td style="padding:5px 0;"><input type="text" id="lk-annotation-metric-ylabel" style="width:100%;box-sizing:border-box;" value="' + LABKEY.Utils.encodeHtml(metric.YAxisLabel || '') + '"/></td></tr>' | ||
| + '<tr><td style="padding:5px 10px 5px 0;white-space:nowrap;">Annotation Type</td>' | ||
| + '<td style="padding:5px 0;">' | ||
| + '<label style="margin-right:16px;"><input type="radio" name="annotationType" value="replicate"' + (!isPrecursor ? ' checked' : '') + '> Replicate</label>' | ||
| + '<label><input type="radio" name="annotationType" value="precursor"' + (isPrecursor ? ' checked' : '') + '> Precursor</label>' | ||
| + '</td></tr>' | ||
| + '<tr><td style="padding:5px 10px 5px 0;white-space:nowrap;"><label for="lk-annotation-name-select">Annotation *</label></td>' | ||
| + '<td style="padding:5px 0;"><select id="lk-annotation-name-select" style="width:100%;box-sizing:border-box;"><option value="">Loading...</option></select></td></tr>' | ||
| + '</table>' | ||
| + '<div id="lk-annotation-metric-error" class="labkey-error" style="display:none;margin-top:8px;"></div>' | ||
| + '<div style="margin-top:12px;text-align:right;">' | ||
| + '<button type="button" class="labkey-button" id="lk-annotation-metric-cancel">Cancel</button>' | ||
| + (op === 'update' ? ' <button type="button" class="labkey-button" id="lk-annotation-metric-delete">Delete</button>' : '') | ||
| + ' <button type="button" class="labkey-button primary" id="lk-annotation-metric-save">Save</button>' | ||
| + '</div>' | ||
| + '</div>' | ||
| + '</div>' | ||
| + '</div>'; | ||
| } | ||
|
|
||
| window.Panorama.Window.AddAnnotationMetricWindow = { | ||
| show: function(config) { | ||
| _config = config; | ||
| _allAnnotations = []; | ||
|
|
||
| $('#' + DIALOG_ID).remove(); | ||
| $('body').append(buildDialogHtml()); | ||
|
|
||
| $('#lk-annotation-metric-cancel').on('click', closeDialog); | ||
| $('#lk-annotation-metric-save').on('click', save); | ||
| if (config.operation === 'update') { | ||
| $('#lk-annotation-metric-delete').on('click', deleteMetric); | ||
| } | ||
| $('input[name="annotationType"]').on('change', refreshAnnotationsSelect); | ||
|
|
||
| // close on overlay click | ||
| $('#' + DIALOG_ID).on('click', function(e) { | ||
| if (e.target === this) closeDialog(); | ||
| }); | ||
|
|
||
| LABKEY.Query.selectRows({ | ||
| schemaName: 'targetedms', | ||
| queryName: 'AnnotationSettings', | ||
| columns: ['Name', 'Targets', 'Type'], | ||
| filterArray: [LABKEY.Filter.create('Type', 'number', LABKEY.Filter.Types.EQUAL)], | ||
| success: function(data) { | ||
| _allAnnotations = data.rows || []; | ||
| refreshAnnotationsSelect(); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| })(jQuery); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.