Skip to content

Commit b28147d

Browse files
committed
Add code to enforce expectations about input seurat objects
1 parent c84fd5d commit b28147d

File tree

5 files changed

+138
-3
lines changed

5 files changed

+138
-3
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
for (datasetId in names(seuratObjects)) {
2+
seuratObj <- readRDS(seuratObjects[[datasetId]])
3+
4+
if (requireSingleDatasetInput && length(unique(seuratObj$DatasetId)) > 1) {
5+
addErrorMessage(paste0('Seurat data prototypes must be a single dataset. Problem ID: ', datasetId))
6+
}
7+
8+
if (is.null(usesHashing[[datasetId]])) {
9+
addErrorMessage(paste0('No hashing context provided for: ', datasetId))
10+
break
11+
}
12+
13+
if (usesHashing[[datasetId]] && requireHashing) {
14+
if (!('HTO.Classification' %in% names(seuratObj@meta.data))) {
15+
addErrorMessage(paste0('Missing cell hashing calls for dataset: ', datasetId))
16+
}
17+
}
18+
19+
if (is.null(usesCiteSeq[[datasetId]])) {
20+
addErrorMessage(paste0('No CITE-seq context provided for: ', datasetId))
21+
break
22+
}
23+
24+
if (usesCiteSeq[[datasetId]] && requireCiteSeq) {
25+
if (!'ADT' %in% names(seuratObj@assays)) {
26+
addErrorMessage(paste0('Missing ADT data for dataset: ', datasetId))
27+
}
28+
}
29+
30+
if (requireSaturation && !'Saturation.RNA' %in% names(seuratObj@meta.data)) {
31+
addErrorMessage(paste0('Missing per-cell RNA saturation data for dataset: ', datasetId))
32+
}
33+
34+
if (requireSingleR && !'SingleRConsensus' %in% names(seuratObj@meta.data)) {
35+
addErrorMessage(paste0('Missing SingleRConsensus label for dataset: ', datasetId))
36+
}
37+
38+
if (requireScGate && !'scGateConsensus' %in% names(seuratObj@meta.data)) {
39+
addErrorMessage(paste0('Missing scGateConsensus label for dataset: ', datasetId))
40+
}
41+
42+
if (length(errorMessages) > 0) {
43+
break
44+
}
45+
46+
saveData(seuratObj, datasetId)
47+
48+
# Cleanup
49+
rm(seuratObj)
50+
gc()
51+
}

singlecell/resources/chunks/SeuratPrototype.R

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,12 @@ for (datasetId in names(seuratObjects)) {
6161
metricData <- rbind(metricData, data.frame(dataId = datasetId, readsetId = datasetIdToReadset[[datasetId]], metricname = 'MeanSaturation.RNA', metricvalue = meanSaturation.RNA))
6262
}
6363

64-
if (requireSingleR && !'dice.label' %in% names(seuratObj@meta.data)) {
65-
addErrorMessage(paste0('Missing SingleR DICE labels for dataset: ', datasetId))
64+
if (requireSingleR && !'SingleRConsensus' %in% names(seuratObj@meta.data)) {
65+
addErrorMessage(paste0('Missing SingleRConsensus label for dataset: ', datasetId))
66+
}
67+
68+
if (requireScGate && !'scGateConsensus' %in% names(seuratObj@meta.data)) {
69+
addErrorMessage(paste0('Missing scGateConsensus label for dataset: ', datasetId))
6670
}
6771

6872
if (length(errorMessages) > 0) {

singlecell/src/org/labkey/singlecell/SingleCellModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ public static void registerPipelineSteps()
173173
SequencePipelineService.get().registerPipelineStep(new DropCiteSeq.Provider());
174174
SequencePipelineService.get().registerPipelineStep(new RunScGate.Provider());
175175
SequencePipelineService.get().registerPipelineStep(new RunCelltypist.Provider());
176+
SequencePipelineService.get().registerPipelineStep(new CheckExpectations.Provider());
176177

177178
SequenceAnalysisService.get().registerFileHandler(new NimbleAlignmentStep());
178179
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package org.labkey.singlecell.pipeline.singlecell;
2+
3+
import org.labkey.api.pipeline.PipelineJobException;
4+
import org.labkey.api.sequenceanalysis.SequenceOutputFile;
5+
import org.labkey.api.sequenceanalysis.model.Readset;
6+
import org.labkey.api.sequenceanalysis.pipeline.AbstractPipelineStepProvider;
7+
import org.labkey.api.sequenceanalysis.pipeline.PipelineContext;
8+
import org.labkey.api.sequenceanalysis.pipeline.SequenceOutputHandler;
9+
import org.labkey.api.singlecell.CellHashingService;
10+
import org.labkey.api.singlecell.pipeline.SeuratToolParameter;
11+
import org.labkey.api.singlecell.pipeline.SingleCellStep;
12+
import org.labkey.singlecell.CellHashingServiceImpl;
13+
14+
import java.util.Arrays;
15+
import java.util.Collections;
16+
import java.util.List;
17+
import java.util.Set;
18+
19+
public class CheckExpectations extends AbstractCellMembraneStep
20+
{
21+
public CheckExpectations(PipelineContext ctx, CheckExpectations.Provider provider)
22+
{
23+
super(provider, ctx);
24+
}
25+
26+
public static class Provider extends AbstractPipelineStepProvider<SingleCellStep>
27+
{
28+
public Provider()
29+
{
30+
super("CheckExpectations", "Check Expectations", "CellMembrane", "This will tag the output of this job as a seurat prototype, which is designed to be a building block for subsequent analyses.", Arrays.asList(
31+
SeuratToolParameter.create("requireSingleDatasetInput", "Expect Single Datasets", "If checked, this will enforce that each input seurat object holds a single dataset. This is expected if the input is a seurat prototype. In contrast, if the input is a merged object this would test false", "checkbox", null, true),
32+
SeuratToolParameter.create("requireHashing", "Require Hashing, If Used", "If this dataset uses cell hashing, hashing calls are required", "checkbox", null, true),
33+
SeuratToolParameter.create("requireCiteSeq", "Require Cite-Seq, If Used", "If this dataset uses CITE-seq, cite-seq data are required", "checkbox", null, true),
34+
SeuratToolParameter.create("requireSaturation", "Require Per-Cell Saturation", "If this dataset uses TCR sequencing, these data are required", "checkbox", null, true),
35+
SeuratToolParameter.create("requireSingleR", "Require SingleR", "If checked, SingleR calls, including singleRConsensus are required to pass", "checkbox", null, true),
36+
SeuratToolParameter.create("requireScGate", "Require scGate", "If checked, scGateConsensus calls are required to pass", "checkbox", null, true)
37+
), null, null);
38+
}
39+
40+
@Override
41+
public CheckExpectations create(PipelineContext ctx)
42+
{
43+
return new CheckExpectations(ctx, this);
44+
}
45+
}
46+
47+
@Override
48+
public void init(SequenceOutputHandler.JobContext ctx, List<SequenceOutputFile> inputFiles) throws PipelineJobException
49+
{
50+
51+
}
52+
53+
@Override
54+
protected Chunk createParamChunk(SequenceOutputHandler.JobContext ctx, List<SeuratObjectWrapper> inputObjects, String outputPrefix) throws PipelineJobException
55+
{
56+
Chunk ret = super.createParamChunk(ctx, inputObjects, outputPrefix);
57+
58+
ret.bodyLines.add("usesHashing <- list()");
59+
ret.bodyLines.add("usesCiteSeq <- list()");
60+
61+
for (SeuratObjectWrapper so : inputObjects)
62+
{
63+
Readset parentReadset = ctx.getSequenceSupport().getCachedReadset(so.getSequenceOutputFile().getReadset());
64+
if (parentReadset == null)
65+
{
66+
throw new PipelineJobException("Unable to find readset for outputfile: " + so.getSequenceOutputFileId());
67+
}
68+
69+
Set<String> htosPerReadset = CellHashingServiceImpl.get().getHtosForParentReadset(parentReadset.getReadsetId(), ctx.getSourceDirectory(), ctx.getSequenceSupport(), false);
70+
ret.bodyLines.add("usesHashing[['" + so.getDatasetId() + "']] <- " + (htosPerReadset.size() > 1 ? "TRUE" : "FALSE"));
71+
72+
boolean usesCiteseq = CellHashingService.get().usesCiteSeq(ctx.getSequenceSupport(), Collections.singletonList(so.getSequenceOutputFile()));
73+
ret.bodyLines.add("usesCiteSeq[['" + so.getDatasetId() + "']] <- " + (usesCiteseq ? "TRUE" : "FALSE"));
74+
}
75+
76+
return ret;
77+
}
78+
}

singlecell/src/org/labkey/singlecell/pipeline/singlecell/SeuratPrototype.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ public Provider()
4242

4343
SeuratToolParameter.create("dietSeurat", "Run DietSeurat", "If checked, DietSeurat will be run, which removes reductions and extraneous data to save file size.", "checkbox", null, true),
4444

45-
SeuratToolParameter.create("requireSingleR", "Require SingleR", "If checked, SingleR calls are required to pass", "checkbox", null, true)
45+
SeuratToolParameter.create("requireSingleR", "Require SingleR", "If checked, SingleR calls, including singleRConsensus are required to pass", "checkbox", null, true),
46+
SeuratToolParameter.create("requireScGate", "Require scGate", "If checked, scGateConsensus calls are required to pass", "checkbox", null, true)
4647
), null, null);
4748
}
4849

0 commit comments

Comments
 (0)