From 288e462074cd31f3bd68eb06117c7e2ad6060b78 Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Mon, 11 May 2026 16:00:54 -0700 Subject: [PATCH 01/24] flat protein/molecule list for a Skyline document --- resources/schemas/targetedms.xml | 2 +- .../targetedms/TargetedMSController.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml index 8fddaf6bf..2bba67291 100644 --- a/resources/schemas/targetedms.xml +++ b/resources/schemas/targetedms.xml @@ -179,7 +179,7 @@ #,### - /targetedms-showPrecursorList.view?id=${Id} + /targetedms-showProteinList.view?id=${Id} #,### diff --git a/src/org/labkey/targetedms/TargetedMSController.java b/src/org/labkey/targetedms/TargetedMSController.java index 6acf8c302..ea4f4eca0 100644 --- a/src/org/labkey/targetedms/TargetedMSController.java +++ b/src/org/labkey/targetedms/TargetedMSController.java @@ -4534,6 +4534,53 @@ public String getDataRegionNameSmallMolecule() } } + @RequiresPermission(ReadPermission.class) + public class ShowProteinListAction extends SimpleViewAction + { + private static final String DATA_REGION_NAME = "ProteinList"; + + @Override + public ModelAndView getView(RunDetailsForm form, BindException errors) + { + if (!form.hasRunId()) + throw new RedirectException(new ActionURL(ShowListAction.class, getContainer())); + + TargetedMSRun run = validateRun(form.getId()); + + TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); + QuerySettings settings; + String title; + + if (run.getPeptideCount() > 0) + { + settings = new QuerySettings(getViewContext(), DATA_REGION_NAME, TargetedMSSchema.TABLE_PROTEIN); + settings.setBaseFilter(new SimpleFilter(FieldKey.fromParts("PeptideGroupId", "RunId"), form.getId())); + title = "Proteins"; + } + else + { + settings = new QuerySettings(getViewContext(), DATA_REGION_NAME, TargetedMSSchema.TABLE_PEPTIDE_GROUP); + settings.setBaseFilter(new SimpleFilter(FieldKey.fromParts("RunId"), form.getId())); + title = "Molecule Lists"; + } + + settings.setContainerFilterName(null); + QueryView view = schema.createView(getViewContext(), settings, errors); + view.setAllowableContainerFilterTypes(); + view.setShowDetailsColumn(false); + view.setShowFilterDescription(false); + view.setFrame(WebPartView.FrameType.PORTAL); + view.setTitle(title); + return view; + } + + @Override + public void addNavTrail(NavTree root) + { + root.addChild("Targeted MS Runs", getShowListURL(getContainer())); + } + } + @RequiresPermission(ReadPermission.class) public class ShowGroupComparisonAction extends ShowRunSplitDetailsAction { From 4c015f9f63ee92b7d47e5caf95cca79ebfb39e13 Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Tue, 12 May 2026 12:12:12 -0700 Subject: [PATCH 02/24] match columns with Skyline file --- resources/queries/protein/Sequences.query.xml | 13 +++++++++++++ resources/queries/targetedms/Protein/.qview.xml | 16 ++++++++++++++++ .../labkey/targetedms/view/runSummaryView.jsp | 5 ++++- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 resources/queries/protein/Sequences.query.xml create mode 100644 resources/queries/targetedms/Protein/.qview.xml 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..3a83e3054 --- /dev/null +++ b/resources/queries/targetedms/Protein/.qview.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/org/labkey/targetedms/view/runSummaryView.jsp b/src/org/labkey/targetedms/view/runSummaryView.jsp index ba61ad3b5..9bc377563 100644 --- a/src/org/labkey/targetedms/view/runSummaryView.jsp +++ b/src/org/labkey/targetedms/view/runSummaryView.jsp @@ -52,6 +52,9 @@ ActionURL precursorListAction = new ActionURL(TargetedMSController.ShowPrecursorListAction.class, getContainer()); precursorListAction.addParameter("id", run.getId()); + ActionURL proteinListAction = new ActionURL(TargetedMSController.ShowProteinListAction.class, getContainer()); + proteinListAction.addParameter("id", run.getId()); + ActionURL ptmReportAction = new ActionURL(TargetedMSController.ShowPTMReportAction.class, getContainer()); ptmReportAction.addParameter("id", run.getId()); @@ -94,7 +97,7 @@  
- <%= h(StringUtilsLabKey.pluralize(run.getPeptideGroupCount(), peptideGroupLabel))%>, + <%= h(StringUtilsLabKey.pluralize(run.getPeptideGroupCount(), peptideGroupLabel))%>, <% if (run.getPeptideCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getPeptideCount(), "peptide"))%>,<% } %> <% if (run.getSmallMoleculeCount() > 0) { %>"><%= h(StringUtilsLabKey.pluralize(run.getSmallMoleculeCount(), "small molecule"))%>,<% } %> <%= h(StringUtilsLabKey.pluralize(run.getPrecursorCount(), "precursor"))%>, From 61492d4c7bec7e578e1e302eab6e23a01628eef7 Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Thu, 14 May 2026 09:26:27 -0700 Subject: [PATCH 03/24] code review comment --- .../targetedms/TargetedMSController.java | 43 +++++++--------- .../view/DocumentMoleculeGroupsView.java | 51 +++++++++++++++++++ .../targetedms/view/DocumentProteinsView.java | 51 +++++++++++++++++++ 3 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java create mode 100644 src/org/labkey/targetedms/view/DocumentProteinsView.java diff --git a/src/org/labkey/targetedms/TargetedMSController.java b/src/org/labkey/targetedms/TargetedMSController.java index ea4f4eca0..0f500af91 100644 --- a/src/org/labkey/targetedms/TargetedMSController.java +++ b/src/org/labkey/targetedms/TargetedMSController.java @@ -258,7 +258,9 @@ import org.labkey.targetedms.view.CalibrationCurvesView; import org.labkey.targetedms.view.ChromatogramGridView; import org.labkey.targetedms.view.ChromatogramsDataRegion; +import org.labkey.targetedms.view.DocumentMoleculeGroupsView; import org.labkey.targetedms.view.DocumentPrecursorsView; +import org.labkey.targetedms.view.DocumentProteinsView; import org.labkey.targetedms.view.DocumentTransitionsView; import org.labkey.targetedms.view.DocumentView; import org.labkey.targetedms.view.FiguresOfMeritView; @@ -4377,7 +4379,7 @@ public void addNavTrail(NavTree root, TargetedMSRun run) // Action to display a document's transition or precursor list, with both proteomics and small molecule views // ------------------------------------------------------------------------ @RequiresPermission(ReadPermission.class) - public abstract class ShowRunSplitDetailsAction extends AbstractShowRunDetailsAction + public abstract class ShowRunSplitDetailsAction extends AbstractShowRunDetailsAction { public ShowRunSplitDetailsAction() { @@ -4535,49 +4537,38 @@ public String getDataRegionNameSmallMolecule() } @RequiresPermission(ReadPermission.class) - public class ShowProteinListAction extends SimpleViewAction + public class ShowProteinListAction extends ShowRunSplitDetailsAction { - private static final String DATA_REGION_NAME = "ProteinList"; - @Override - public ModelAndView getView(RunDetailsForm form, BindException errors) + protected QueryView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) { - if (!form.hasRunId()) - throw new RedirectException(new ActionURL(ShowListAction.class, getContainer())); - - TargetedMSRun run = validateRun(form.getId()); - TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); - QuerySettings settings; - String title; + QueryView view; - if (run.getPeptideCount() > 0) + if (DocumentProteinsView.DATAREGION_NAME.equals(dataRegion)) { - settings = new QuerySettings(getViewContext(), DATA_REGION_NAME, TargetedMSSchema.TABLE_PROTEIN); - settings.setBaseFilter(new SimpleFilter(FieldKey.fromParts("PeptideGroupId", "RunId"), form.getId())); - title = "Proteins"; + view = new DocumentProteinsView(getViewContext(), schema, form.getId(), forExport); } else { - settings = new QuerySettings(getViewContext(), DATA_REGION_NAME, TargetedMSSchema.TABLE_PEPTIDE_GROUP); - settings.setBaseFilter(new SimpleFilter(FieldKey.fromParts("RunId"), form.getId())); - title = "Molecule Lists"; + view = new DocumentMoleculeGroupsView(getViewContext(), schema, form.getId(), forExport); } - settings.setContainerFilterName(null); - QueryView view = schema.createView(getViewContext(), settings, errors); - view.setAllowableContainerFilterTypes(); view.setShowDetailsColumn(false); view.setShowFilterDescription(false); - view.setFrame(WebPartView.FrameType.PORTAL); - view.setTitle(title); return view; } @Override - public void addNavTrail(NavTree root) + public String getDataRegionNamePeptide() { - root.addChild("Targeted MS Runs", getShowListURL(getContainer())); + return DocumentProteinsView.DATAREGION_NAME; + } + + @Override + public String getDataRegionNameSmallMolecule() + { + return DocumentMoleculeGroupsView.DATAREGION_NAME; } } diff --git a/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java b/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java new file mode 100644 index 000000000..5e7b687ce --- /dev/null +++ b/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.targetedms.view; + +import org.labkey.api.data.CompareType; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryView; +import org.labkey.api.view.ViewContext; +import org.labkey.targetedms.TargetedMSSchema; +import org.labkey.targetedms.query.TargetedMSTable; + +public class DocumentMoleculeGroupsView extends QueryView +{ + public static final String DATAREGION_NAME = "document_molecule_groups_view"; + public static final String TITLE = "Molecule Lists"; + + private final TargetedMSSchema _schema; + private final long _runId; + + public DocumentMoleculeGroupsView(ViewContext ctx, TargetedMSSchema schema, long runId, boolean forExport) + { + super(schema, schema.getSettings(ctx, DATAREGION_NAME, TargetedMSSchema.TABLE_PEPTIDE_GROUP), null); + _schema = schema; + _runId = runId; + setTitle(TITLE); + } + + @Override + public TableInfo createTable() + { + assert null != _schema : "TargetedMSSchema was not set in DocumentMoleculeGroupsView"; + TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(TargetedMSSchema.TABLE_PEPTIDE_GROUP, null, true, true); + tinfo.addContainerTableFilter(new CompareType.EqualsCompareClause(FieldKey.fromParts("Id"), CompareType.EQUAL, _runId)); + tinfo.setLocked(true); + return tinfo; + } +} diff --git a/src/org/labkey/targetedms/view/DocumentProteinsView.java b/src/org/labkey/targetedms/view/DocumentProteinsView.java new file mode 100644 index 000000000..a02a01925 --- /dev/null +++ b/src/org/labkey/targetedms/view/DocumentProteinsView.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.targetedms.view; + +import org.labkey.api.data.CompareType; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryView; +import org.labkey.api.view.ViewContext; +import org.labkey.targetedms.TargetedMSSchema; +import org.labkey.targetedms.query.TargetedMSTable; + +public class DocumentProteinsView extends QueryView +{ + public static final String DATAREGION_NAME = "document_proteins_view"; + public static final String TITLE = "Proteins"; + + private final TargetedMSSchema _schema; + private final long _runId; + + public DocumentProteinsView(ViewContext ctx, TargetedMSSchema schema, long runId, boolean forExport) + { + super(schema, schema.getSettings(ctx, DATAREGION_NAME, TargetedMSSchema.TABLE_PROTEIN), null); + _schema = schema; + _runId = runId; + setTitle(TITLE); + } + + @Override + public TableInfo createTable() + { + assert null != _schema : "TargetedMSSchema was not set in DocumentProteinsView"; + TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(TargetedMSSchema.TABLE_PROTEIN, null, true, true); + tinfo.addContainerTableFilter(new CompareType.EqualsCompareClause(FieldKey.fromParts("Id"), CompareType.EQUAL, _runId)); + tinfo.setLocked(true); + return tinfo; + } +} From 163c16c9ffdf58b173db832d103bfa08dd10f907 Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Thu, 14 May 2026 10:58:25 -0700 Subject: [PATCH 04/24] update text --- src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java | 3 +-- src/org/labkey/targetedms/view/DocumentProteinsView.java | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java b/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java index 5e7b687ce..23b8c2b26 100644 --- a/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java +++ b/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java @@ -26,7 +26,7 @@ public class DocumentMoleculeGroupsView extends QueryView { public static final String DATAREGION_NAME = "document_molecule_groups_view"; - public static final String TITLE = "Molecule Lists"; + public static final String TITLE = "Molecules List"; private final TargetedMSSchema _schema; private final long _runId; @@ -42,7 +42,6 @@ public DocumentMoleculeGroupsView(ViewContext ctx, TargetedMSSchema schema, long @Override public TableInfo createTable() { - assert null != _schema : "TargetedMSSchema was not set in DocumentMoleculeGroupsView"; TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(TargetedMSSchema.TABLE_PEPTIDE_GROUP, null, true, true); tinfo.addContainerTableFilter(new CompareType.EqualsCompareClause(FieldKey.fromParts("Id"), CompareType.EQUAL, _runId)); tinfo.setLocked(true); diff --git a/src/org/labkey/targetedms/view/DocumentProteinsView.java b/src/org/labkey/targetedms/view/DocumentProteinsView.java index a02a01925..1cc1fa36c 100644 --- a/src/org/labkey/targetedms/view/DocumentProteinsView.java +++ b/src/org/labkey/targetedms/view/DocumentProteinsView.java @@ -42,7 +42,6 @@ public DocumentProteinsView(ViewContext ctx, TargetedMSSchema schema, long runId @Override public TableInfo createTable() { - assert null != _schema : "TargetedMSSchema was not set in DocumentProteinsView"; TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(TargetedMSSchema.TABLE_PROTEIN, null, true, true); tinfo.addContainerTableFilter(new CompareType.EqualsCompareClause(FieldKey.fromParts("Id"), CompareType.EQUAL, _runId)); tinfo.setLocked(true); From 294a82b30dfe0571b7045daae274f1c480441730 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Fri, 15 May 2026 23:52:32 -0700 Subject: [PATCH 05/24] More direct Panorama links --- resources/schemas/targetedms.xml | 4 +- .../targetedms/TargetedMSController.java | 55 +++- .../labkey/targetedms/TargetedMSSchema.java | 302 ++++++++++-------- .../query/AnnotatedTargetedMSTable.java | 3 +- .../query/MoleculePrecursorTableInfo.java | 6 +- .../targetedms/query/PrecursorTableInfo.java | 6 +- ...java => DocumentGeneralMoleculesView.java} | 35 +- ...iew.java => DocumentPeptideGroupView.java} | 31 +- .../labkey/targetedms/view/runSummaryView.jsp | 10 +- .../targetedms/TargetedMSExperimentTest.java | 59 +++- 10 files changed, 333 insertions(+), 178 deletions(-) rename src/org/labkey/targetedms/view/{DocumentProteinsView.java => DocumentGeneralMoleculesView.java} (50%) rename src/org/labkey/targetedms/view/{DocumentMoleculeGroupsView.java => DocumentPeptideGroupView.java} (52%) diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml index 2bba67291..2b476325d 100644 --- a/resources/schemas/targetedms.xml +++ b/resources/schemas/targetedms.xml @@ -183,11 +183,11 @@ #,### - /targetedms-showPrecursorList.view?id=${Id} + /targetedms-showPeptideList.view?id=${Id} #,### - /targetedms-showPrecursorList.view?id=${Id} + /targetedms-showMoleculeList.view?id=${Id} #,### diff --git a/src/org/labkey/targetedms/TargetedMSController.java b/src/org/labkey/targetedms/TargetedMSController.java index 0f500af91..75e6ca779 100644 --- a/src/org/labkey/targetedms/TargetedMSController.java +++ b/src/org/labkey/targetedms/TargetedMSController.java @@ -258,11 +258,10 @@ import org.labkey.targetedms.view.CalibrationCurvesView; import org.labkey.targetedms.view.ChromatogramGridView; import org.labkey.targetedms.view.ChromatogramsDataRegion; -import org.labkey.targetedms.view.DocumentMoleculeGroupsView; +import org.labkey.targetedms.view.DocumentGeneralMoleculesView; +import org.labkey.targetedms.view.DocumentPeptideGroupView; import org.labkey.targetedms.view.DocumentPrecursorsView; -import org.labkey.targetedms.view.DocumentProteinsView; import org.labkey.targetedms.view.DocumentTransitionsView; -import org.labkey.targetedms.view.DocumentView; import org.labkey.targetedms.view.FiguresOfMeritView; import org.labkey.targetedms.view.GroupComparisonView; import org.labkey.targetedms.view.InstrumentSummaryWebPart; @@ -4545,13 +4544,15 @@ protected QueryView createQueryView(RunDetailsForm form, BindException errors, b TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); QueryView view; - if (DocumentProteinsView.DATAREGION_NAME.equals(dataRegion)) + if (TargetedMSSchema.TABLE_PEPTIDE_GROUP.equalsIgnoreCase(dataRegion)) { - view = new DocumentProteinsView(getViewContext(), schema, form.getId(), forExport); + view = new DocumentPeptideGroupView(getViewContext(), schema, form.getId(), + TargetedMSSchema.TABLE_PEPTIDE_GROUP, "Proteins"); } else { - view = new DocumentMoleculeGroupsView(getViewContext(), schema, form.getId(), forExport); + view = new DocumentPeptideGroupView(getViewContext(), schema, form.getId(), + TargetedMSSchema.TABLE_MOLECULE_GROUP, "Molecule Lists"); } view.setShowDetailsColumn(false); @@ -4562,13 +4563,51 @@ protected QueryView createQueryView(RunDetailsForm form, BindException errors, b @Override public String getDataRegionNamePeptide() { - return DocumentProteinsView.DATAREGION_NAME; + return TargetedMSSchema.TABLE_PEPTIDE_GROUP; } @Override public String getDataRegionNameSmallMolecule() { - return DocumentMoleculeGroupsView.DATAREGION_NAME; + return TargetedMSSchema.TABLE_MOLECULE_GROUP; + } + } + + @RequiresPermission(ReadPermission.class) + public class ShowPeptideListAction extends ShowRunSingleDetailsAction + { + public ShowPeptideListAction() + { + super(RunDetailsForm.class, "Peptides", TargetedMSSchema.TABLE_PEPTIDE); + } + + @Override + protected QueryView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) + { + TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); + DocumentGeneralMoleculesView view = new DocumentGeneralMoleculesView(getViewContext(), schema, form.getId(), + TargetedMSSchema.TABLE_PEPTIDE, "Peptides"); + view.setShowDetailsColumn(false); + return view; + } + } + + @RequiresPermission(ReadPermission.class) + public class ShowMoleculeListAction extends ShowRunSingleDetailsAction + { + public ShowMoleculeListAction() + { + super(RunDetailsForm.class, "Small Molecules", TargetedMSSchema.TABLE_MOLECULE); + } + + @Override + protected QueryView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) + { + TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); + DocumentGeneralMoleculesView view = new DocumentGeneralMoleculesView(getViewContext(), schema, form.getId(), + TargetedMSSchema.TABLE_MOLECULE, "Small Molecules"); + view.setShowDetailsColumn(false); + return view; } } diff --git a/src/org/labkey/targetedms/TargetedMSSchema.java b/src/org/labkey/targetedms/TargetedMSSchema.java index a863332d8..edc911daa 100644 --- a/src/org/labkey/targetedms/TargetedMSSchema.java +++ b/src/org/labkey/targetedms/TargetedMSSchema.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; +import org.jspecify.annotations.NonNull; import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.collections.CaseInsensitiveHashSet; import org.labkey.api.collections.LongHashMap; @@ -1149,144 +1150,7 @@ public TableInfo createTable(String name, ContainerFilter cf) // Tables that have a FK directly to targetedms.Runs if (TABLE_PEPTIDE_GROUP.equalsIgnoreCase(name) || TABLE_MOLECULE_GROUP.equalsIgnoreCase(name)) { - boolean proteomics = TABLE_PEPTIDE_GROUP.equalsIgnoreCase(name); - TargetedMSTable result = new AnnotatedTargetedMSTable(getSchema().getTable(TABLE_PEPTIDE_GROUP), - this, cf, - ContainerJoinType.RunFK, - TargetedMSManager.getTableInfoPeptideGroupAnnotation(), - "PeptideGroupId", - proteomics ? COL_PROTEIN : COL_LIST, - "protein") - { - @Override - protected Class getDetailsActionClass() - { - return TargetedMSController.ShowProteinAction.class; - } - }; - var labelColumn = result.getMutableColumnOrThrow("Label"); - labelColumn.setURL(result.getDetailsURL(null, null)); - if (proteomics) - { - // Figure out if we have at least 3 replicates marked as QCs, and we have a "Day" or "SampleGroup" annotation, or a value for BatchName - SQLFragment reproducibilitySQL = new SQLFragment("(SELECT CASE WHEN COUNT(DISTINCT r.Id) > 2 THEN COUNT(DISTINCT r.Id) END FROM "); - reproducibilitySQL.append(TargetedMSManager.getTableInfoReplicate(), "r"); - reproducibilitySQL.append(" LEFT OUTER JOIN "); - reproducibilitySQL.append(TargetedMSManager.getTableInfoReplicateAnnotation(), "ra"); - reproducibilitySQL.append(" ON ra.ReplicateId = r.Id AND (LOWER(ra.Name) = 'day' OR LOWER(ra.Name) = 'samplegroup') WHERE r.RunId = "); - reproducibilitySQL.append(ExprColumn.STR_TABLE_ALIAS); - reproducibilitySQL.append(".RunId AND (r.SampleType IS NULL OR r.SampleType IN ('qc')) AND (ra.ReplicateId IS NOT NULL OR r.BatchName IS NOT NULL)"); - reproducibilitySQL.append(")"); - - // Render a link to the reproducibility report - ExprColumn reproducibilityCol = new ExprColumn(result, "Reproducibility", reproducibilitySQL, JdbcType.INTEGER, result.getColumn("Id")); - result.addColumn(reproducibilityCol); - reproducibilityCol.setURL(DetailsURL.fromString("passport-protein.view?proteinId=${Id}")); - - reproducibilityCol.setDisplayColumnFactory(colInfo -> new FontAwesomeLinkColumn(colInfo, "fa-th", "Reproducibility Report")); - - // Create SQL to see if we have one or more calibration curves. - // If there's a single match, the value will be the id of the curve's row. If there are multiple - // curves, the value will be the negative value of the run's row so that we can send the user - // to the full list of curves to let the user choose which peptide to view - SQLFragment calCurveSQL = new SQLFragment("(SELECT CASE WHEN COUNT(*) > 1 THEN "); - calCurveSQL.append(ExprColumn.STR_TABLE_ALIAS); - calCurveSQL.append(".RunId * -1 WHEN COUNT(*) = 1 THEN MIN(cc.Id) END FROM "); - calCurveSQL.append(TargetedMSManager.getTableInfoCalibrationCurve(), "cc"); - calCurveSQL.append(" INNER JOIN "); - calCurveSQL.append(TargetedMSManager.getTableInfoGeneralMolecule(), "gm"); - calCurveSQL.append(" ON cc.GeneralMoleculeId = gm.Id AND gm.PeptideGroupId = "); - calCurveSQL.append(ExprColumn.STR_TABLE_ALIAS); - calCurveSQL.append(".Id)"); - - ExprColumn calCurvesCol = new ExprColumn(result, "CalibrationCurves", calCurveSQL, JdbcType.INTEGER, result.getColumn("Id")); - result.addColumn(calCurvesCol); - - calCurvesCol.setDisplayColumnFactory(colInfo -> new FontAwesomeLinkColumn(colInfo, "fa-line-chart", "Calibration Curves") - { - @Override - protected String renderURLorValueURL(RenderContext ctx) - { - Number value = (Number)getValue(ctx); - if (value != null) - { - // If value is positive, it means there's a single curve and we can link directly to it - if (value.intValue() > 0) - { - return new ActionURL(TargetedMSController.ShowCalibrationCurveAction.class, getContainer()).addParameter("calibrationCurveId", value.intValue()).getLocalURIString(); - } - Long groupId = ctx.get(new FieldKey(getColumnInfo().getFieldKey().getParent(), "Id"), Long.class); - // If the value is negative, it's the run ID. Use it to send the user to the listing - if (value.intValue() < 0 && groupId != null) - { - return new ActionURL(TargetedMSController.ShowCalibrationCurvesAction.class, getContainer()). - addParameter("id", value.intValue() * -1). - addParameter("calibration_curves.GeneralMoleculeId/PeptideGroupId~eq", groupId). - getLocalURIString(); - } - } - return null; - } - }); - - labelColumn.setDisplayColumnFactory(new DisplayColumnFactory() - { - @Override - public DisplayColumn createRenderer(ColumnInfo colInfo) - { - FieldKey seqIdFK = new FieldKey(colInfo.getFieldKey().getParent(), "Id"); - Map params = new HashMap<>(); - params.put("id", seqIdFK); - JSONObject props = new JSONObject(); - props.put("width", 450); - props.put("title", "Protein Details"); - FieldKey containerFieldKey = result.getContainerFieldKey(); - return new AJAXDetailsDisplayColumn(colInfo, new ActionURL(TargetedMSController.ShowProteinAJAXAction.class, getContainer()), params, props, containerFieldKey) - { - @Override - public @NotNull Set getClientDependencies() - { - Set result = super.getClientDependencies(); - result.add(ClientDependency.fromPath("protein/ProteinCoverageMap.css")); - result.add(ClientDependency.fromPath("protein/ProteinCoverageMap.js")); - result.add(ClientDependency.fromPath("util.js")); - return result; - } - }; - } - }); - } - else - { - labelColumn.setLabel(TargetedMSSchema.COL_LIST); - } - result.getMutableColumnOrThrow("RunId").setFk(QueryForeignKey.from(this, cf).to(TABLE_TARGETED_MS_RUNS, "File", null)); - result.getMutableColumnOrThrow("RepresentativeDataState").setFk(QueryForeignKey.from(this, cf).to(TargetedMSSchema.TABLE_REPRESENTATIVE_DATA_STATE, "RowId", null)); - result.getMutableColumnOrThrow("RepresentativeDataState").setHidden(true); - - SQLFragment libPrecursorCountSQL; - if (TargetedMSManager.isLibraryFolder(getContainer())) - { - // In a protein library folder, peptide groups that are in the library, and all their precursors, are marked as "representative". - // In a peptide library, however, only precursors are marked as "representative". So, applying a filter on the "representative" - // state of the peptide groups in a peptide library folder will display an empty grid. - // Add a "RepresentativePrecursorCount" column for the number of precursors in a peptide group that are marked as "representative". - // This count can be used as a filter to display peptide groups that are in the current library in both protein and peptide library folders. - libPrecursorCountSQL = new SQLFragment(" (SELECT COUNT(p.Id) FROM ") - .append(TargetedMSManager.getTableInfoGeneralPrecursor(), "p") - .append(" INNER JOIN ").append(TargetedMSManager.getTableInfoGeneralMolecule(), "gm").append(" ON p.generalmoleculeid = gm.id ") - .append(" WHERE gm.peptidegroupid = ").append(ExprColumn.STR_TABLE_ALIAS).append(".id ") - .append(" AND p.RepresentativeDataState = ? ").add(RepresentativeDataState.Representative.ordinal()) - .append(") "); - } - else - { - libPrecursorCountSQL = new SQLFragment(" (SELECT 0) "); - } - ExprColumn currentLibPrecursorCountCol = new ExprColumn(result, "RepresentativePrecursorCount", libPrecursorCountSQL, JdbcType.INTEGER); - currentLibPrecursorCountCol.setHidden(true); - result.addColumn(currentLibPrecursorCountCol); - return result; + return createGroupTable(name, cf); } if (TABLE_REPLICATE.equalsIgnoreCase(name)) @@ -1705,6 +1569,168 @@ else if (TABLE_INSTRUMENT_USAGE_PAYMENT.equalsIgnoreCase(name)) return null; } + private @NonNull TargetedMSTable createGroupTable(String name, ContainerFilter cf) + { + boolean proteomics = TABLE_PEPTIDE_GROUP.equalsIgnoreCase(name); + TargetedMSTable result = new AnnotatedTargetedMSTable(getSchema().getTable(TABLE_PEPTIDE_GROUP), + this, cf, + ContainerJoinType.RunFK, + TargetedMSManager.getTableInfoPeptideGroupAnnotation(), + "PeptideGroupId", + proteomics ? COL_PROTEIN : COL_LIST, + "protein") + { + @Override + protected Class getDetailsActionClass() + { + return TargetedMSController.ShowProteinAction.class; + } + }; + var labelColumn = result.getMutableColumnOrThrow("Label"); + labelColumn.setURL(result.getDetailsURL(null, null)); + if (proteomics) + { + SQLFragment peptideCountSQL = new SQLFragment("(SELECT COUNT(p.Id) FROM ") + .append(TargetedMSManager.getTableInfoPeptide(), "p") + .append(" INNER JOIN ").append(TargetedMSManager.getTableInfoGeneralMolecule(), "gm") + .append(" ON p.Id = gm.Id WHERE gm.PeptideGroupId = ") + .append(ExprColumn.STR_TABLE_ALIAS).append(".Id)"); + ExprColumn peptideCountCol = new ExprColumn(result, "PeptideCount", peptideCountSQL, JdbcType.INTEGER, result.getColumn("Id")); + peptideCountCol.setLabel("Peptides"); + peptideCountCol.setURL(result.getDetailsURL(null, null)); + result.addColumn(peptideCountCol); + + // Figure out if we have at least 3 replicates marked as QCs, and we have a "Day" or "SampleGroup" annotation, or a value for BatchName + SQLFragment reproducibilitySQL = new SQLFragment("(SELECT CASE WHEN COUNT(DISTINCT r.Id) > 2 THEN COUNT(DISTINCT r.Id) END FROM "); + reproducibilitySQL.append(TargetedMSManager.getTableInfoReplicate(), "r"); + reproducibilitySQL.append(" LEFT OUTER JOIN "); + reproducibilitySQL.append(TargetedMSManager.getTableInfoReplicateAnnotation(), "ra"); + reproducibilitySQL.append(" ON ra.ReplicateId = r.Id AND (LOWER(ra.Name) = 'day' OR LOWER(ra.Name) = 'samplegroup') WHERE r.RunId = "); + reproducibilitySQL.append(ExprColumn.STR_TABLE_ALIAS); + reproducibilitySQL.append(".RunId AND (r.SampleType IS NULL OR r.SampleType IN ('qc')) AND (ra.ReplicateId IS NOT NULL OR r.BatchName IS NOT NULL)"); + reproducibilitySQL.append(")"); + + // Render a link to the reproducibility report + ExprColumn reproducibilityCol = new ExprColumn(result, "Reproducibility", reproducibilitySQL, JdbcType.INTEGER, result.getColumn("Id")); + result.addColumn(reproducibilityCol); + reproducibilityCol.setURL(DetailsURL.fromString("passport-protein.view?proteinId=${Id}")); + + reproducibilityCol.setDisplayColumnFactory(colInfo -> new FontAwesomeLinkColumn(colInfo, "fa-th", "Reproducibility Report")); + + // Create SQL to see if we have one or more calibration curves. + // If there's a single match, the value will be the id of the curve's row. If there are multiple + // curves, the value will be the negative value of the run's row so that we can send the user + // to the full list of curves to let the user choose which peptide to view + SQLFragment calCurveSQL = new SQLFragment("(SELECT CASE WHEN COUNT(*) > 1 THEN "); + calCurveSQL.append(ExprColumn.STR_TABLE_ALIAS); + calCurveSQL.append(".RunId * -1 WHEN COUNT(*) = 1 THEN MIN(cc.Id) END FROM "); + calCurveSQL.append(TargetedMSManager.getTableInfoCalibrationCurve(), "cc"); + calCurveSQL.append(" INNER JOIN "); + calCurveSQL.append(TargetedMSManager.getTableInfoGeneralMolecule(), "gm"); + calCurveSQL.append(" ON cc.GeneralMoleculeId = gm.Id AND gm.PeptideGroupId = "); + calCurveSQL.append(ExprColumn.STR_TABLE_ALIAS); + calCurveSQL.append(".Id)"); + + ExprColumn calCurvesCol = new ExprColumn(result, "CalibrationCurves", calCurveSQL, JdbcType.INTEGER, result.getColumn("Id")); + result.addColumn(calCurvesCol); + + calCurvesCol.setDisplayColumnFactory(colInfo -> new FontAwesomeLinkColumn(colInfo, "fa-line-chart", "Calibration Curves") + { + @Override + protected String renderURLorValueURL(RenderContext ctx) + { + Number value = (Number)getValue(ctx); + if (value != null) + { + // If value is positive, it means there's a single curve and we can link directly to it + if (value.intValue() > 0) + { + return new ActionURL(TargetedMSController.ShowCalibrationCurveAction.class, getContainer()).addParameter("calibrationCurveId", value.intValue()).getLocalURIString(); + } + Long groupId = ctx.get(new FieldKey(getColumnInfo().getFieldKey().getParent(), "Id"), Long.class); + // If the value is negative, it's the run ID. Use it to send the user to the listing + if (value.intValue() < 0 && groupId != null) + { + return new ActionURL(TargetedMSController.ShowCalibrationCurvesAction.class, getContainer()). + addParameter("id", value.intValue() * -1). + addParameter("calibration_curves.GeneralMoleculeId/PeptideGroupId~eq", groupId). + getLocalURIString(); + } + } + return null; + } + }); + + labelColumn.setDisplayColumnFactory(new DisplayColumnFactory() + { + @Override + public DisplayColumn createRenderer(ColumnInfo colInfo) + { + FieldKey seqIdFK = new FieldKey(colInfo.getFieldKey().getParent(), "Id"); + Map params = new HashMap<>(); + params.put("id", seqIdFK); + JSONObject props = new JSONObject(); + props.put("width", 450); + props.put("title", "Protein Details"); + FieldKey containerFieldKey = result.getContainerFieldKey(); + return new AJAXDetailsDisplayColumn(colInfo, new ActionURL(TargetedMSController.ShowProteinAJAXAction.class, getContainer()), params, props, containerFieldKey) + { + @Override + public @NotNull Set getClientDependencies() + { + Set result = super.getClientDependencies(); + result.add(ClientDependency.fromPath("protein/ProteinCoverageMap.css")); + result.add(ClientDependency.fromPath("protein/ProteinCoverageMap.js")); + result.add(ClientDependency.fromPath("util.js")); + return result; + } + }; + } + }); + } + else + { + labelColumn.setLabel(TargetedMSSchema.COL_LIST); + + SQLFragment moleculeCountSQL = new SQLFragment("(SELECT COUNT(m.Id) FROM ") + .append(TargetedMSManager.getTableInfoMolecule(), "m") + .append(" INNER JOIN ").append(TargetedMSManager.getTableInfoGeneralMolecule(), "gm") + .append(" ON m.Id = gm.Id WHERE gm.PeptideGroupId = ") + .append(ExprColumn.STR_TABLE_ALIAS).append(".Id)"); + ExprColumn moleculeCountCol = new ExprColumn(result, "MoleculeCount", moleculeCountSQL, JdbcType.INTEGER, result.getColumn("Id")); + moleculeCountCol.setLabel("Molecules"); + moleculeCountCol.setURL(result.getDetailsURL(null, null)); + result.addColumn(moleculeCountCol); + } + result.getMutableColumnOrThrow("RunId").setFk(QueryForeignKey.from(this, cf).to(TABLE_TARGETED_MS_RUNS, "File", null)); + result.getMutableColumnOrThrow("RepresentativeDataState").setFk(QueryForeignKey.from(this, cf).to(TargetedMSSchema.TABLE_REPRESENTATIVE_DATA_STATE, "RowId", null)); + result.getMutableColumnOrThrow("RepresentativeDataState").setHidden(true); + + SQLFragment libPrecursorCountSQL; + if (TargetedMSManager.isLibraryFolder(getContainer())) + { + // In a protein library folder, peptide groups that are in the library, and all their precursors, are marked as "representative". + // In a peptide library, however, only precursors are marked as "representative". So, applying a filter on the "representative" + // state of the peptide groups in a peptide library folder will display an empty grid. + // Add a "RepresentativePrecursorCount" column for the number of precursors in a peptide group that are marked as "representative". + // This count can be used as a filter to display peptide groups that are in the current library in both protein and peptide library folders. + libPrecursorCountSQL = new SQLFragment(" (SELECT COUNT(p.Id) FROM ") + .append(TargetedMSManager.getTableInfoGeneralPrecursor(), "p") + .append(" INNER JOIN ").append(TargetedMSManager.getTableInfoGeneralMolecule(), "gm").append(" ON p.generalmoleculeid = gm.id ") + .append(" WHERE gm.peptidegroupid = ").append(ExprColumn.STR_TABLE_ALIAS).append(".id ") + .append(" AND p.RepresentativeDataState = ? ").add(RepresentativeDataState.Representative.ordinal()) + .append(") "); + } + else + { + libPrecursorCountSQL = new SQLFragment(" (SELECT 0) "); + } + ExprColumn currentLibPrecursorCountCol = new ExprColumn(result, "RepresentativePrecursorCount", libPrecursorCountSQL, JdbcType.INTEGER); + currentLibPrecursorCountCol.setHidden(true); + result.addColumn(currentLibPrecursorCountCol); + return result; + } + @Override protected QuerySettings createQuerySettings(String dataRegionName, String queryName, String viewName) { diff --git a/src/org/labkey/targetedms/query/AnnotatedTargetedMSTable.java b/src/org/labkey/targetedms/query/AnnotatedTargetedMSTable.java index f462dc497..5e0da5664 100644 --- a/src/org/labkey/targetedms/query/AnnotatedTargetedMSTable.java +++ b/src/org/labkey/targetedms/query/AnnotatedTargetedMSTable.java @@ -56,6 +56,7 @@ public class AnnotatedTargetedMSTable extends TargetedMSTable { private static final String ANNOT_NAME_VALUE_SEPARATOR = ": "; private static final String ANNOT_DELIMITER = "\n"; + public static final String NOTE_ANNOTATIONS_COLUMN_NAME = "NoteAnnotations"; public AnnotatedTargetedMSTable(TableInfo table, TargetedMSSchema schema, @@ -160,7 +161,7 @@ protected void addAnnotationsColumns(TableInfo annotationTableInfo, String annot annotationsColumn.setDisplayColumnFactory(AnnotationsDisplayColumn::new); - var noteAnnotation = WrappedColumnInfo.wrapAsCopy(this, FieldKey.fromParts("NoteAnnotations"), annotationsColumn, labelPrefix + " Note/Annotations", null); + var noteAnnotation = WrappedColumnInfo.wrapAsCopy(this, FieldKey.fromParts(NOTE_ANNOTATIONS_COLUMN_NAME), annotationsColumn, labelPrefix + " Note/Annotations", null); noteAnnotation.setDisplayColumnFactory(AnnotationUIDisplayColumn::new); addColumn(noteAnnotation); } diff --git a/src/org/labkey/targetedms/query/MoleculePrecursorTableInfo.java b/src/org/labkey/targetedms/query/MoleculePrecursorTableInfo.java index 71e0d3d70..fcb0652c9 100644 --- a/src/org/labkey/targetedms/query/MoleculePrecursorTableInfo.java +++ b/src/org/labkey/targetedms/query/MoleculePrecursorTableInfo.java @@ -57,16 +57,16 @@ public MoleculePrecursorTableInfo(final TableInfo tableInfo, String tableName, f ArrayList visibleColumns = new ArrayList<>(); visibleColumns.add(FieldKey.fromParts("MoleculeId", "PeptideGroupId", "Label")); visibleColumns.add(FieldKey.fromParts("MoleculeId", "PeptideGroupId", "Description")); - visibleColumns.add(FieldKey.fromParts("MoleculeId", "PeptideGroupId", "NoteAnnotations")); + visibleColumns.add(FieldKey.fromParts("MoleculeId", "PeptideGroupId", NOTE_ANNOTATIONS_COLUMN_NAME)); visibleColumns.add(FieldKey.fromParts("MoleculeId", "Molecule")); visibleColumns.add(FieldKey.fromParts("MoleculeId", "IonFormula")); - visibleColumns.add(FieldKey.fromParts("MoleculeId", "NoteAnnotations")); + visibleColumns.add(FieldKey.fromParts("MoleculeId", NOTE_ANNOTATIONS_COLUMN_NAME)); visibleColumns.add(FieldKey.fromParts("MoleculeId", "MassAverage")); visibleColumns.add(FieldKey.fromParts("MoleculeId", "MassMonoisotopic")); visibleColumns.add(FieldKey.fromParts("CustomIonName")); - visibleColumns.add(FieldKey.fromParts("NoteAnnotations")); + visibleColumns.add(FieldKey.fromParts(NOTE_ANNOTATIONS_COLUMN_NAME)); visibleColumns.add(FieldKey.fromParts("Charge")); visibleColumns.add(FieldKey.fromParts("Mz")); visibleColumns.add(FieldKey.fromParts("MassAverage")); diff --git a/src/org/labkey/targetedms/query/PrecursorTableInfo.java b/src/org/labkey/targetedms/query/PrecursorTableInfo.java index 67a825a12..a247e232f 100644 --- a/src/org/labkey/targetedms/query/PrecursorTableInfo.java +++ b/src/org/labkey/targetedms/query/PrecursorTableInfo.java @@ -64,16 +64,16 @@ public PrecursorTableInfo(final TableInfo tableInfo, ContainerFilter cf, String visibleColumns.add(FieldKey.fromParts("PeptideId", "PeptideGroupId", "Label")); visibleColumns.add(FieldKey.fromParts("PeptideId", "PeptideGroupId", "Description")); - visibleColumns.add(FieldKey.fromParts("PeptideId", "PeptideGroupId", "NoteAnnotations")); + visibleColumns.add(FieldKey.fromParts("PeptideId", "PeptideGroupId", NOTE_ANNOTATIONS_COLUMN_NAME)); visibleColumns.add(FieldKey.fromParts("PeptideId", ModifiedSequenceDisplayColumn.PEPTIDE_COLUMN_NAME)); - visibleColumns.add(FieldKey.fromParts("PeptideId", "NoteAnnotations")); + visibleColumns.add(FieldKey.fromParts("PeptideId", NOTE_ANNOTATIONS_COLUMN_NAME)); visibleColumns.add(FieldKey.fromParts("PeptideId", "NumMissedCleavages")); visibleColumns.add(FieldKey.fromParts("PeptideId", "CalcNeutralMass")); visibleColumns.add(FieldKey.fromParts("PeptideId", "Rank")); visibleColumns.add(FieldKey.fromParts(ModifiedSequenceDisplayColumn.PRECURSOR_COLUMN_NAME)); - visibleColumns.add(FieldKey.fromParts("NoteAnnotations")); + visibleColumns.add(FieldKey.fromParts(NOTE_ANNOTATIONS_COLUMN_NAME)); visibleColumns.add(FieldKey.fromParts("IsotopeLabelId", "Name")); visibleColumns.add(FieldKey.fromParts("Charge")); visibleColumns.add(FieldKey.fromParts("Mz")); diff --git a/src/org/labkey/targetedms/view/DocumentProteinsView.java b/src/org/labkey/targetedms/view/DocumentGeneralMoleculesView.java similarity index 50% rename from src/org/labkey/targetedms/view/DocumentProteinsView.java rename to src/org/labkey/targetedms/view/DocumentGeneralMoleculesView.java index 1cc1fa36c..f955e5774 100644 --- a/src/org/labkey/targetedms/view/DocumentProteinsView.java +++ b/src/org/labkey/targetedms/view/DocumentGeneralMoleculesView.java @@ -23,28 +23,45 @@ import org.labkey.targetedms.TargetedMSSchema; import org.labkey.targetedms.query.TargetedMSTable; -public class DocumentProteinsView extends QueryView -{ - public static final String DATAREGION_NAME = "document_proteins_view"; - public static final String TITLE = "Proteins"; +import java.util.ArrayList; +import java.util.List; + +import static org.labkey.targetedms.query.AnnotatedTargetedMSTable.NOTE_ANNOTATIONS_COLUMN_NAME; +public class DocumentGeneralMoleculesView extends QueryView +{ private final TargetedMSSchema _schema; private final long _runId; - public DocumentProteinsView(ViewContext ctx, TargetedMSSchema schema, long runId, boolean forExport) + public DocumentGeneralMoleculesView(ViewContext ctx, TargetedMSSchema schema, long runId, + String tableName, String title) { - super(schema, schema.getSettings(ctx, DATAREGION_NAME, TargetedMSSchema.TABLE_PROTEIN), null); + super(schema, schema.getSettings(ctx, tableName, tableName), null); _schema = schema; _runId = runId; - setTitle(TITLE); + setTitle(title); } @Override public TableInfo createTable() { - TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(TargetedMSSchema.TABLE_PROTEIN, null, true, true); + TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(getSettings().getQueryName(), null, true, true); tinfo.addContainerTableFilter(new CompareType.EqualsCompareClause(FieldKey.fromParts("Id"), CompareType.EQUAL, _runId)); + List cols = new ArrayList<>(tinfo.getDefaultVisibleColumns()); + cols.remove(FieldKey.fromParts("PeptideGroupId", "RunId", "Folder", "Path")); + cols.remove(FieldKey.fromParts("PeptideGroupId", "RunId", "File")); + cols.remove(FieldKey.fromParts(NOTE_ANNOTATIONS_COLUMN_NAME)); + cols.remove(FieldKey.fromParts("NormalizationMethod")); + cols.remove(FieldKey.fromParts("InternalStandardConcentration")); + cols.remove(FieldKey.fromParts("AttributeGroupId")); + cols.remove(FieldKey.fromParts("ConcentrationMultiplier")); + + cols.remove(FieldKey.fromParts("MoleculeId")); + + cols.remove(FieldKey.fromParts("PeptideModifiedSequence")); + cols.remove(FieldKey.fromParts("Sequence")); + tinfo.setDefaultVisibleColumns(cols); tinfo.setLocked(true); return tinfo; } -} +} \ No newline at end of file diff --git a/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java b/src/org/labkey/targetedms/view/DocumentPeptideGroupView.java similarity index 52% rename from src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java rename to src/org/labkey/targetedms/view/DocumentPeptideGroupView.java index 23b8c2b26..dbe02552d 100644 --- a/src/org/labkey/targetedms/view/DocumentMoleculeGroupsView.java +++ b/src/org/labkey/targetedms/view/DocumentPeptideGroupView.java @@ -23,28 +23,41 @@ import org.labkey.targetedms.TargetedMSSchema; import org.labkey.targetedms.query.TargetedMSTable; -public class DocumentMoleculeGroupsView extends QueryView -{ - public static final String DATAREGION_NAME = "document_molecule_groups_view"; - public static final String TITLE = "Molecules List"; +import java.util.ArrayList; +import java.util.List; + +import static org.labkey.targetedms.query.AnnotatedTargetedMSTable.NOTE_ANNOTATIONS_COLUMN_NAME; +public class DocumentPeptideGroupView extends QueryView +{ private final TargetedMSSchema _schema; private final long _runId; - public DocumentMoleculeGroupsView(ViewContext ctx, TargetedMSSchema schema, long runId, boolean forExport) + public DocumentPeptideGroupView(ViewContext ctx, TargetedMSSchema schema, long runId, + String tableName, String title) { - super(schema, schema.getSettings(ctx, DATAREGION_NAME, TargetedMSSchema.TABLE_PEPTIDE_GROUP), null); + super(schema, schema.getSettings(ctx, tableName, tableName), null); _schema = schema; _runId = runId; - setTitle(TITLE); + setTitle(title); } @Override public TableInfo createTable() { - TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(TargetedMSSchema.TABLE_PEPTIDE_GROUP, null, true, true); + TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(getSettings().getQueryName(), null, true, true); tinfo.addContainerTableFilter(new CompareType.EqualsCompareClause(FieldKey.fromParts("Id"), CompareType.EQUAL, _runId)); + String queryName = getSettings().getQueryName(); + if (TargetedMSSchema.TABLE_PEPTIDE_GROUP.equalsIgnoreCase(queryName)) + getSettings().getBaseFilter().addCondition(FieldKey.fromParts("PeptideCount"), 0, CompareType.GT); + else if (TargetedMSSchema.TABLE_MOLECULE_GROUP.equalsIgnoreCase(queryName)) + getSettings().getBaseFilter().addCondition(FieldKey.fromParts("MoleculeCount"), 0, CompareType.GT); + List cols = new ArrayList<>(tinfo.getDefaultVisibleColumns()); + cols.remove(FieldKey.fromParts("RunId")); + cols.remove(FieldKey.fromParts(NOTE_ANNOTATIONS_COLUMN_NAME)); + cols.remove(FieldKey.fromParts("Modified")); + tinfo.setDefaultVisibleColumns(cols); tinfo.setLocked(true); return tinfo; } -} +} \ No newline at end of file diff --git a/src/org/labkey/targetedms/view/runSummaryView.jsp b/src/org/labkey/targetedms/view/runSummaryView.jsp index 9bc377563..d6ee40a6f 100644 --- a/src/org/labkey/targetedms/view/runSummaryView.jsp +++ b/src/org/labkey/targetedms/view/runSummaryView.jsp @@ -70,6 +70,12 @@ ActionURL calibrationCurveListAction = new ActionURL(TargetedMSController.ShowCalibrationCurvesAction.class, getContainer()); calibrationCurveListAction.addParameter("id", run.getId()); + ActionURL peptideListAction = new ActionURL(TargetedMSController.ShowPeptideListAction.class, getContainer()); + peptideListAction.addParameter("id", run.getId()); + + ActionURL moleculeListAction = new ActionURL(TargetedMSController.ShowMoleculeListAction.class, getContainer()); + moleculeListAction.addParameter("id", run.getId()); + ActionURL listAction = new ActionURL(TargetedMSController.ShowListsAction.class, getContainer()); listAction.addParameter("id", run.getId()); @@ -98,8 +104,8 @@  
<%= h(StringUtilsLabKey.pluralize(run.getPeptideGroupCount(), peptideGroupLabel))%>, - <% if (run.getPeptideCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getPeptideCount(), "peptide"))%>,<% } %> - <% if (run.getSmallMoleculeCount() > 0) { %>"><%= h(StringUtilsLabKey.pluralize(run.getSmallMoleculeCount(), "small molecule"))%>,<% } %> + <% if (run.getPeptideCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getPeptideCount(), "peptide"))%>,<% } %> + <% if (run.getSmallMoleculeCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getSmallMoleculeCount(), "small molecule"))%>,<% } %> <%= h(StringUtilsLabKey.pluralize(run.getPrecursorCount(), "precursor"))%>, <%= h(StringUtilsLabKey.pluralize(run.getTransitionCount(), "transition"))%>  -  diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 2f70382b3..7bf183eea 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -90,6 +90,7 @@ public void testSteps() throws IOException, CommandException verifyImportedSmallMoleculeData(); verifyAttributeGroupIdCalcs(); testRawFileLinks(SKY_FILE_SMALLMOL_PEP); + verifyRunDetailLinks(); // SKYD version 14 importData(SKY_FILE_SKYD_14, ++jobCount); @@ -483,7 +484,7 @@ private void verifyQueries() query.setFilter("Sequence", "Equals", "TNNPETLVALR"); query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label")); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("TNNPETLVALR", query.getDataAsText(0, "Modified Peptide")); assertEquals("K", query.getDataAsText(0, "Next Aa")); @@ -516,7 +517,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Q1 m/z")); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label")); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); @@ -552,7 +553,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(3, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Precursor Id Mz")); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label")); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); @@ -895,4 +896,56 @@ private void verifyNoGuestAccessMessage(boolean selfSignupEnabled) assertFalse(fullBodyText.contains("Don't have an account? Register")); } } + + @LogMethod + protected void verifyRunDetailLinks() + { + // From the runs listing (Panorama Dashboard), clicking a run's name navigates to the default + // run view. A mixed proteomics + small molecule document shows both precursor grids there. + // precursors_view is nested by protein group (24 total groups, 10 shown per page due to maxRows). + // getDataRowCount() returns outer rows + even-indexed expanded rows: 5+5+5=15 for 10 groups. + // small_mol_precursors_view has 3 molecule groups (all fit on one page): 2+1+2=5. + goToDashboard(); + clickAndWait(Locator.linkContainingText(SKY_FILE_SMALLMOL_PEP)); + DataRegionTable precursorsView = DataRegion(getDriver()).withName("precursors_view").find(); + assertEquals(15, precursorsView.getDataRowCount()); + DataRegionTable smallMolPrecursorsView = DataRegion(getDriver()).withName("small_mol_precursors_view").find(); + assertEquals(5, smallMolPrecursorsView.getDataRowCount()); + + // Test each header link from runSummaryView.jsp while on the default run view. + // Each link must reach its own dedicated list page, not the precursor list. + + // "27 molecule lists" → protein/molecule-list page: 24 protein groups + 3 molecule groups + clickAndWait(Locator.linkContainingText("27 molecule lists")); + DataRegionTable peptideGroupView = DataRegion(getDriver()).withName("PeptideGroup").find(); + assertEquals(24, peptideGroupView.getDataRowCount()); + DataRegionTable moleculeGroupView = DataRegion(getDriver()).withName("MoleculeGroup").find(); + assertEquals(3, moleculeGroupView.getDataRowCount()); + + // Navigate back to the run's precursor list via the breadcrumb. + clickAndWait(Locator.linkContainingText("Targeted MS Runs")); + + // "44 peptides" → peptide list page: one row per peptide + clickAndWait(Locator.linkContainingText("44")); + DataRegionTable peptideView = DataRegion(getDriver()).withName("Peptide").find(); + assertEquals(44, peptideView.getDataRowCount()); + + // "98 small molecules" → small molecule list page: one row per molecule + clickAndWait(Locator.linkContainingText("98 small molecules")); + DataRegionTable moleculeView = DataRegion(getDriver()).withName("Molecule").find(); + assertEquals(98, moleculeView.getDataRowCount()); + + // Verify the runSummaryView.jsp header links also work when on a non-default run detail page. + // The header is embedded on every run detail view, not just the precursor list. + // From the small molecule list page, click "27 molecule lists". + clickAndWait(Locator.linkContainingText("27 molecule lists")); + peptideGroupView = DataRegion(getDriver()).withName("PeptideGroup").find(); + assertEquals(24, peptideGroupView.getDataRowCount()); + moleculeGroupView = DataRegion(getDriver()).withName("MoleculeGroup").find(); + assertEquals(3, moleculeGroupView.getDataRowCount()); + // From the protein/molecule-list page, click "44 peptides". + clickAndWait(Locator.linkContainingText("44 peptides")); + peptideView = DataRegion(getDriver()).withName("Peptide").find(); + assertEquals(44, peptideView.getDataRowCount()); + } } From ec31d7d78122ca00288f21793062ea264d58bd1a Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sat, 16 May 2026 09:33:36 -0700 Subject: [PATCH 06/24] Self-review --- resources/queries/targetedms/Protein/.qview.xml | 2 +- src/org/labkey/targetedms/TargetedMSController.java | 13 ++++++++----- src/org/labkey/targetedms/TargetedMSSchema.java | 2 +- .../view/DocumentGeneralMoleculesView.java | 2 +- .../targetedms/view/DocumentPeptideGroupView.java | 2 +- .../targetedms/view/DocumentPrecursorsView.java | 1 - 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/resources/queries/targetedms/Protein/.qview.xml b/resources/queries/targetedms/Protein/.qview.xml index 3a83e3054..5659c1ee6 100644 --- a/resources/queries/targetedms/Protein/.qview.xml +++ b/resources/queries/targetedms/Protein/.qview.xml @@ -13,4 +13,4 @@ - \ No newline at end of file + diff --git a/src/org/labkey/targetedms/TargetedMSController.java b/src/org/labkey/targetedms/TargetedMSController.java index 75e6ca779..196ae81c1 100644 --- a/src/org/labkey/targetedms/TargetedMSController.java +++ b/src/org/labkey/targetedms/TargetedMSController.java @@ -4498,7 +4498,7 @@ public ShowPrecursorListAction(ViewContext ctx) protected DocumentPrecursorsView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) { DocumentPrecursorsView view; - if(PeptidePrecursorsView.DATAREGION_NAME.equals(dataRegion)) + if (PeptidePrecursorsView.DATAREGION_NAME.equals(dataRegion)) { view = new PeptidePrecursorsView(getViewContext(), new TargetedMSSchema(getUser(), getContainer()), @@ -4506,7 +4506,7 @@ protected DocumentPrecursorsView createQueryView(RunDetailsForm form, BindExcept form.getId(), forExport); } - else + else if (SmallMoleculePrecursorsView.DATAREGION_NAME.equals(dataRegion)) { view = new SmallMoleculePrecursorsView(getViewContext(), new TargetedMSSchema(getUser(), getContainer()), @@ -4515,9 +4515,12 @@ protected DocumentPrecursorsView createQueryView(RunDetailsForm form, BindExcept forExport); } - view.setShowExportButtons(true); - view.setShowDetailsColumn(false); - view.setButtonBarPosition(DataRegion.ButtonBarPosition.BOTH); + if (view != null) + { + view.setShowExportButtons(true); + view.setShowDetailsColumn(false); + view.setButtonBarPosition(DataRegion.ButtonBarPosition.BOTH); + } return view; } diff --git a/src/org/labkey/targetedms/TargetedMSSchema.java b/src/org/labkey/targetedms/TargetedMSSchema.java index edc911daa..39ebe481a 100644 --- a/src/org/labkey/targetedms/TargetedMSSchema.java +++ b/src/org/labkey/targetedms/TargetedMSSchema.java @@ -1569,7 +1569,7 @@ else if (TABLE_INSTRUMENT_USAGE_PAYMENT.equalsIgnoreCase(name)) return null; } - private @NonNull TargetedMSTable createGroupTable(String name, ContainerFilter cf) + private @NotNull TargetedMSTable createGroupTable(String name, ContainerFilter cf) { boolean proteomics = TABLE_PEPTIDE_GROUP.equalsIgnoreCase(name); TargetedMSTable result = new AnnotatedTargetedMSTable(getSchema().getTable(TABLE_PEPTIDE_GROUP), diff --git a/src/org/labkey/targetedms/view/DocumentGeneralMoleculesView.java b/src/org/labkey/targetedms/view/DocumentGeneralMoleculesView.java index f955e5774..adf38d4c8 100644 --- a/src/org/labkey/targetedms/view/DocumentGeneralMoleculesView.java +++ b/src/org/labkey/targetedms/view/DocumentGeneralMoleculesView.java @@ -64,4 +64,4 @@ public TableInfo createTable() tinfo.setLocked(true); return tinfo; } -} \ No newline at end of file +} diff --git a/src/org/labkey/targetedms/view/DocumentPeptideGroupView.java b/src/org/labkey/targetedms/view/DocumentPeptideGroupView.java index dbe02552d..89b333f06 100644 --- a/src/org/labkey/targetedms/view/DocumentPeptideGroupView.java +++ b/src/org/labkey/targetedms/view/DocumentPeptideGroupView.java @@ -60,4 +60,4 @@ else if (TargetedMSSchema.TABLE_MOLECULE_GROUP.equalsIgnoreCase(queryName)) tinfo.setLocked(true); return tinfo; } -} \ No newline at end of file +} diff --git a/src/org/labkey/targetedms/view/DocumentPrecursorsView.java b/src/org/labkey/targetedms/view/DocumentPrecursorsView.java index 604db0400..4732c8d19 100644 --- a/src/org/labkey/targetedms/view/DocumentPrecursorsView.java +++ b/src/org/labkey/targetedms/view/DocumentPrecursorsView.java @@ -36,5 +36,4 @@ public DocumentPrecursorsView(ViewContext ctx, TargetedMSSchema schema, String q _targetedMsSchema = schema; _tableName = queryName; } - } From 646ca68b0c7a9e848e215c436d4fe389577d219e Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sat, 16 May 2026 09:41:09 -0700 Subject: [PATCH 07/24] Fix build --- src/org/labkey/targetedms/TargetedMSController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/targetedms/TargetedMSController.java b/src/org/labkey/targetedms/TargetedMSController.java index 196ae81c1..29470372e 100644 --- a/src/org/labkey/targetedms/TargetedMSController.java +++ b/src/org/labkey/targetedms/TargetedMSController.java @@ -4497,7 +4497,7 @@ public ShowPrecursorListAction(ViewContext ctx) @Override protected DocumentPrecursorsView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) { - DocumentPrecursorsView view; + DocumentPrecursorsView view = null; if (PeptidePrecursorsView.DATAREGION_NAME.equals(dataRegion)) { view = new PeptidePrecursorsView(getViewContext(), From 2ce8072b635784de034b20b4ec054c2a55507539 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sat, 16 May 2026 10:24:26 -0700 Subject: [PATCH 08/24] Remove extra import --- src/org/labkey/targetedms/TargetedMSSchema.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/labkey/targetedms/TargetedMSSchema.java b/src/org/labkey/targetedms/TargetedMSSchema.java index 39ebe481a..1bacf57d6 100644 --- a/src/org/labkey/targetedms/TargetedMSSchema.java +++ b/src/org/labkey/targetedms/TargetedMSSchema.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; -import org.jspecify.annotations.NonNull; import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.collections.CaseInsensitiveHashSet; import org.labkey.api.collections.LongHashMap; From d76fb319ca037b170384e9857b1e66853a8f08de Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 10:02:38 -0700 Subject: [PATCH 09/24] Fix test failures --- src/org/labkey/targetedms/TargetedMSSchema.java | 3 ++- .../tests/targetedms/TargetedMSLibraryTest.java | 16 ++++++++-------- .../targetedms/TargetedMSPeptideLibraryTest.java | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/org/labkey/targetedms/TargetedMSSchema.java b/src/org/labkey/targetedms/TargetedMSSchema.java index 1bacf57d6..312caebc2 100644 --- a/src/org/labkey/targetedms/TargetedMSSchema.java +++ b/src/org/labkey/targetedms/TargetedMSSchema.java @@ -78,6 +78,7 @@ import org.labkey.api.util.SafeToRender; import org.labkey.api.util.UnexpectedException; import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NotFoundException; import org.labkey.api.view.PopupMenu; import org.labkey.api.view.ViewContext; import org.labkey.api.view.template.ClientDependency; @@ -1959,7 +1960,7 @@ private QueryDefinition createRunScopedPTMQuery(String prefix, String baseQueryN long runId = Long.parseLong(runIdString); if (TargetedMSManager.getFolderType(getContainer()) != TargetedMSService.FolderType.ExperimentMAM) { - throw new IllegalStateException("PTM queries are only supported in ExperimentMAM folders"); + throw new NotFoundException("PTM queries are only supported in ExperimentMAM folders"); } QueryDefinition queryDef = Objects.requireNonNull(getQueryDef(baseQueryName)); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java index ea4057fe6..9c971a789 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java @@ -111,7 +111,7 @@ protected void verifyRevision1() // Verify proteins in the library // Proteins in SKY_FILE1: "CTCF", "TAF11", "MAX", "QPrEST_CystC_HPRR5000001", "HPRR1440042", "DifferentProteinSameLabel", "iRT-C18 Standard Peptides" List proteins = Arrays.asList("CTCF", "TAF11", "MAX", "QPrEST_CystC_HPRR5000001", "HPRR1440042", "DifferentProteinSameLabel", "iRT-C18 Standard Peptides"); - List files = new ArrayList(); + List files = new ArrayList<>(); for (int i = 0; i < proteins.size(); i++) { files.add(SKY_FILE1); // All proteins are from the same file in version 1. @@ -132,7 +132,7 @@ private void verifyLibraryProteins(List proteins, List files, in int i = 0; for(String protein: proteins) { - int idx = proteinsTable.getRowIndex("Label", protein); + int idx = proteinsTable.getRowIndex("Label", protein + "⁠"); // Include trailing non-breaking space assertTrue("Expected protein " + protein + " not found in table", idx != -1); ListfileName = proteinsTable.getRowDataAsText(idx, "RunId/File"); assertEquals(1, fileName.size()); @@ -275,8 +275,8 @@ private void verifyAndResolveConflicts() var proteinName = "MAX"; var oldProteinCb = getCheckBox(proteinName, true).waitForElement(shortWait()); var newProteinCb = getCheckBox(proteinName, false).waitForElement(shortWait()); - assertTrue(isChecked(newProteinCb)); // checkbox for the protein in the new document should be checked - assertFalse(isChecked(oldProteinCb)); // checkbox for the protein in the old document should be unchecked + assertTrue(newProteinCb.isSelected()); // checkbox for the protein in the new document should be checked + assertFalse(oldProteinCb.isSelected()); // checkbox for the protein in the old document should be unchecked // check, uncheck and check again (See Issue 44424) changeChecked(oldProteinCb, newProteinCb , true, proteinName); changeChecked(oldProteinCb, newProteinCb , false, proteinName); @@ -290,13 +290,13 @@ private void changeChecked(WebElement oldProteinCb, WebElement newProteinCb, boo setCheckbox(oldProteinCb, selectOld); if (selectOld) { - assertFalse("Expected " + proteinName + " from the new document to be unchecked", isChecked(newProteinCb)); // new protein unchecked - assertTrue("Expected " + proteinName + " from the old document to be checked", isChecked(oldProteinCb)); // old protein checked + assertFalse("Expected " + proteinName + " from the new document to be unchecked", newProteinCb.isSelected()); // new protein unchecked + assertTrue("Expected " + proteinName + " from the old document to be checked", oldProteinCb.isSelected()); // old protein checked } else { - assertTrue("Expected " + proteinName + " from the new document to be checked", isChecked(newProteinCb)); // new protein checked - assertFalse("Expected " + proteinName + " from the old document to be unchecked", isChecked(oldProteinCb)); // old protein unchecked + assertTrue("Expected " + proteinName + " from the new document to be checked", newProteinCb.isSelected()); // new protein checked + assertFalse("Expected " + proteinName + " from the old document to be unchecked", oldProteinCb.isSelected()); // old protein unchecked } } diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java index 307e41d37..2adcd1849 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java @@ -147,7 +147,7 @@ private void verifyLibraryPrecursors(Map> precursor ListfileNameAndProtein = precursorTable.getRowDataAsText(idx, "File", "Protein / Label"); assertEquals(2, fileNameAndProtein.size()); assertEquals("Unexpected file name for " + precursor, entry.getValue().first, fileNameAndProtein.get(0)); - assertEquals("Unexpected protein name for " + precursor, entry.getValue().second, fileNameAndProtein.get(1)); + assertEquals("Unexpected protein name for " + precursor, entry.getValue().second, fileNameAndProtein.get(1).replace("⁠", "")); // Strip trailing non-breaking space } // After the "Library Precursors" view is modified, its name in the menu appears as "LibraryPrecursors" (no space) so the From fc2c266d7b13b5435eef5427e07fda0a9dcf1c2b Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 10:24:24 -0700 Subject: [PATCH 10/24] Comments --- .../test/tests/targetedms/TargetedMSExperimentTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 7bf183eea..5f2fde48d 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -484,7 +484,7 @@ private void verifyQueries() query.setFilter("Sequence", "Equals", "TNNPETLVALR"); query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); // Strip non-breaking space assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("TNNPETLVALR", query.getDataAsText(0, "Modified Peptide")); assertEquals("K", query.getDataAsText(0, "Next Aa")); @@ -517,7 +517,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Q1 m/z")); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); // Strip non-breaking space assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); @@ -553,7 +553,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(3, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Precursor Id Mz")); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); // Strip non-breaking space assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); From c77d10a8b8006897425db8bc5870b589c8ec3923 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 13:40:29 -0700 Subject: [PATCH 11/24] Switch to centralized method --- .../test/tests/targetedms/TargetedMSExperimentTest.java | 6 +++--- .../labkey/test/tests/targetedms/TargetedMSLibraryTest.java | 5 ++++- .../test/tests/targetedms/TargetedMSPeptideLibraryTest.java | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 5f2fde48d..d4c6a722c 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -484,7 +484,7 @@ private void verifyQueries() query.setFilter("Sequence", "Equals", "TNNPETLVALR"); query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); // Strip non-breaking space + assertEquals("YAL038W", DataRegionTable.stripWordJoiner(query.getDataAsText(0, "Protein / Label"))); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("TNNPETLVALR", query.getDataAsText(0, "Modified Peptide")); assertEquals("K", query.getDataAsText(0, "Next Aa")); @@ -517,7 +517,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Q1 m/z")); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); // Strip non-breaking space + assertEquals("YAL038W", DataRegionTable.stripWordJoiner(query.getDataAsText(0, "Protein / Label"))); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); @@ -553,7 +553,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(3, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Precursor Id Mz")); - assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label").replace("⁠", "")); // Strip non-breaking space + assertEquals("YAL038W", DataRegionTable.stripWordJoiner(query.getDataAsText(0, "Protein / Label"))); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java index 9c971a789..5668bae25 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.stream.IntStream; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -132,7 +133,9 @@ private void verifyLibraryProteins(List proteins, List files, in int i = 0; for(String protein: proteins) { - int idx = proteinsTable.getRowIndex("Label", protein + "⁠"); // Include trailing non-breaking space + int idx = IntStream.range(0, proteinsTable.getDataRowCount()) + .filter(row -> protein.equals(DataRegionTable.stripWordJoiner(proteinsTable.getDataAsText(row, "Label")))) + .findFirst().orElse(-1); assertTrue("Expected protein " + protein + " not found in table", idx != -1); ListfileName = proteinsTable.getRowDataAsText(idx, "RunId/File"); assertEquals(1, fileName.size()); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java index 2adcd1849..ebbb768b7 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java @@ -147,7 +147,7 @@ private void verifyLibraryPrecursors(Map> precursor ListfileNameAndProtein = precursorTable.getRowDataAsText(idx, "File", "Protein / Label"); assertEquals(2, fileNameAndProtein.size()); assertEquals("Unexpected file name for " + precursor, entry.getValue().first, fileNameAndProtein.get(0)); - assertEquals("Unexpected protein name for " + precursor, entry.getValue().second, fileNameAndProtein.get(1).replace("⁠", "")); // Strip trailing non-breaking space + assertEquals("Unexpected protein name for " + precursor, entry.getValue().second, DataRegionTable.stripWordJoiner(fileNameAndProtein.get(1))); } // After the "Library Precursors" view is modified, its name in the menu appears as "LibraryPrecursors" (no space) so the From f5962eeec25849ff9bb404e22e8b4587a4688398 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 16:37:27 -0700 Subject: [PATCH 12/24] Code review followup --- .../labkey/test/tests/targetedms/TargetedMSExperimentTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index d4c6a722c..5a6f3a0f3 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -926,7 +926,7 @@ protected void verifyRunDetailLinks() clickAndWait(Locator.linkContainingText("Targeted MS Runs")); // "44 peptides" → peptide list page: one row per peptide - clickAndWait(Locator.linkContainingText("44")); + clickAndWait(Locator.linkContainingText("44 peptides")); DataRegionTable peptideView = DataRegion(getDriver()).withName("Peptide").find(); assertEquals(44, peptideView.getDataRowCount()); From fbfee5d61baadcc7bcf450414531b414c27cb35a Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 16:45:46 -0700 Subject: [PATCH 13/24] Another test fix --- .../test/tests/targetedms/TargetedMSPeptideLibraryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java index ebbb768b7..50411b474 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java @@ -361,7 +361,7 @@ private List getPeptideGroupsInGrid(DataRegionTable precursorTable) // However, rows with class "labkey-row" have a nested row without a class attribute (TODO: add class attribute to nested rows) // so they don't get counted in DataRegionTable.getDataRows(). // To get the protein names, we have to skip every other nested row. The very first row in the grid has the "labkey-alternate-row" class. - peptideGroups.add(precursorTable.getDataAsText(i, 0)); + peptideGroups.add(DataRegionTable.stripWordJoiner(precursorTable.getDataAsText(i, 0))); } return peptideGroups; } From caae759273028aea85e8a2da0a17cea7ddb9013e Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 22:04:26 -0700 Subject: [PATCH 14/24] Protein vs peptide group --- resources/schemas/targetedms.xml | 8 +++ .../labkey/targetedms/SkylineDocImporter.java | 10 ++- .../targetedms/TargetedMSController.java | 55 +++++++++++------ .../labkey/targetedms/TargetedMSManager.java | 10 +++ .../labkey/targetedms/TargetedMSModule.java | 2 +- src/org/labkey/targetedms/TargetedMSRun.java | 22 +++++++ .../labkey/targetedms/TargetedMSSchema.java | 61 +++++++++++++++++-- .../labkey/targetedms/view/runSummaryView.jsp | 14 +++-- .../targetedms/TargetedMSExperimentTest.java | 4 +- .../tests/targetedms/TargetedMSListTest.java | 4 +- .../tests/targetedms/TargetedMSMAMTest.java | 4 +- .../test/tests/targetedms/TargetedMSTest.java | 24 +++++--- 12 files changed, 178 insertions(+), 40 deletions(-) diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml index 2b476325d..b864b0d66 100644 --- a/resources/schemas/targetedms.xml +++ b/resources/schemas/targetedms.xml @@ -178,6 +178,14 @@ + #,### + /targetedms-showPeptideGroupList.view?id=${Id} + + + #,### + /targetedms-showMoleculeGroupList.view?id=${Id} + + #,### /targetedms-showProteinList.view?id=${Id} diff --git a/src/org/labkey/targetedms/SkylineDocImporter.java b/src/org/labkey/targetedms/SkylineDocImporter.java index 8a1e1afd5..6a971d237 100644 --- a/src/org/labkey/targetedms/SkylineDocImporter.java +++ b/src/org/labkey/targetedms/SkylineDocImporter.java @@ -144,6 +144,9 @@ public class SkylineDocImporter protected static final Logger _systemLog = LogHelper.getLogger(SkylineDocImporter.class, "Imports Skyline documents"); protected final XarContext _context; private int blankLabelIndex; + private int _importedPeptideGroupCount = 0; + private int _importedMoleculeGroupCount = 0; + private int _importedProteinCount = 0; private final LocalDirectory _localDirectory; private final PipeRoot _pipeRoot; @@ -450,7 +453,9 @@ private void importSkylineDoc(TargetedMSRun run, File f) throws XMLStreamExcepti int auditLogEntriesCount = importer.importAuditLogFile(_user, _auditLogFile, parser.getDocumentGUID(), run); run.setAuditLogEntriesCount(auditLogEntriesCount); - run.setPeptideGroupCount(parser.getPeptideGroupCount()); + run.setPeptideGroupCount(_importedPeptideGroupCount); + run.setMoleculeGroupCount(_importedMoleculeGroupCount); + run.setProteinCount(_importedProteinCount); run.setPeptideCount(parser.getPeptideCount()); run.setSmallMoleculeCount(parser.getSmallMoleculeCount()); run.setPrecursorCount(parser.getPrecursorCount()); @@ -1357,10 +1362,12 @@ else if(!pepGroup.isDecoy()) if(peptideCount > 0) { _log.debug(String.format("Total peptides inserted: %d", peptideCount)); + _importedPeptideGroupCount++; } if(moleculeCount > 0) { _log.debug(String.format("Total molecules inserted: %d", moleculeCount)); + _importedMoleculeGroupCount++; } } @@ -1381,6 +1388,7 @@ private void insertProtein(Protein protein) proteinService.ensureIdentifiers(seqId, identifierMap); } Table.insert(null, TargetedMSManager.getTableInfoProtein(), protein); + _importedProteinCount++; } private static final String SKYLINE_IDENT_TYPE = "Skyline"; diff --git a/src/org/labkey/targetedms/TargetedMSController.java b/src/org/labkey/targetedms/TargetedMSController.java index 29470372e..b615bba52 100644 --- a/src/org/labkey/targetedms/TargetedMSController.java +++ b/src/org/labkey/targetedms/TargetedMSController.java @@ -260,6 +260,7 @@ import org.labkey.targetedms.view.ChromatogramsDataRegion; import org.labkey.targetedms.view.DocumentGeneralMoleculesView; import org.labkey.targetedms.view.DocumentPeptideGroupView; +import org.labkey.targetedms.view.DocumentProteinView; import org.labkey.targetedms.view.DocumentPrecursorsView; import org.labkey.targetedms.view.DocumentTransitionsView; import org.labkey.targetedms.view.FiguresOfMeritView; @@ -4539,40 +4540,60 @@ public String getDataRegionNameSmallMolecule() } @RequiresPermission(ReadPermission.class) - public class ShowProteinListAction extends ShowRunSplitDetailsAction + public class ShowPeptideGroupListAction extends ShowRunSingleDetailsAction { + public ShowPeptideGroupListAction() + { + super(RunDetailsForm.class, "Peptide Groups", TargetedMSSchema.TABLE_PEPTIDE_GROUP); + } + @Override protected QueryView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) { TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); - QueryView view; + DocumentPeptideGroupView view = new DocumentPeptideGroupView(getViewContext(), schema, form.getId(), + TargetedMSSchema.TABLE_PEPTIDE_GROUP, "Peptide Groups"); + view.setShowDetailsColumn(false); + view.setShowFilterDescription(false); + return view; + } + } - if (TargetedMSSchema.TABLE_PEPTIDE_GROUP.equalsIgnoreCase(dataRegion)) - { - view = new DocumentPeptideGroupView(getViewContext(), schema, form.getId(), - TargetedMSSchema.TABLE_PEPTIDE_GROUP, "Proteins"); - } - else - { - view = new DocumentPeptideGroupView(getViewContext(), schema, form.getId(), - TargetedMSSchema.TABLE_MOLECULE_GROUP, "Molecule Lists"); - } + @RequiresPermission(ReadPermission.class) + public class ShowMoleculeGroupListAction extends ShowRunSingleDetailsAction + { + public ShowMoleculeGroupListAction() + { + super(RunDetailsForm.class, "Molecule Lists", TargetedMSSchema.TABLE_MOLECULE_GROUP); + } + @Override + protected QueryView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) + { + TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); + DocumentPeptideGroupView view = new DocumentPeptideGroupView(getViewContext(), schema, form.getId(), + TargetedMSSchema.TABLE_MOLECULE_GROUP, "Molecule Lists"); view.setShowDetailsColumn(false); view.setShowFilterDescription(false); return view; } + } - @Override - public String getDataRegionNamePeptide() + @RequiresPermission(ReadPermission.class) + public class ShowProteinListAction extends ShowRunSingleDetailsAction + { + public ShowProteinListAction() { - return TargetedMSSchema.TABLE_PEPTIDE_GROUP; + super(RunDetailsForm.class, "Proteins", TargetedMSSchema.TABLE_PROTEIN); } @Override - public String getDataRegionNameSmallMolecule() + protected QueryView createQueryView(RunDetailsForm form, BindException errors, boolean forExport, String dataRegion) { - return TargetedMSSchema.TABLE_MOLECULE_GROUP; + TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); + DocumentProteinView view = new DocumentProteinView(getViewContext(), schema, form.getId()); + view.setShowDetailsColumn(false); + return view; } } diff --git a/src/org/labkey/targetedms/TargetedMSManager.java b/src/org/labkey/targetedms/TargetedMSManager.java index c9e643dde..f52fa3b62 100644 --- a/src/org/labkey/targetedms/TargetedMSManager.java +++ b/src/org/labkey/targetedms/TargetedMSManager.java @@ -2660,6 +2660,16 @@ public static boolean containerHasPeptides(Container container) return new SqlSelector(TargetedMSManager.getSchema(), new SQLFragment("SELECT Id FROM ", container, false).append(TargetedMSManager.getTableInfoRuns(), "r").append(" WHERE PeptideCount > 0 AND Container = ? AND Deleted = ?")).exists(); } + public static boolean containerHasMoleculeGroups(Container container) + { + return new SqlSelector(TargetedMSManager.getSchema(), new SQLFragment("SELECT Id FROM ", container, false).append(TargetedMSManager.getTableInfoRuns(), "r").append(" WHERE MoleculeGroupCount > 0 AND Container = ? AND Deleted = ?")).exists(); + } + + public static boolean containerHasProteins(Container container) + { + return new SqlSelector(TargetedMSManager.getSchema(), new SQLFragment("SELECT Id FROM ", container, false).append(TargetedMSManager.getTableInfoRuns(), "r").append(" WHERE ProteinCount > 0 AND Container = ? AND Deleted = ?")).exists(); + } + public static boolean containerHasCalibrationCurves(Container container) { return new SqlSelector(TargetedMSManager.getSchema(), new SQLFragment("SELECT Id FROM ", container, false).append(TargetedMSManager.getTableInfoRuns(), "r").append(" WHERE CalibrationCurveCount > 0 AND Container = ? AND Deleted = ?")).exists(); diff --git a/src/org/labkey/targetedms/TargetedMSModule.java b/src/org/labkey/targetedms/TargetedMSModule.java index 581f67f52..b3bf9a4a7 100644 --- a/src/org/labkey/targetedms/TargetedMSModule.java +++ b/src/org/labkey/targetedms/TargetedMSModule.java @@ -231,7 +231,7 @@ public String getName() @Override public Double getSchemaVersion() { - return 26.006; + return 26.007; } @Override diff --git a/src/org/labkey/targetedms/TargetedMSRun.java b/src/org/labkey/targetedms/TargetedMSRun.java index a1319b6ec..47c64c56e 100644 --- a/src/org/labkey/targetedms/TargetedMSRun.java +++ b/src/org/labkey/targetedms/TargetedMSRun.java @@ -51,6 +51,8 @@ public class TargetedMSRun implements Serializable, ITargetedMSRun protected RunRepresentativeDataState _representativeDataState = NotRepresentative; protected int _peptideGroupCount; + protected int _moleculeGroupCount; + protected int _proteinCount; protected int _peptideCount; protected int _smallMoleculeCount; protected int _precursorCount; @@ -249,6 +251,26 @@ public void setPeptideGroupCount(int peptideGroupCount) _peptideGroupCount = peptideGroupCount; } + public int getMoleculeGroupCount() + { + return _moleculeGroupCount; + } + + public void setMoleculeGroupCount(int moleculeGroupCount) + { + _moleculeGroupCount = moleculeGroupCount; + } + + public int getProteinCount() + { + return _proteinCount; + } + + public void setProteinCount(int proteinCount) + { + _proteinCount = proteinCount; + } + public int getPeptideCount() { return _peptideCount; diff --git a/src/org/labkey/targetedms/TargetedMSSchema.java b/src/org/labkey/targetedms/TargetedMSSchema.java index 312caebc2..f9a6371f2 100644 --- a/src/org/labkey/targetedms/TargetedMSSchema.java +++ b/src/org/labkey/targetedms/TargetedMSSchema.java @@ -305,6 +305,8 @@ public class TargetedMSSchema extends UserSchema // Cache this info at the schema level to make it easy to reuse private Boolean _hasSmallMolecules; private Boolean _hasPeptides; + private Boolean _hasMoleculeGroups; + private Boolean _hasProteins; static public void register(Module module) { @@ -788,7 +790,8 @@ public ExpRunTable getTargetedMSRunsTable(ContainerFilter cf) boolean hasSmallMolecules = hasSmallMolecules(); boolean hasPeptides = hasPeptides(); - String peptideGroupColName = (hasSmallMolecules ? COL_LIST : COL_PROTEIN) + "s"; + boolean hasMoleculeGroups = hasMoleculeGroups(); + boolean hasProteins = hasProteins(); skyDocDetailColumn.setFk(new LookupForeignKey(url, "id", "Id", "Description") { @@ -867,7 +870,9 @@ public boolean isSortable() } }); - result.addWrapColumn(peptideGroupColName, result.getRealTable().getColumn("PeptideGroupCount")); + result.addWrapColumn("PeptideGroups", result.getRealTable().getColumn("PeptideGroupCount")); + result.addWrapColumn("MoleculeLists", result.getRealTable().getColumn("MoleculeGroupCount")); + result.addWrapColumn("Proteins", result.getRealTable().getColumn("ProteinCount")); result.addWrapColumn("Peptides", result.getRealTable().getColumn("PeptideCount")); result.addWrapColumn("SmallMolecules", result.getRealTable().getColumn("SmallMoleculeCount")); result.addWrapColumn("Precursors", result.getRealTable().getColumn("PrecursorCount")); @@ -1003,7 +1008,13 @@ private List init() _fieldKeys.add(2, FieldKey.fromParts("File")); _fieldKeys.add(3, FieldKey.fromParts("File", "Download")); - _fieldKeys.add(FieldKey.fromParts("File", peptideGroupColName)); + + if (hasPeptides) + _fieldKeys.add(FieldKey.fromParts("File", "PeptideGroups")); + if (hasMoleculeGroups) + _fieldKeys.add(FieldKey.fromParts("File", "MoleculeLists")); + if (hasProteins) + _fieldKeys.add(FieldKey.fromParts("File", "Proteins")); // Omit peptides or small molecules if we don't have any in this container if (hasPeptides || !hasSmallMolecules) @@ -1068,11 +1079,29 @@ private boolean hasPeptides() { if (_hasPeptides == null) { - _hasPeptides = TargetedMSManager.containerHasSmallMolecules(getContainer()); + _hasPeptides = TargetedMSManager.containerHasPeptides(getContainer()); } return _hasPeptides.booleanValue(); } + private boolean hasMoleculeGroups() + { + if (_hasMoleculeGroups == null) + { + _hasMoleculeGroups = TargetedMSManager.containerHasMoleculeGroups(getContainer()); + } + return _hasMoleculeGroups.booleanValue(); + } + + private boolean hasProteins() + { + if (_hasProteins == null) + { + _hasProteins = TargetedMSManager.containerHasProteins(getContainer()); + } + return _hasProteins.booleanValue(); + } + @Override public TableInfo createTable(String name, ContainerFilter cf) { @@ -1246,6 +1275,30 @@ public TableInfo createTable(String name, ContainerFilter cf) FieldKey.fromParts("Description") )); + var labelColumn = result.getMutableColumnOrThrow("Label"); + labelColumn.setURL(new DetailsURL(new ActionURL(TargetedMSController.ShowProteinAction.class, getContainer()), + Collections.singletonMap("id", FieldKey.fromParts("PeptideGroupId")))); + FieldKey containerFieldKey = result.getContainerFieldKey(); + labelColumn.setDisplayColumnFactory(colInfo -> { + Map params = new HashMap<>(); + params.put("id", new FieldKey(colInfo.getFieldKey().getParent(), "PeptideGroupId")); + JSONObject props = new JSONObject(); + props.put("width", 450); + props.put("title", "Protein Details"); + return new AJAXDetailsDisplayColumn(colInfo, new ActionURL(TargetedMSController.ShowProteinAJAXAction.class, getContainer()), params, props, containerFieldKey) + { + @Override + public @NotNull Set getClientDependencies() + { + Set deps = super.getClientDependencies(); + deps.add(ClientDependency.fromPath("protein/ProteinCoverageMap.css")); + deps.add(ClientDependency.fromPath("protein/ProteinCoverageMap.js")); + deps.add(ClientDependency.fromPath("util.js")); + return deps; + } + }; + }); + return result; } if (TABLE_PEPTIDE_GROUP_ANNOTATION.equalsIgnoreCase(name)) diff --git a/src/org/labkey/targetedms/view/runSummaryView.jsp b/src/org/labkey/targetedms/view/runSummaryView.jsp index d6ee40a6f..cd3674186 100644 --- a/src/org/labkey/targetedms/view/runSummaryView.jsp +++ b/src/org/labkey/targetedms/view/runSummaryView.jsp @@ -24,9 +24,7 @@ <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.targetedms.TargetedMSController" %> -<%@ page import="org.labkey.targetedms.TargetedMSManager" %> <%@ page import="org.labkey.targetedms.TargetedMSRun" %> -<%@ page import="org.labkey.targetedms.TargetedMSSchema" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @@ -52,6 +50,12 @@ ActionURL precursorListAction = new ActionURL(TargetedMSController.ShowPrecursorListAction.class, getContainer()); precursorListAction.addParameter("id", run.getId()); + ActionURL peptideGroupListAction = new ActionURL(TargetedMSController.ShowPeptideGroupListAction.class, getContainer()); + peptideGroupListAction.addParameter("id", run.getId()); + + ActionURL moleculeGroupListAction = new ActionURL(TargetedMSController.ShowMoleculeGroupListAction.class, getContainer()); + moleculeGroupListAction.addParameter("id", run.getId()); + ActionURL proteinListAction = new ActionURL(TargetedMSController.ShowProteinListAction.class, getContainer()); proteinListAction.addParameter("id", run.getId()); @@ -89,7 +93,6 @@ if(c.hasPermission(getUser(), UpdatePermission.class)) renameAction = TargetedMSController.getRenameRunURL(c, run, getActionURL()); - String peptideGroupLabel = TargetedMSManager.containerHasSmallMolecules(getContainer()) ? TargetedMSSchema.COL_LIST.toLowerCase() : TargetedMSSchema.COL_PROTEIN.toLowerCase(); %> @@ -103,7 +106,10 @@
 
- <%= h(StringUtilsLabKey.pluralize(run.getPeptideGroupCount(), peptideGroupLabel))%>, + <% if (run.getPeptideGroupCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getPeptideGroupCount(), "peptide group"))%><% + if (run.getProteinCount() > 0) { %> (with <%= h(StringUtilsLabKey.pluralize(run.getProteinCount(), "protein"))%>)<% } %>, + <% } %> + <% if (run.getMoleculeGroupCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getMoleculeGroupCount(), "molecule list"))%>,<% } %> <% if (run.getPeptideCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getPeptideCount(), "peptide"))%>,<% } %> <% if (run.getSmallMoleculeCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getSmallMoleculeCount(), "small molecule"))%>,<% } %> <%= h(StringUtilsLabKey.pluralize(run.getPrecursorCount(), "precursor"))%>, diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 5a6f3a0f3..92d77c301 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -166,7 +166,7 @@ protected void verifyImportedPeptideData() { clickAndWait(Locator.linkContainingText("Panorama Dashboard")); clickAndWait(Locator.linkContainingText(SKY_FILE)); - verifyRunSummaryCountsPep(24,44,0, 88,296, 1, 0, 0); + verifyRunSummaryCountsPep(24, 24, 44, 0, 88, 296, 1, 0, 0); verifyDocumentDetails(false); verifyPeptide(); } @@ -176,7 +176,7 @@ protected void verifyImportedSmallMoleculeData() { clickAndWait(Locator.linkContainingText("Panorama Dashboard")); clickAndWait(Locator.linkContainingText(SKY_FILE_SMALLMOL_PEP)); - verifyRunSummaryCountsSmallMol(27, 44, 98, 186, 394, 5, 0, 0); // Number of protein (groups), peptides, precursors, transitions, small molecules + verifyRunSummaryCountsMixed(24, 3, 24, 44, 98, 186, 394, 5, 0, 0); verifyDocumentDetails(true); verifyMolecule(); } diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSListTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSListTest.java index 8ef5ee315..61c180dcd 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSListTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSListTest.java @@ -53,13 +53,13 @@ public void testSteps() throws IOException, CommandException clickAndWait(Locator.linkContainingText("Panorama Dashboard")); clickAndWait(Locator.linkContainingText(LIST_SKY_FILE_1)); - verifyRunSummaryCountsPep(2,4,0, 5,53, 1, 0, 6); + verifyRunSummaryCountsPep(2, 2, 4, 0, 5, 53, 1, 0, 6); clickAndWait(Locator.linkContainingText("6 lists")); assertTextPresent("DocumentProperties", "Lorem Ipsum", "Protein Descriptions"); clickAndWait(Locator.linkContainingText("Protein Descriptions")); // Check that the document header remains - verifyRunSummaryCountsPep(2,4,0, 5,53, 1, 0, 6); + verifyRunSummaryCountsPep(2, 2, 4, 0, 5, 53, 1, 0, 6); assertTextPresent("ALBU_BOVIN", "main protein of plasma"); List queryNames = validateSampleInfo(1, Set.of("Mickey"), 5, 12); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSMAMTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSMAMTest.java index 4b9beb6b1..5b8aa5fec 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSMAMTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSMAMTest.java @@ -50,7 +50,7 @@ public void testSteps() clickAndWait(Locator.linkContainingText("Panorama Dashboard")); clickAndWait(Locator.linkContainingText(SKY_FILE)); - verifyRunSummaryCountsPep(125,158,0, 160,628, 1, 0, 0); + verifyRunSummaryCountsPep(125, 124, 158, 0, 160, 628, 1, 0, 0); clickAndWait(Locator.linkContainingText("PTM Report")); @@ -83,7 +83,7 @@ public void testCrossLinkedPeptideMap() clickAndWait(Locator.linkContainingText("Panorama Dashboard")); clickAndWait(Locator.linkContainingText(CROSS_LINKED_SKY_FILE)); - verifyRunSummaryCountsPep(2,2,0, 2,2, 1, 0, 0); + verifyRunSummaryCountsPep(2, 2, 2, 0, 2, 2, 1, 0, 0); clickAndWait(Locator.linkContainingText("Peptide Map")); assertTextPresentInThisOrder("121-124", "342-345", "142-145"); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java index 2b1bf0650..40c6ea0e3 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java @@ -273,22 +273,32 @@ private void runDbMaintenance() } } - protected void verifyRunSummaryCountsSmallMol(int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) + protected void verifyRunSummaryCountsSmallMol(int moleculeGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) { - verifyRunSummaryCounts(proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount, "molecule lists"); + verifyRunSummaryCounts(0, moleculeGroupCount, proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount); } - protected void verifyRunSummaryCountsPep(int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) + protected void verifyRunSummaryCountsPep(int peptideGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) { - verifyRunSummaryCounts(proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount, "proteins"); + verifyRunSummaryCounts(peptideGroupCount, 0, proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount); + } + + protected void verifyRunSummaryCountsMixed(int peptideGroupCount, int moleculeGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) + { + verifyRunSummaryCounts(peptideGroupCount, moleculeGroupCount, proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount); } @LogMethod - protected void verifyRunSummaryCounts(int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, - int replicateCount, int calibrationCount, int listCount, String peptideGroupLabel) + protected void verifyRunSummaryCounts(int peptideGroupCount, int moleculeGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, + int replicateCount, int calibrationCount, int listCount) { log("Verifying expected summary counts"); - waitForElement(Locator.linkContainingText(proteinCount + " " + peptideGroupLabel)); + if (peptideGroupCount > 0) + waitForElement(Locator.linkContainingText(peptideGroupCount + " peptide group" + (peptideGroupCount == 1 ? "" : "s"))); + if (moleculeGroupCount > 0) + assertElementPresent(Locator.linkContainingText(moleculeGroupCount + " molecule list" + (moleculeGroupCount == 1 ? "" : "s"))); + if (proteinCount > 0) + assertElementPresent(Locator.linkContainingText(proteinCount + " protein" + (proteinCount == 1 ? "" : "s"))); if (peptideCount > 0) { assertElementPresent(Locator.linkContainingText(peptideCount + " peptides")); From e9caa2f20a7088b944a31c67ab9aedde894a446d Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 22:29:37 -0700 Subject: [PATCH 15/24] Missing files --- .../postgresql/targetedms-26.006-26.007.sql | 30 ++++++++++++ .../targetedms/view/DocumentProteinView.java | 47 +++++++++++++++++++ .../targetedms/TargetedMSExperimentTest.java | 5 +- 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql create mode 100644 src/org/labkey/targetedms/view/DocumentProteinView.java 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..813b45d02 --- /dev/null +++ b/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql @@ -0,0 +1,30 @@ +ALTER TABLE targetedms.Runs ADD COLUMN ProteinCount INT DEFAULT 0; + +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 NOT NULL DEFAULT 0; + +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 +); diff --git a/src/org/labkey/targetedms/view/DocumentProteinView.java b/src/org/labkey/targetedms/view/DocumentProteinView.java new file mode 100644 index 000000000..aa17e2cc7 --- /dev/null +++ b/src/org/labkey/targetedms/view/DocumentProteinView.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.targetedms.view; + +import org.labkey.api.data.CompareType; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryView; +import org.labkey.api.view.ViewContext; +import org.labkey.targetedms.TargetedMSSchema; +import org.labkey.targetedms.query.TargetedMSTable; + +public class DocumentProteinView extends QueryView +{ + private final TargetedMSSchema _schema; + private final long _runId; + + public DocumentProteinView(ViewContext ctx, TargetedMSSchema schema, long runId) + { + super(schema, schema.getSettings(ctx, TargetedMSSchema.TABLE_PROTEIN, TargetedMSSchema.TABLE_PROTEIN), null); + _schema = schema; + _runId = runId; + setTitle("Proteins"); + } + + @Override + public TableInfo createTable() + { + TargetedMSTable tinfo = (TargetedMSTable) _schema.getTable(TargetedMSSchema.TABLE_PROTEIN, null, true, true); + tinfo.addContainerTableFilter(new CompareType.EqualsCompareClause(FieldKey.fromParts("Id"), CompareType.EQUAL, _runId)); + tinfo.setLocked(true); + return tinfo; + } +} diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 92d77c301..6d8195abd 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -915,10 +915,11 @@ protected void verifyRunDetailLinks() // Test each header link from runSummaryView.jsp while on the default run view. // Each link must reach its own dedicated list page, not the precursor list. - // "27 molecule lists" → protein/molecule-list page: 24 protein groups + 3 molecule groups - clickAndWait(Locator.linkContainingText("27 molecule lists")); + // 24 protein groups + 3 molecule groups + clickAndWait(Locator.linkContainingText("24 peptide groups")); DataRegionTable peptideGroupView = DataRegion(getDriver()).withName("PeptideGroup").find(); assertEquals(24, peptideGroupView.getDataRowCount()); + clickAndWait(Locator.linkContainingText("3 molecule lists")); DataRegionTable moleculeGroupView = DataRegion(getDriver()).withName("MoleculeGroup").find(); assertEquals(3, moleculeGroupView.getDataRowCount()); From 1f0325bf923e0053696a8828d62b81b9e7e9e026 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 22:31:36 -0700 Subject: [PATCH 16/24] Better comments --- .../targetedms/TargetedMSExperimentTest.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 6d8195abd..6598027d6 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -913,19 +913,18 @@ protected void verifyRunDetailLinks() assertEquals(5, smallMolPrecursorsView.getDataRowCount()); // Test each header link from runSummaryView.jsp while on the default run view. - // Each link must reach its own dedicated list page, not the precursor list. + // Each link must reach its own dedicated list page, not the combined precursor list. - // 24 protein groups + 3 molecule groups + // "24 peptide groups" → peptide group list: one row per peptide-bearing group clickAndWait(Locator.linkContainingText("24 peptide groups")); DataRegionTable peptideGroupView = DataRegion(getDriver()).withName("PeptideGroup").find(); assertEquals(24, peptideGroupView.getDataRowCount()); + + // "3 molecule lists" → molecule group list: one row per molecule-bearing group clickAndWait(Locator.linkContainingText("3 molecule lists")); DataRegionTable moleculeGroupView = DataRegion(getDriver()).withName("MoleculeGroup").find(); assertEquals(3, moleculeGroupView.getDataRowCount()); - // Navigate back to the run's precursor list via the breadcrumb. - clickAndWait(Locator.linkContainingText("Targeted MS Runs")); - // "44 peptides" → peptide list page: one row per peptide clickAndWait(Locator.linkContainingText("44 peptides")); DataRegionTable peptideView = DataRegion(getDriver()).withName("Peptide").find(); @@ -938,13 +937,11 @@ protected void verifyRunDetailLinks() // Verify the runSummaryView.jsp header links also work when on a non-default run detail page. // The header is embedded on every run detail view, not just the precursor list. - // From the small molecule list page, click "27 molecule lists". - clickAndWait(Locator.linkContainingText("27 molecule lists")); + // From the small molecule list page, click "24 peptide groups". + clickAndWait(Locator.linkContainingText("24 peptide groups")); peptideGroupView = DataRegion(getDriver()).withName("PeptideGroup").find(); assertEquals(24, peptideGroupView.getDataRowCount()); - moleculeGroupView = DataRegion(getDriver()).withName("MoleculeGroup").find(); - assertEquals(3, moleculeGroupView.getDataRowCount()); - // From the protein/molecule-list page, click "44 peptides". + // From the peptide group list page, click "44 peptides". clickAndWait(Locator.linkContainingText("44 peptides")); peptideView = DataRegion(getDriver()).withName("Peptide").find(); assertEquals(44, peptideView.getDataRowCount()); From d7333f79f0971e8fa81e657ef444bc5f7c35c312 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 22:59:47 -0700 Subject: [PATCH 17/24] Self-review improvements --- .../dbscripts/postgresql/targetedms-26.006-26.007.sql | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql b/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql index 813b45d02..73c90cbd7 100644 --- a/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql +++ b/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql @@ -1,4 +1,4 @@ -ALTER TABLE targetedms.Runs ADD COLUMN ProteinCount INT DEFAULT 0; +ALTER TABLE targetedms.Runs ADD COLUMN ProteinCount INT; UPDATE targetedms.Runs r SET ProteinCount = ( @@ -18,7 +18,7 @@ SET PeptideGroupCount = ( WHERE pg.RunId = r.Id ); -ALTER TABLE targetedms.Runs ADD COLUMN MoleculeGroupCount INT NOT NULL DEFAULT 0; +ALTER TABLE targetedms.Runs ADD COLUMN MoleculeGroupCount INT; UPDATE targetedms.Runs r SET MoleculeGroupCount = ( @@ -28,3 +28,6 @@ SET MoleculeGroupCount = ( 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 ProteinCount SET NOT NULL; From 3b7f78f0f29164254e8da78c74aabfaaff226946 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Sun, 17 May 2026 23:06:14 -0700 Subject: [PATCH 18/24] Simplify after refactor --- .../test/tests/targetedms/TargetedMSExperimentTest.java | 6 +++--- .../labkey/test/tests/targetedms/TargetedMSLibraryTest.java | 2 +- .../test/tests/targetedms/TargetedMSPeptideLibraryTest.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 6598027d6..9c27c9da3 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -484,7 +484,7 @@ private void verifyQueries() query.setFilter("Sequence", "Equals", "TNNPETLVALR"); query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); - assertEquals("YAL038W", DataRegionTable.stripWordJoiner(query.getDataAsText(0, "Protein / Label"))); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label")); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("TNNPETLVALR", query.getDataAsText(0, "Modified Peptide")); assertEquals("K", query.getDataAsText(0, "Next Aa")); @@ -517,7 +517,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(1, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Q1 m/z")); - assertEquals("YAL038W", DataRegionTable.stripWordJoiner(query.getDataAsText(0, "Protein / Label"))); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label")); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); @@ -553,7 +553,7 @@ private void verifyQueries() query = new DataRegionTable("query", this); assertEquals(3, query.getDataRowCount()); assertEquals("677.8818", query.getDataAsText(0, "Precursor Id Mz")); - assertEquals("YAL038W", DataRegionTable.stripWordJoiner(query.getDataAsText(0, "Protein / Label"))); + assertEquals("YAL038W", query.getDataAsText(0, "Protein / Label")); assertElementPresent(Locator.linkWithText("YAL038W")); assertEquals("LTSLNVVAGSDLR", query.getDataAsText(0, "Peptide")); assertEquals("1343.7409", query.getDataAsText(0, "Peptide Neutral Mass")); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java index 5668bae25..0ede73415 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSLibraryTest.java @@ -134,7 +134,7 @@ private void verifyLibraryProteins(List proteins, List files, in for(String protein: proteins) { int idx = IntStream.range(0, proteinsTable.getDataRowCount()) - .filter(row -> protein.equals(DataRegionTable.stripWordJoiner(proteinsTable.getDataAsText(row, "Label")))) + .filter(row -> protein.equals(proteinsTable.getDataAsText(row, "Label"))) .findFirst().orElse(-1); assertTrue("Expected protein " + protein + " not found in table", idx != -1); ListfileName = proteinsTable.getRowDataAsText(idx, "RunId/File"); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java index 50411b474..307e41d37 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSPeptideLibraryTest.java @@ -147,7 +147,7 @@ private void verifyLibraryPrecursors(Map> precursor ListfileNameAndProtein = precursorTable.getRowDataAsText(idx, "File", "Protein / Label"); assertEquals(2, fileNameAndProtein.size()); assertEquals("Unexpected file name for " + precursor, entry.getValue().first, fileNameAndProtein.get(0)); - assertEquals("Unexpected protein name for " + precursor, entry.getValue().second, DataRegionTable.stripWordJoiner(fileNameAndProtein.get(1))); + assertEquals("Unexpected protein name for " + precursor, entry.getValue().second, fileNameAndProtein.get(1)); } // After the "Library Precursors" view is modified, its name in the menu appears as "LibraryPrecursors" (no space) so the @@ -361,7 +361,7 @@ private List getPeptideGroupsInGrid(DataRegionTable precursorTable) // However, rows with class "labkey-row" have a nested row without a class attribute (TODO: add class attribute to nested rows) // so they don't get counted in DataRegionTable.getDataRows(). // To get the protein names, we have to skip every other nested row. The very first row in the grid has the "labkey-alternate-row" class. - peptideGroups.add(DataRegionTable.stripWordJoiner(precursorTable.getDataAsText(i, 0))); + peptideGroups.add(precursorTable.getDataAsText(i, 0)); } return peptideGroups; } From 96371fe889ddead29ce58a3595824c5ba7494196 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Mon, 18 May 2026 09:33:10 -0700 Subject: [PATCH 19/24] Extract TargetedMSHelper and add upgrade test for 26.3 schema migration Extracts setupFolder(), selectFolderType(), and importData() from TargetedMSTest into a new TargetedMSHelper class so the logic can be shared without subclassing. Adds TargetedMSUpgradeTest to verify the targetedms-26.006-26.007 migration correctly populates PeptideGroupCount, MoleculeGroupCount, and ProteinCount on existing runs. Co-Authored-By: Claude Sonnet 4.6 --- .../test/tests/targetedms/TargetedMSTest.java | 41 ++++------- .../upgrade/TargetedMSUpgradeTest.java | 71 +++++++++++++++++++ .../util/targetedms/TargetedMSHelper.java | 68 ++++++++++++++++++ 3 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java create mode 100644 test/src/org/labkey/test/util/targetedms/TargetedMSHelper.java diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java index 40c6ea0e3..eacfceea0 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java @@ -19,6 +19,7 @@ import org.junit.BeforeClass; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; +import org.labkey.test.WebDriverWrapper; import org.labkey.test.ModulePropertyValue; import org.labkey.test.TestFileUtils; import org.labkey.test.TestProperties; @@ -37,9 +38,8 @@ import org.labkey.test.util.LoggedParam; import org.labkey.test.util.ReflectionUtils; import org.labkey.test.util.UIContainerHelper; -import org.openqa.selenium.WebElement; +import org.labkey.test.util.targetedms.TargetedMSHelper; -import java.nio.file.Paths; import java.util.Arrays; import java.util.List; @@ -62,6 +62,7 @@ public abstract class TargetedMSTest extends BaseWebDriverTest protected static final String SAMPLE_FILE_CHROM_INFO = "SampleFileChromInfo.sky.zip"; protected static final String USER = "qcuser@targetedms.test"; private static ConfiguresSite siteConfigurer; + protected final TargetedMSHelper _targetedMSHelper = new TargetedMSHelper(this); protected enum SvgShapes { @@ -85,7 +86,7 @@ public enum FolderType { Experiment { @Override - public void chooseFolderType(TargetedMSTest test) + public void chooseFolderType(WebDriverWrapper test) { test.click(Locator.radioButtonById("experimentalData")); // click the first radio button - Experimental Data } @@ -93,21 +94,21 @@ public void chooseFolderType(TargetedMSTest test) ExperimentMAM { @Override - public void chooseFolderType(TargetedMSTest test) + public void chooseFolderType(WebDriverWrapper test) { test.click(Locator.radioButtonById("multiAttributeMethod")); // click the second radio button - Experimental Data } }, Library { @Override - public void chooseFolderType(TargetedMSTest test) + public void chooseFolderType(WebDriverWrapper test) { test.click(Locator.radioButtonById("chromatogramLibrary")); // click the 3rd radio button - Library } }, LibraryProtein { @Override - public void chooseFolderType(TargetedMSTest test) + public void chooseFolderType(WebDriverWrapper test) { test.click(Locator.radioButtonById("chromatogramLibrary")); // click the 3rd radio button - Library test.click(Locator.checkboxByName("precursorNormalized")); // check the normalization checkbox. @@ -115,13 +116,13 @@ public void chooseFolderType(TargetedMSTest test) }, QC { @Override - public void chooseFolderType(TargetedMSTest test) + public void chooseFolderType(WebDriverWrapper test) { test.click(Locator.radioButtonById("QC")); // click the 4th radio button - QC } }; - public abstract void chooseFolderType(TargetedMSTest test); + public abstract void chooseFolderType(WebDriverWrapper test); } public TargetedMSTest() @@ -181,10 +182,7 @@ protected void setupFolder(FolderType folderType) protected void setUpFolder(String folderName, FolderType folderType ) { - _containerHelper.createProject(folderName, "Panorama"); - waitForElement(Locator.linkContainingText("Save")); - clickAndWait(Locator.linkContainingText("Next")); - selectFolderType(folderType); + _targetedMSHelper.setupFolder(folderName, folderType); getSiteConfigurer().configureProject(getProjectName()); } @@ -227,20 +225,7 @@ protected void importData(@LoggedParam String file, int jobCount, boolean expect @LogMethod protected void importData(@LoggedParam String file, int jobCount, boolean expectError, boolean doDbMaintenance) { - Locator.XPathLocator importButtonLoc = Locator.lkButton("Process and Import Data"); - WebElement importButton = importButtonLoc.findElementOrNull(getDriver()); - if (null == importButton) - { - goToModule("Pipeline"); - importButton = importButtonLoc.findElement(getDriver()); - } - clickAndWait(importButton); - String fileName = Paths.get(file).getFileName().toString(); - if (!_fileBrowserHelper.fileIsPresent(fileName)) - _fileBrowserHelper.uploadFile(TestFileUtils.getSampleData("TargetedMS/" + file)); - _fileBrowserHelper.importFile(fileName, "Import Skyline Results"); - waitForText("Skyline document import"); - waitForPipelineJobsToComplete(jobCount, file, expectError); + _targetedMSHelper.importData(file, jobCount, expectError); if (doDbMaintenance) { @@ -337,9 +322,7 @@ protected void verifyRunSummaryCounts(int peptideGroupCount, int moleculeGroupCo @LogMethod protected void selectFolderType(FolderType folderType) { - log("Select Folder Type: " + folderType); - folderType.chooseFolderType(this); - clickButton("Finish"); + _targetedMSHelper.selectFolderType(folderType); } /** Verify that the comparison plots have been AJAX'd into place */ diff --git a/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java new file mode 100644 index 000000000..1c451fd5a --- /dev/null +++ b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java @@ -0,0 +1,71 @@ +package org.labkey.test.tests.targetedms.upgrade; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.labkey.remoteapi.query.SelectRowsCommand; +import org.labkey.remoteapi.query.SelectRowsResponse; +import org.labkey.test.tests.targetedms.TargetedMSTest.FolderType; +import org.labkey.test.tests.upgrade.BaseUpgradeTest; +import org.labkey.test.util.UIContainerHelper; +import org.labkey.test.util.targetedms.TargetedMSHelper; + +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +/** + * Verifies that the targetedms-26.006-26.007 upgrade script correctly populates PeptideGroupCount, + * MoleculeGroupCount, and ProteinCount on existing runs after the schema migration. + */ +@Category({}) +public class TargetedMSUpgradeTest extends BaseUpgradeTest +{ + private static final String SKY_FILE = "smallmol_plus_peptides.sky.zip"; + + public TargetedMSUpgradeTest() + { + setContainerHelper(new UIContainerHelper(this)); + } + + @Override + protected String getProjectName() + { + return "TargetedMS Upgrade Test"; + } + + @Override + protected void doSetup() throws Exception + { + TargetedMSHelper helper = new TargetedMSHelper(this); + helper.setupFolder(getProjectName(), FolderType.Experiment); + helper.importData(SKY_FILE); + } + + @Test + public void testRunCounts() throws Exception + { + SelectRowsCommand cmd = new SelectRowsCommand("targetedms", "Runs"); + cmd.setColumns(List.of("PeptideGroups", "MoleculeLists", "Proteins")); + SelectRowsResponse response = cmd.execute(createDefaultConnection(), getProjectName()); + + List> rows = response.getRows(); + assertEquals("Expected exactly one run", 1, rows.size()); + Map run = rows.get(0); + assertEquals("PeptideGroupCount", 24, ((Number) run.get("PeptideGroups")).intValue()); + assertEquals("MoleculeGroupCount", 3, ((Number) run.get("MoleculeLists")).intValue()); + assertEquals("ProteinCount", 24, ((Number) run.get("Proteins")).intValue()); + } + + @Override + protected void doCleanup(boolean afterTest) + { + _containerHelper.deleteProject(getProjectName(), afterTest); + } + + @Override + public List getAssociatedModules() + { + return List.of("targetedms"); + } +} diff --git a/test/src/org/labkey/test/util/targetedms/TargetedMSHelper.java b/test/src/org/labkey/test/util/targetedms/TargetedMSHelper.java new file mode 100644 index 000000000..36797c54e --- /dev/null +++ b/test/src/org/labkey/test/util/targetedms/TargetedMSHelper.java @@ -0,0 +1,68 @@ +package org.labkey.test.util.targetedms; + +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.Locator; +import org.labkey.test.TestFileUtils; +import org.labkey.test.tests.targetedms.TargetedMSTest.FolderType; +import org.labkey.test.util.LogMethod; +import org.labkey.test.util.LoggedParam; +import org.openqa.selenium.WebElement; + +import java.nio.file.Paths; + +/** Setup and import utilities to share between standard and upgrade tests for TargetedMS */ +public class TargetedMSHelper +{ + private final BaseWebDriverTest _test; + + public TargetedMSHelper(BaseWebDriverTest test) + { + _test = test; + } + + public void setupFolder(String projectName, FolderType folderType) + { + _test._containerHelper.createProject(projectName, "Panorama"); + _test.waitForElement(Locator.linkContainingText("Save")); + _test.clickAndWait(Locator.linkContainingText("Next")); + selectFolderType(folderType); + } + + @LogMethod + public void selectFolderType(@LoggedParam FolderType folderType) + { + _test.log("Select Folder Type: " + folderType); + folderType.chooseFolderType(_test); + _test.clickButton("Finish"); + } + + public void importData(String file) + { + importData(file, 1); + } + + @LogMethod + public void importData(@LoggedParam String file, int jobCount) + { + importData(file, jobCount, false); + } + + @LogMethod + public void importData(@LoggedParam String file, int jobCount, boolean expectError) + { + Locator.XPathLocator importButtonLoc = Locator.lkButton("Process and Import Data"); + WebElement importButton = importButtonLoc.findElementOrNull(_test.getDriver()); + if (null == importButton) + { + _test.goToModule("Pipeline"); + importButton = importButtonLoc.findElement(_test.getDriver()); + } + _test.clickAndWait(importButton); + String fileName = Paths.get(file).getFileName().toString(); + if (!_test._fileBrowserHelper.fileIsPresent(fileName)) + _test._fileBrowserHelper.uploadFile(TestFileUtils.getSampleData("TargetedMS/" + file)); + _test._fileBrowserHelper.importFile(fileName, "Import Skyline Results"); + _test.waitForText("Skyline document import"); + _test.waitForPipelineJobsToComplete(jobCount, file, expectError); + } +} From f5f93bfc893f165f4136061a474b4c583a610804 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Mon, 18 May 2026 09:42:40 -0700 Subject: [PATCH 20/24] Protein group, not peptide group --- src/org/labkey/targetedms/TargetedMSController.java | 2 +- src/org/labkey/targetedms/TargetedMSSchema.java | 7 +++++-- src/org/labkey/targetedms/view/runSummaryView.jsp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/targetedms/TargetedMSController.java b/src/org/labkey/targetedms/TargetedMSController.java index b615bba52..7ae73decc 100644 --- a/src/org/labkey/targetedms/TargetedMSController.java +++ b/src/org/labkey/targetedms/TargetedMSController.java @@ -4552,7 +4552,7 @@ protected QueryView createQueryView(RunDetailsForm form, BindException errors, b { TargetedMSSchema schema = new TargetedMSSchema(getUser(), getContainer()); DocumentPeptideGroupView view = new DocumentPeptideGroupView(getViewContext(), schema, form.getId(), - TargetedMSSchema.TABLE_PEPTIDE_GROUP, "Peptide Groups"); + TargetedMSSchema.TABLE_PEPTIDE_GROUP, "Protein Groups"); view.setShowDetailsColumn(false); view.setShowFilterDescription(false); return view; diff --git a/src/org/labkey/targetedms/TargetedMSSchema.java b/src/org/labkey/targetedms/TargetedMSSchema.java index f9a6371f2..0a53853aa 100644 --- a/src/org/labkey/targetedms/TargetedMSSchema.java +++ b/src/org/labkey/targetedms/TargetedMSSchema.java @@ -1276,12 +1276,15 @@ public TableInfo createTable(String name, ContainerFilter cf) )); var labelColumn = result.getMutableColumnOrThrow("Label"); - labelColumn.setURL(new DetailsURL(new ActionURL(TargetedMSController.ShowProteinAction.class, getContainer()), - Collections.singletonMap("id", FieldKey.fromParts("PeptideGroupId")))); + Map labelUrlParams = new HashMap<>(); + labelUrlParams.put("id", FieldKey.fromParts("PeptideGroupId")); + labelUrlParams.put("proteinId", FieldKey.fromParts("Id")); + labelColumn.setURL(new DetailsURL(new ActionURL(TargetedMSController.ShowProteinAction.class, getContainer()), labelUrlParams)); FieldKey containerFieldKey = result.getContainerFieldKey(); labelColumn.setDisplayColumnFactory(colInfo -> { Map params = new HashMap<>(); params.put("id", new FieldKey(colInfo.getFieldKey().getParent(), "PeptideGroupId")); + params.put("proteinId", new FieldKey(colInfo.getFieldKey().getParent(), "Id")); JSONObject props = new JSONObject(); props.put("width", 450); props.put("title", "Protein Details"); diff --git a/src/org/labkey/targetedms/view/runSummaryView.jsp b/src/org/labkey/targetedms/view/runSummaryView.jsp index cd3674186..3d068102c 100644 --- a/src/org/labkey/targetedms/view/runSummaryView.jsp +++ b/src/org/labkey/targetedms/view/runSummaryView.jsp @@ -106,7 +106,7 @@
 
- <% if (run.getPeptideGroupCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getPeptideGroupCount(), "peptide group"))%><% + <% if (run.getPeptideGroupCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getPeptideGroupCount(), "protein group"))%><% if (run.getProteinCount() > 0) { %> (with <%= h(StringUtilsLabKey.pluralize(run.getProteinCount(), "protein"))%>)<% } %>, <% } %> <% if (run.getMoleculeGroupCount() > 0) { %><%= h(StringUtilsLabKey.pluralize(run.getMoleculeGroupCount(), "molecule list"))%>,<% } %> From 3eb5e36b69e144c61214656970fc28beb30b5872 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Mon, 18 May 2026 09:42:47 -0700 Subject: [PATCH 21/24] Protein group, not peptide group --- .../tests/targetedms/TargetedMSExperimentTest.java | 10 +++++----- .../targetedms/TargetedMSProteinGroupingTest.java | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java index 9c27c9da3..6430a5336 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSExperimentTest.java @@ -915,8 +915,8 @@ protected void verifyRunDetailLinks() // Test each header link from runSummaryView.jsp while on the default run view. // Each link must reach its own dedicated list page, not the combined precursor list. - // "24 peptide groups" → peptide group list: one row per peptide-bearing group - clickAndWait(Locator.linkContainingText("24 peptide groups")); + // "24 protein groups" → protein group list: one row per peptide-bearing group + clickAndWait(Locator.linkContainingText("24 protein groups")); DataRegionTable peptideGroupView = DataRegion(getDriver()).withName("PeptideGroup").find(); assertEquals(24, peptideGroupView.getDataRowCount()); @@ -937,11 +937,11 @@ protected void verifyRunDetailLinks() // Verify the runSummaryView.jsp header links also work when on a non-default run detail page. // The header is embedded on every run detail view, not just the precursor list. - // From the small molecule list page, click "24 peptide groups". - clickAndWait(Locator.linkContainingText("24 peptide groups")); + // From the small molecule list page, click "24 protein groups". + clickAndWait(Locator.linkContainingText("24 protein groups")); peptideGroupView = DataRegion(getDriver()).withName("PeptideGroup").find(); assertEquals(24, peptideGroupView.getDataRowCount()); - // From the peptide group list page, click "44 peptides". + // From the protein group list page, click "44 peptides". clickAndWait(Locator.linkContainingText("44 peptides")); peptideView = DataRegion(getDriver()).withName("Peptide").find(); assertEquals(44, peptideView.getDataRowCount()); diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSProteinGroupingTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSProteinGroupingTest.java index dd285d028..f61286f6c 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSProteinGroupingTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSProteinGroupingTest.java @@ -46,6 +46,7 @@ public void testProteinGrouping() goToProjectHome(); clickAndWait(Locator.linkWithText(SKY_FILE)); + verifyRunSummaryCountsPep(31, 47, 33, 0, 33, 198, 4, 0, 0); clickAndWait(Locator.linkWithText(group)); log("Verifying protein matches for peptide"); From 021efcf9af864a2e3a617c9b251591f5e9cf3020 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Mon, 18 May 2026 13:19:49 -0700 Subject: [PATCH 22/24] Split upgrade test into pre- and post-upgrade count checks; fix upgrade script and run summary labels testPreUpgradeCounts queries columns available in 25.11 (Peptides, SmallMolecules, Replicates) and runs on both setup and verify phases. testPostUpgradeCounts is gated with @EarliestVersion("26.3") and Assume.assumeFalse(isUpgradeSetupPhase) so it only runs after the 26.006-26.007 migration. Also adds missing PeptideGroupCount NOT NULL constraint to the upgrade script and renames 'peptide group' labels to 'protein group' in verifyRunSummaryCounts. Co-Authored-By: Claude Sonnet 4.6 --- .../postgresql/targetedms-26.006-26.007.sql | 1 + .../test/tests/targetedms/TargetedMSTest.java | 10 ++++----- .../upgrade/TargetedMSUpgradeTest.java | 21 ++++++++++++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql b/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql index 73c90cbd7..3ee9b11ed 100644 --- a/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql +++ b/resources/schemas/dbscripts/postgresql/targetedms-26.006-26.007.sql @@ -30,4 +30,5 @@ SET MoleculeGroupCount = ( ); 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/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java index eacfceea0..f60b1519f 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSTest.java @@ -268,18 +268,18 @@ protected void verifyRunSummaryCountsPep(int peptideGroupCount, int proteinCount verifyRunSummaryCounts(peptideGroupCount, 0, proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount); } - protected void verifyRunSummaryCountsMixed(int peptideGroupCount, int moleculeGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) + protected void verifyRunSummaryCountsMixed(int proteinGroupCount, int moleculeGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) { - verifyRunSummaryCounts(peptideGroupCount, moleculeGroupCount, proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount); + verifyRunSummaryCounts(proteinGroupCount, moleculeGroupCount, proteinCount, peptideCount, moleculeCount, precursorCount, transitionCount, replicateCount, calibrationCount, listCount); } @LogMethod - protected void verifyRunSummaryCounts(int peptideGroupCount, int moleculeGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, + protected void verifyRunSummaryCounts(int proteinGroupCount, int moleculeGroupCount, int proteinCount, int peptideCount, int moleculeCount, int precursorCount, int transitionCount, int replicateCount, int calibrationCount, int listCount) { log("Verifying expected summary counts"); - if (peptideGroupCount > 0) - waitForElement(Locator.linkContainingText(peptideGroupCount + " peptide group" + (peptideGroupCount == 1 ? "" : "s"))); + if (proteinGroupCount > 0) + waitForElement(Locator.linkContainingText(proteinGroupCount + " protein group" + (proteinGroupCount == 1 ? "" : "s"))); if (moleculeGroupCount > 0) assertElementPresent(Locator.linkContainingText(moleculeGroupCount + " molecule list" + (moleculeGroupCount == 1 ? "" : "s"))); if (proteinCount > 0) diff --git a/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java index 1c451fd5a..93f22fdc1 100644 --- a/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java +++ b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java @@ -1,5 +1,6 @@ package org.labkey.test.tests.targetedms.upgrade; +import org.junit.Assume; import org.junit.Test; import org.junit.experimental.categories.Category; import org.labkey.remoteapi.query.SelectRowsCommand; @@ -43,8 +44,26 @@ protected void doSetup() throws Exception } @Test - public void testRunCounts() throws Exception + public void testPreUpgradeCounts() throws Exception { + SelectRowsCommand cmd = new SelectRowsCommand("targetedms", "Runs"); + cmd.setColumns(List.of("Peptides", "SmallMolecules", "Replicates")); + SelectRowsResponse response = cmd.execute(createDefaultConnection(), getProjectName()); + + List> rows = response.getRows(); + assertEquals("Expected exactly one run", 1, rows.size()); + Map run = rows.get(0); + assertEquals("PeptideCount", 44, ((Number) run.get("Peptides")).intValue()); + assertEquals("SmallMoleculeCount", 98, ((Number) run.get("SmallMolecules")).intValue()); + assertEquals("ReplicateCount", 5, ((Number) run.get("Replicates")).intValue()); + } + + @Test + @EarliestVersion("26.3") + public void testPostUpgradeCounts() throws Exception + { + Assume.assumeFalse("Skipping post-upgrade count checks during setup phase", isUpgradeSetupPhase); + SelectRowsCommand cmd = new SelectRowsCommand("targetedms", "Runs"); cmd.setColumns(List.of("PeptideGroups", "MoleculeLists", "Proteins")); SelectRowsResponse response = cmd.execute(createDefaultConnection(), getProjectName()); From 1ff37d609edff0b5a4c0928b77df99194bc253be Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Mon, 18 May 2026 15:27:29 -0700 Subject: [PATCH 23/24] Fix field names --- .../upgrade/TargetedMSUpgradeTest.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java index 93f22fdc1..0c38f533e 100644 --- a/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java +++ b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java @@ -47,15 +47,15 @@ protected void doSetup() throws Exception public void testPreUpgradeCounts() throws Exception { SelectRowsCommand cmd = new SelectRowsCommand("targetedms", "Runs"); - cmd.setColumns(List.of("Peptides", "SmallMolecules", "Replicates")); + cmd.setColumns(List.of("PeptideCount", "SmallMoleculeCount", "ReplicateCount")); SelectRowsResponse response = cmd.execute(createDefaultConnection(), getProjectName()); List> rows = response.getRows(); assertEquals("Expected exactly one run", 1, rows.size()); - Map run = rows.get(0); - assertEquals("PeptideCount", 44, ((Number) run.get("Peptides")).intValue()); - assertEquals("SmallMoleculeCount", 98, ((Number) run.get("SmallMolecules")).intValue()); - assertEquals("ReplicateCount", 5, ((Number) run.get("Replicates")).intValue()); + Map run = rows.getFirst(); + assertEquals("PeptideCount", 44, ((Number) run.get("PeptideCount")).intValue()); + assertEquals("SmallMoleculeCount", 98, ((Number) run.get("SmallMoleculeCount")).intValue()); + assertEquals("ReplicateCount", 5, ((Number) run.get("ReplicateCount")).intValue()); } @Test @@ -70,10 +70,10 @@ public void testPostUpgradeCounts() throws Exception List> rows = response.getRows(); assertEquals("Expected exactly one run", 1, rows.size()); - Map run = rows.get(0); - assertEquals("PeptideGroupCount", 24, ((Number) run.get("PeptideGroups")).intValue()); - assertEquals("MoleculeGroupCount", 3, ((Number) run.get("MoleculeLists")).intValue()); - assertEquals("ProteinCount", 24, ((Number) run.get("Proteins")).intValue()); + Map run = rows.getFirst(); + assertEquals("PeptideGroupCount", 24, ((Number) run.get("PeptideGroupCount")).intValue()); + assertEquals("MoleculeGroupCount", 3, ((Number) run.get("MoleculeGroupCount")).intValue()); + assertEquals("ProteinCount", 24, ((Number) run.get("ProteinCount")).intValue()); } @Override From c46bd98e935304d8ffcb71ca8c5fdd51f04d62ec Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Mon, 18 May 2026 21:07:55 -0700 Subject: [PATCH 24/24] Fix annotations --- .../test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java index 0c38f533e..8220294af 100644 --- a/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java +++ b/test/src/org/labkey/test/tests/targetedms/upgrade/TargetedMSUpgradeTest.java @@ -44,6 +44,7 @@ protected void doSetup() throws Exception } @Test + @EarliestVersion("25.11") public void testPreUpgradeCounts() throws Exception { SelectRowsCommand cmd = new SelectRowsCommand("targetedms", "Runs"); @@ -65,7 +66,7 @@ public void testPostUpgradeCounts() throws Exception Assume.assumeFalse("Skipping post-upgrade count checks during setup phase", isUpgradeSetupPhase); SelectRowsCommand cmd = new SelectRowsCommand("targetedms", "Runs"); - cmd.setColumns(List.of("PeptideGroups", "MoleculeLists", "Proteins")); + cmd.setColumns(List.of("PeptideGroupCount", "MoleculeGroupCount", "ProteinCount")); SelectRowsResponse response = cmd.execute(createDefaultConnection(), getProjectName()); List> rows = response.getRows();