diff --git a/resources/queries/targetedms/PTMPercentsGrouped.sql b/resources/queries/targetedms/PTMPercentsGrouped.sql
index a196a9cdd..42402a76e 100644
--- a/resources/queries/targetedms/PTMPercentsGrouped.sql
+++ b/resources/queries/targetedms/PTMPercentsGrouped.sql
@@ -18,7 +18,7 @@ SELECT
MAX(ModificationCount) AS ModificationCount @hidden
FROM
- PTMPercentsGroupedPrepivot
+ PTMPercentsGroupedPrepivotCache
GROUP BY
ReplicateName,
Sequence,
diff --git a/resources/queries/targetedms/PTMPercentsGroupedPrepivot/.qview.xml b/resources/queries/targetedms/PTMPercentsGroupedPrepivot/.qview.xml
new file mode 100644
index 000000000..0b110ac4f
--- /dev/null
+++ b/resources/queries/targetedms/PTMPercentsGroupedPrepivot/.qview.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/queries/targetedms/PTMPercentsGroupedPrepivotCache.query.xml b/resources/queries/targetedms/PTMPercentsGroupedPrepivotCache.query.xml
new file mode 100644
index 000000000..191fa354a
--- /dev/null
+++ b/resources/queries/targetedms/PTMPercentsGroupedPrepivotCache.query.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+ org.labkey.targetedms.query.ModifiedSequenceDisplayColumn$PeptideDisplayColumnFactory
+
+ true
+ true
+ SiteLocation
+
+
+ Sequence
+
+
+ 0.00%
+
+
+ 0.00%
+
+
+ 0.00%
+
+
+ UnmodifiedSequence
+
+
+
+ org.labkey.targetedms.query.CDRDisplayColumnFactory
+
+
+
+
+ org.labkey.targetedms.query.PTMRiskDisplayColumnFactory
+
+
+
+
+
+
+
diff --git a/resources/queries/targetedms/PTMPercentsGroupedPrepivotCache/.qview.xml b/resources/queries/targetedms/PTMPercentsGroupedPrepivotCache/.qview.xml
new file mode 100644
index 000000000..0b110ac4f
--- /dev/null
+++ b/resources/queries/targetedms/PTMPercentsGroupedPrepivotCache/.qview.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/schemas/dbscripts/postgresql/targetedms-26.001-26.002.sql b/resources/schemas/dbscripts/postgresql/targetedms-26.001-26.002.sql
new file mode 100644
index 000000000..59d26d370
--- /dev/null
+++ b/resources/schemas/dbscripts/postgresql/targetedms-26.001-26.002.sql
@@ -0,0 +1,35 @@
+CREATE TABLE targetedms.PTMPercentsGroupedPrepivotCache
+(
+ Id BIGINT NOT NULL, -- PeptideId lookup, not a primary key
+ Container ENTITYID NOT NULL,
+ RunId BIGINT NOT NULL,
+ Modification VARCHAR(300) NOT NULL,
+ TotalPercentModified REAL,
+ PercentModified REAL,
+ MaxPercentModified REAL,
+ ModificationCount INT,
+ PeptideModifiedSequence VARCHAR(300),
+ Sequence VARCHAR(300),
+ PreviousAA VARCHAR(2),
+ NextAA VARCHAR(2),
+ SampleFileId BIGINT NOT NULL,
+ ReplicateName VARCHAR(200),
+ AminoAcid VARCHAR(5),
+ SiteLocation VARCHAR(50),
+ Location INT,
+ PeptideGroupId BIGINT NOT NULL,
+
+ CONSTRAINT FK_PTMPercentsGroupedPrepivotCache_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId),
+ CONSTRAINT FK_PTMPercentsGroupedPrepivotCache_Id FOREIGN KEY (Id) REFERENCES targetedms.Peptide(Id),
+ CONSTRAINT FK_PTMPercentsGroupedPrepivotCache_RunId FOREIGN KEY (RunId) REFERENCES targetedms.Runs(Id),
+ CONSTRAINT FK_PTMPercentsGroupedPrepivotCache_SampleFileId FOREIGN KEY (SampleFileId) REFERENCES targetedms.SampleFile(Id),
+ CONSTRAINT FK_PTMPercentsGroupedPrepivotCache_PeptideGroupId FOREIGN KEY (PeptideGroupId) REFERENCES targetedms.PeptideGroup(Id)
+);
+
+CREATE INDEX IDX_PTMPercentsGroupedPrepivotCache_Id ON targetedms.PTMPercentsGroupedPrepivotCache(Id);
+CREATE INDEX IDX_PTMPercentsGroupedPrepivotCache_RunId ON targetedms.PTMPercentsGroupedPrepivotCache(RunId);
+CREATE INDEX IDX_PTMPercentsGroupedPrepivotCache_Container ON targetedms.PTMPercentsGroupedPrepivotCache(Container);
+CREATE INDEX IDX_PTMPercentsGroupedPrepivotCache_SampleFileId ON targetedms.PTMPercentsGroupedPrepivotCache(SampleFileId);
+CREATE INDEX IDX_PTMPercentsGroupedPrepivotCache_PeptideGroupId ON targetedms.PTMPercentsGroupedPrepivotCache(PeptideGroupId);
+
+SELECT core.executeJavaUpgradeCode('populatePTMPercentsGroupedPrepivotCache');
diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml
index 2bd32a2d8..569b890e1 100644
--- a/resources/schemas/targetedms.xml
+++ b/resources/schemas/targetedms.xml
@@ -1950,4 +1950,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/labkey/targetedms/SkylineDocImporter.java b/src/org/labkey/targetedms/SkylineDocImporter.java
index 5e6f30648..3ceca58f6 100644
--- a/src/org/labkey/targetedms/SkylineDocImporter.java
+++ b/src/org/labkey/targetedms/SkylineDocImporter.java
@@ -514,6 +514,7 @@ else if (folderType == TargetedMSService.FolderType.Library)
parser.logMissingChromatogramCounts();
TargetedMSManager.updateModifiedAreaProportions(_log, run);
+ TargetedMSManager.populatePTMPercentsGroupedPrepivotCache(run, _user, _container);
if (_pipeRoot.isCloudRoot())
copyExtractedFilesToCloud(run);
diff --git a/src/org/labkey/targetedms/TargetedMSManager.java b/src/org/labkey/targetedms/TargetedMSManager.java
index 2d55db0fd..64ed12e47 100644
--- a/src/org/labkey/targetedms/TargetedMSManager.java
+++ b/src/org/labkey/targetedms/TargetedMSManager.java
@@ -602,6 +602,11 @@ public static TableInfo getTableInfoQCMetricCache()
return getSchema().getTable(TargetedMSSchema.TABLE_QC_METRIC_CACHE);
}
+ public static TableInfo getTableInfoPTMPercentsGroupedPrepivotCache()
+ {
+ return getSchema().getTable(TargetedMSSchema.TABLE_PTM_PERCENTS_GROUPED_PREPIVOT_CACHE);
+ }
+
public static TableInfo getTableInfoSkylineAuditLogEntry()
{
return getSchema().getTable(TargetedMSSchema.TABLE_SKYLINE_AUDITLOG_ENTRY);
@@ -1029,6 +1034,12 @@ public static TargetedMSRun[] getRunsInContainer(Container container)
container.getId(), SkylineDocImporter.STATUS_SUCCESS, Boolean.FALSE);
}
+ public static TargetedMSRun[] getAllNonDeletedRuns()
+ {
+ return getRuns("StatusId=? AND deleted=?",
+ SkylineDocImporter.STATUS_SUCCESS, Boolean.FALSE);
+ }
+
@Nullable
public static TargetedMSRun getRunByFileName(String fileName, Container container)
{
@@ -1762,6 +1773,7 @@ private static SQLFragment getTempChromInfoIdsDependentDeleteSql(TableInfo fromT
/** Actually delete runs that have been marked as deleted from the database */
private static void purgeDeletedRuns()
{
+ deleteRunDependent(getTableInfoPTMPercentsGroupedPrepivotCache());
// Delete from FoldChange
deleteRunDependent(getTableInfoFoldChange());
// Delete from CalibrationCurve
@@ -2732,6 +2744,63 @@ public static void updateModifiedAreaProportions(@Nullable Logger log, @NotNull
executor.execute("DROP TABLE " + areasTableName);
}
+ /**
+ * Pre-compute PTMPercentsGroupedPrepivot results during import and store in PTMPercentsCache.
+ * Only populates cache for ExperimentMAM folders.
+ */
+ public static void populatePTMPercentsGroupedPrepivotCache(@NotNull TargetedMSRun run, @NotNull User user, @NotNull Container container)
+ {
+ // Delete any existing cache rows for this run
+ new SqlExecutor(getSchema()).execute(
+ new SQLFragment("DELETE FROM ").append(getTableInfoPTMPercentsGroupedPrepivotCache()).append(" WHERE RunId = ?").add(run.getId()));
+
+ // Only populate cache for ExperimentMAM folders
+ if (getFolderType(container) != TargetedMSService.FolderType.ExperimentMAM)
+ {
+ return;
+ }
+
+ _log.info("Populating PTMPercentsGroupedPrepivotCache for run " + run.getId());
+
+ String labkeySql = "SELECT\n" +
+ " Modification,\n" +
+ " TotalPercentModified,\n" +
+ " PercentModified,\n" +
+ " MaxPercentModified,\n" +
+ " ModificationCount,\n" +
+ " Id,\n" +
+ " PeptideModifiedSequence,\n" +
+ " Sequence,\n" +
+ " PreviousAA,\n" +
+ " NextAA,\n" +
+ " SampleFileId,\n" +
+ " ReplicateName,\n" +
+ " AminoAcid,\n" +
+ " SiteLocation,\n" +
+ " Location,\n" +
+ " PeptideGroupId\n" +
+ "FROM PTMPercentsGroupedPrepivot\n" +
+ "WHERE PeptideGroupId.RunId = " + run.getId();
+
+ UserSchema schema = QueryService.get().getUserSchema(user, container, TargetedMSSchema.SCHEMA_KEY);
+ TableInfo tableInfo = QueryService.get().createTable(schema, labkeySql, null, true);
+
+ SQLFragment insertSql = new SQLFragment();
+ insertSql.append("INSERT INTO ").append(getTableInfoPTMPercentsGroupedPrepivotCache());
+ insertSql.append(" (Container, RunId, Modification, TotalPercentModified, PercentModified, MaxPercentModified,");
+ insertSql.append(" ModificationCount, Id, PeptideModifiedSequence, Sequence,");
+ insertSql.append(" PreviousAA, NextAA, SampleFileId, ReplicateName, AminoAcid, SiteLocation, Location, PeptideGroupId)");
+ insertSql.append(" SELECT ?, ?, lk.Modification, lk.TotalPercentModified, lk.PercentModified, lk.MaxPercentModified,");
+ insertSql.append(" lk.ModificationCount, lk.Id, lk.PeptideModifiedSequence, lk.Sequence,");
+ insertSql.append(" lk.PreviousAA, lk.NextAA, lk.SampleFileId, lk.ReplicateName, lk.AminoAcid, lk.SiteLocation, lk.Location, lk.PeptideGroupId");
+ insertSql.append(" FROM ").append(tableInfo, "lk");
+ insertSql.add(container.getEntityId());
+ insertSql.add(run.getId());
+ new SqlExecutor(getSchema()).execute(insertSql);
+
+ _log.info("Finished populating PTMPercentsGroupedPrepivotCache for run " + run.getId());
+ }
+
/**
* Returns the Transition Full Scan settings for the given run. This may be null if the settings were not included
* in the Skyline document.
diff --git a/src/org/labkey/targetedms/TargetedMSModule.java b/src/org/labkey/targetedms/TargetedMSModule.java
index c68243f38..6d6538561 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.001;
+ return 26.002;
}
@Override
diff --git a/src/org/labkey/targetedms/TargetedMSSchema.java b/src/org/labkey/targetedms/TargetedMSSchema.java
index db2ef2cb7..a863332d8 100644
--- a/src/org/labkey/targetedms/TargetedMSSchema.java
+++ b/src/org/labkey/targetedms/TargetedMSSchema.java
@@ -71,6 +71,7 @@
import org.labkey.api.security.permissions.AdminPermission;
import org.labkey.api.targetedms.RepresentativeDataState;
import org.labkey.api.targetedms.RunRepresentativeDataState;
+import org.labkey.api.targetedms.TargetedMSService;
import org.labkey.api.util.ContainerContext;
import org.labkey.api.util.HtmlString;
import org.labkey.api.util.Pair;
@@ -232,6 +233,7 @@ public class TargetedMSSchema extends UserSchema
public static final String TABLE_QC_ENABLED_METRICS = "QCEnabledMetrics";
public static final String TABLE_QC_TRACE_METRIC_VALUES = "QCTraceMetricValues";
public static final String TABLE_QC_METRIC_CACHE = "QCMetricCache";
+ public static final String TABLE_PTM_PERCENTS_GROUPED_PREPIVOT_CACHE = "PTMPercentsGroupedPrepivotCache";
public static final String TABLE_GUIDE_SET = "GuideSet";
@@ -1689,6 +1691,14 @@ else if (TABLE_INSTRUMENT_USAGE_PAYMENT.equalsIgnoreCase(name))
result.getMutableColumnOrThrow("ExperimentRunLSID").setFk(QueryForeignKey.from(_expSchema, cf).to("Runs", "LSID", null));
}
TargetedMSTable.fixupLookups(result);
+
+ if (name.equalsIgnoreCase(TABLE_PTM_PERCENTS_GROUPED_PREPIVOT_CACHE))
+ {
+ // Inject two null columns to match how the uncached version wires up DisplayColumns
+ result.addColumn(new ExprColumn(result, "Risk", new SQLFragment("CAST(NULL AS VARCHAR)"), JdbcType.VARCHAR));
+ result.addColumn(new ExprColumn(result, "IsCdr", new SQLFragment("CAST(NULL AS VARCHAR)"), JdbcType.VARCHAR));
+ }
+
return result;
}
@@ -1882,6 +1892,7 @@ private Set getAllTableNames(boolean caseInsensitive)
hs.add(TABLE_RATE_TYPE);
hs.add(TABLE_INSTRUMENT_RATE);
hs.add(TABLE_INSTRUMENT_USAGE_PAYMENT);
+ hs.add(TABLE_PTM_PERCENTS_GROUPED_PREPIVOT_CACHE);
return hs;
}
@@ -1921,6 +1932,11 @@ private QueryDefinition createRunScopedPTMQuery(String prefix, String baseQueryN
try
{
long runId = Long.parseLong(runIdString);
+ if (TargetedMSManager.getFolderType(getContainer()) != TargetedMSService.FolderType.ExperimentMAM)
+ {
+ throw new IllegalStateException("PTM queries are only supported in ExperimentMAM folders");
+ }
+
QueryDefinition queryDef = Objects.requireNonNull(getQueryDef(baseQueryName));
QueryDefinition result = QueryService.get().createQueryDef(getUser(), getContainer(), getSchemaPath(), queryName);
result.setSql(queryDef.getSql() + " IN (SELECT r.Name FROM targetedms.Replicate r WHERE r.RunId = " + runId + ")");
diff --git a/src/org/labkey/targetedms/TargetedMSUpgradeCode.java b/src/org/labkey/targetedms/TargetedMSUpgradeCode.java
index 1654517a3..3035b5fec 100644
--- a/src/org/labkey/targetedms/TargetedMSUpgradeCode.java
+++ b/src/org/labkey/targetedms/TargetedMSUpgradeCode.java
@@ -15,7 +15,10 @@
*/
package org.labkey.targetedms;
+import org.apache.logging.log4j.Logger;
+import org.labkey.api.data.Container;
import org.labkey.api.data.ContainerManager;
+import org.labkey.api.data.DeferredUpgrade;
import org.labkey.api.data.SQLFragment;
import org.labkey.api.data.SqlExecutor;
import org.labkey.api.data.UpgradeCode;
@@ -23,6 +26,8 @@
import org.labkey.api.module.ModuleContext;
import org.labkey.api.module.ModuleLoader;
import org.labkey.api.security.User;
+import org.labkey.api.targetedms.TargetedMSService;
+import org.labkey.api.util.logging.LogHelper;
import org.labkey.targetedms.query.QCAnnotationTypeTable;
import java.util.Date;
@@ -36,6 +41,8 @@
*/
public class TargetedMSUpgradeCode implements UpgradeCode
{
+ private static final Logger LOG = LogHelper.getLogger(TargetedMSUpgradeCode.class, "TargetedMS schema upgrade operations");
+
// called at every bootstrap to initialize annotation types
@SuppressWarnings({"UnusedDeclaration"})
public void populateDefaultAnnotationTypes(final ModuleContext moduleContext)
@@ -69,4 +76,36 @@ private void insertAnnotationType(String name, String color, User user)
sql.add(color);
new SqlExecutor(TargetedMSManager.getSchema()).execute(sql);
}
+
+ /** Populate PTMPercentsGroupedPrepivotCache for all runs in existing ExperimentMAM folders */
+ @SuppressWarnings("UnusedDeclaration")
+ @DeferredUpgrade
+ public void populatePTMPercentsGroupedPrepivotCache(final ModuleContext moduleContext)
+ {
+ if (moduleContext.isNewInstall())
+ {
+ return;
+ }
+
+ User user = moduleContext.getUpgradeUser();
+ LOG.info("Populating PTMPercentsGroupedPrepivotCache for existing ExperimentMAM folders");
+
+ for (TargetedMSRun run : TargetedMSManager.getAllNonDeletedRuns())
+ {
+ Container container = run.getContainer();
+ if (container != null && TargetedMSManager.getFolderType(container) == TargetedMSService.FolderType.ExperimentMAM)
+ {
+ try
+ {
+ TargetedMSManager.populatePTMPercentsGroupedPrepivotCache(run, user, container);
+ }
+ catch (Exception e)
+ {
+ LOG.error("Error populating PTMPercentsGroupedPrepivotCache for run " + run.getId() + " in " + container.getPath(), e);
+ }
+ }
+ }
+
+ LOG.info("Finished populating PTMPercentsGroupedPrepivotCache for existing ExperimentMAM folders");
+ }
}
diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSEarlyStagePTMReportTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSEarlyStagePTMReportTest.java
index dedbdb2e4..c0b5907b1 100644
--- a/test/src/org/labkey/test/tests/targetedms/TargetedMSEarlyStagePTMReportTest.java
+++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSEarlyStagePTMReportTest.java
@@ -39,10 +39,20 @@ private void doInit()
@Test
public void testEarlyStagePrepivot()
{
+ // Test against the live-query version
goToProjectHome();
goToSchemaBrowser();
DataRegionTable table = viewQueryData("targetedms", "PTMPercentsGroupedPrepivot");
+ verifyPrepivotData(table);
+ // Test against the cached version too
+ goToSchemaBrowser();
+ table = viewQueryData("targetedms", "PTMPercentsGroupedPrepivotCache");
+ verifyPrepivotData(table);
+ }
+
+ private void verifyPrepivotData(DataRegionTable table)
+ {
// Test special-cased peptide
table.setFilter("PeptideModifiedSequence", "Starts With", "EEQ");
table = new DataRegionTable("query", this);
@@ -59,7 +69,7 @@ public void testEarlyStagePrepivot()
table.setFilter("PeptideModifiedSequence", "Starts With", "VTN");
table = new DataRegionTable("query", this);
assertEquals(List.of("true", "true"), table.getColumnDataAsText("IsCdr"));
- assertEquals(List.of("High", "Medium"), table.getColumnDataAsText("Risk"));
+ assertEquals(List.of("Medium", "High"), table.getColumnDataAsText("Risk"));
// Test special-cased N-Term Modification, present on QVTL peptide (Q is modified, so don't use it in the filter)
table.setFilter("PeptideModifiedSequence", "Contains", "VTL");