Skip to content

Commit 22f9978

Browse files
authored
Merge pull request #140 from LabKey/fb_merge_22.3_to_develop
Merge discvr-22.3 to develop
2 parents 919b3d4 + 5d305ab commit 22f9978

File tree

10 files changed

+140
-21
lines changed

10 files changed

+140
-21
lines changed

jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantWidget/ExtendedVariantWidget.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import {FIELD_NAME_MAP, INFO_FIELD_GROUPS, IGNORED_INFO_FIELDS} from "./fields";
2-
import {ActionURL} from "@labkey/api";
1+
import {FIELD_NAME_MAP, IGNORED_INFO_FIELDS, INFO_FIELD_GROUPS} from "./fields";
32
import {Chart} from "react-google-charts";
43
import {style as styles} from "./style";
4+
import {getGenotypeURL} from "../../../../utils";
55

66
export default jbrowse => {
77
const {
@@ -245,8 +245,7 @@ export default jbrowse => {
245245
const start = feat["POS"];
246246
const end = feat["end"];
247247

248-
const link = ActionURL.buildURL("jbrowse", "genotypeTable.view", null, {trackId: trackId, chr: contig, start: start, stop: end})
249-
const href = <a href={link} target="_blank">Click here to view sample-level genotypes</a>
248+
const href = <a href={getGenotypeURL(trackId, contig, start, end)} target="_blank">Click here to view sample-level genotypes</a>
250249

251250
setState(
252251
<div>

jbrowse/src/client/JBrowse/utils.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export async function fetchSession(queryParam, sessionId, nativePlugins, refThem
133133
}
134134
},
135135
failure: function(res){
136-
handleFailure("There was an error: " + res.status, sessionId, trackId, isTable)
136+
handleFailure("There was an error: " + res.status, sessionId, trackId, isTable, false)
137137
},
138138
params: {session: sessionId, activeTracks: activeTracks ? activeTracks.join(',') : undefined}
139139
});
@@ -158,7 +158,7 @@ function applyUrlParams(json, queryParam) {
158158
} else {
159159
let found = false
160160
for (const track of json.tracks) {
161-
if (track.trackId?.toLowerCase() === trackId?.toLowerCase() || track.name?.toLowerCase() === trackId?.toLowerCase()) {
161+
if (track.trackId?.toLowerCase() === trackId?.toLowerCase() || track.name?.toLowerCase() === trackId?.toLowerCase() || track.trackId?.toLowerCase().includes(trackId?.toLowerCase())) {
162162
track.displays[0].renderer.activeSamples = sampleList.join(',')
163163
found = true
164164
break
@@ -182,7 +182,7 @@ function applyUrlParams(json, queryParam) {
182182
const infoFilterList = JSON.parse(decodeURIComponent(infoFilterObj))
183183
let found = false
184184
for (const track of json.tracks) {
185-
if (track.trackId?.toLowerCase() === trackId?.toLowerCase() || track.name?.toLowerCase() === trackId?.toLowerCase()) {
185+
if (track.trackId?.toLowerCase() === trackId?.toLowerCase() || track.name?.toLowerCase() === trackId?.toLowerCase() || track.trackId?.toLowerCase().includes(trackId?.toLowerCase())) {
186186
track.displays[0].renderer.infoFilters = [...infoFilterList]
187187
found = true
188188
break
@@ -244,10 +244,10 @@ function serializeInfoFilters(track) {
244244
return track.configuration.trackId + ":" + encodeURIComponent(track.configuration.displays[0].renderer.infoFilters.valueJSON)
245245
}
246246

247-
function handleFailure(error, sessionId?, trackId?, isTable?) {
247+
function handleFailure(error, sessionId?, trackId?, isTable?, reloadOnFailure = true) {
248248
alert(error)
249249

250-
if (sessionId && trackId) {
250+
if (reloadOnFailure && sessionId && trackId) {
251251
if (isTable) {
252252
navigateToTable(sessionId, "", trackId)
253253
} else {
@@ -257,5 +257,12 @@ function handleFailure(error, sessionId?, trackId?, isTable?) {
257257
}
258258

259259
export function getGenotypeURL(trackId, contig, start, end) {
260+
// NOTE: due to jbrowse/trix behavior, the trackId that gets serialized into the trix index is the actual trackGUID plus the filename.
261+
// Since this action expects the GUID alone, detect long filenames and subset.
262+
// TODO: once this behavior is fixed in jbrowse, remove this logic
263+
if (trackId.length > 36) {
264+
trackId = trackId.substr(0,36)
265+
}
266+
260267
return ActionURL.buildURL("jbrowse", "genotypeTable.view", null, {trackId: trackId, chr: contig, start: start, stop: end})
261268
}

jbrowse/src/org/labkey/jbrowse/model/JBrowseSession.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,14 @@ public JSONObject getDefaultSessionJson(List<JsonFile> tracks, @Nullable List<St
311311
{
312312
boolean visibleByDefault = jf.isVisibleByDefault() || jf.matchesTrackSelector(additionalActiveTracks);
313313
if (visibleByDefault) {
314-
String trackId = jf.getObjectId();
315314
defaultTracks.put(new JSONObject(){{
316315
put("type", jf.getTrackType());
317-
put("configuration", trackId);
316+
put("configuration", jf.getJsonTrackId());
318317
JSONArray displaysArr = new JSONArray();
319318
displaysArr.put(new JSONObject(){{
320319
String displayType = jf.getDisplayType();
321320
put("type", displayType);
322-
put("configuration", trackId + "-" + displayType);
321+
put("configuration", jf.getJsonTrackId() + "-" + displayType);
323322
}});
324323
put("displays", displaysArr);
325324
}});

jbrowse/src/org/labkey/jbrowse/model/JsonFile.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.labkey.api.query.FieldKey;
3333
import org.labkey.api.query.QueryService;
3434
import org.labkey.api.query.UserSchema;
35+
import org.labkey.api.reader.Readers;
3536
import org.labkey.api.security.User;
3637
import org.labkey.api.security.permissions.ReadPermission;
3738
import org.labkey.api.sequenceanalysis.SequenceAnalysisService;
@@ -46,13 +47,16 @@
4647
import org.labkey.api.util.PageFlowUtil;
4748
import org.labkey.api.util.Path;
4849
import org.labkey.api.view.UnauthorizedException;
50+
import org.labkey.api.writer.PrintWriters;
4951
import org.labkey.jbrowse.JBrowseManager;
5052
import org.labkey.jbrowse.JBrowseSchema;
5153
import org.labkey.sequenceanalysis.run.util.TabixRunner;
5254

5355
import javax.annotation.Nullable;
56+
import java.io.BufferedReader;
5457
import java.io.File;
5558
import java.io.IOException;
59+
import java.io.PrintWriter;
5660
import java.sql.SQLException;
5761
import java.util.Arrays;
5862
import java.util.Collections;
@@ -497,7 +501,7 @@ private JSONObject getVcfTrack(Logger log, ExpData targetFile, ReferenceGenome r
497501
{
498502
JSONObject ret = new JSONObject();
499503
ret.put("type", getTrackType());
500-
ret.put("trackId", getObjectId());
504+
ret.put("trackId", getJsonTrackId());
501505
ret.put("name", getLabel());
502506
ret.put("assemblyNames", new JSONArray(){{
503507
put(JBrowseSession.getAssemblyName(rg));
@@ -527,7 +531,7 @@ private JSONObject getVcfTrack(Logger log, ExpData targetFile, ReferenceGenome r
527531
ret.put("displays", new JSONArray(){{
528532
put(new JSONObject(){{
529533
put("type", "ExtendedVariantDisplay");
530-
put("displayId", getObjectId() + "-ExtendedVariantDisplay");
534+
put("displayId", getJsonTrackId() + "-ExtendedVariantDisplay");
531535
put("maxDisplayedBpPerPx", 2000);
532536
put("mouseover", "jexl:'Position: ' + formatWithCommas(get(feature,'POS'))");
533537
put("renderer", new JSONObject(){{
@@ -559,11 +563,17 @@ public boolean matchesTrackSelector(List<String> toTest)
559563
return toTest.contains(getObjectId()) || toTest.contains(getLabel());
560564
}
561565

566+
public String getJsonTrackId()
567+
{
568+
final File finalLocation = getLocationOfProcessedTrack(false);
569+
return finalLocation == null ? null : finalLocation.getName();
570+
}
571+
562572
private JSONObject getBamTrack(Logger log, ExpData targetFile, ReferenceGenome rg)
563573
{
564574
JSONObject ret = new JSONObject();
565575
ret.put("type", getTrackType());
566-
ret.put("trackId", getObjectId());
576+
ret.put("trackId", getJsonTrackId());
567577
ret.put("name", getLabel());
568578
ret.put("category", new JSONArray(){{
569579
put(getCategory());
@@ -647,7 +657,7 @@ private JSONObject getTabixTrack(Logger log, ExpData targetFile, ReferenceGenome
647657
{
648658
JSONObject ret = new JSONObject();
649659
ret.put("type", getTrackType());
650-
ret.put("trackId", getObjectId());
660+
ret.put("trackId", getJsonTrackId());
651661
ret.put("name", getLabel());
652662
ret.put("category", new JSONArray(){{
653663
put(getCategory());
@@ -829,13 +839,20 @@ public File prepareResource(Logger log, boolean throwIfNotPrepared, boolean forc
829839
wrapper.setWorkingDir(targetFile.getParentFile());
830840
wrapper.setThrowNonZeroExits(true);
831841

832-
//TODO: eventually remove this. see: https://github.com/GMOD/jbrowse-components/issues/2354#issuecomment-926320747
833-
wrapper.addToEnvironment("DEBUG", "*");
834842
wrapper.execute(Arrays.asList(exe.getPath(), "text-index", "--force", "--quiet", "--attributes", StringUtils.join(attributes, ","), "--prefixSize", "5", "--file", targetFile.getPath()));
835843
if (!ixx.exists())
836844
{
837845
throw new PipelineJobException("Unable to find expected index file: " + ixx.getPath());
838846
}
847+
848+
// See here: https://github.com/GMOD/jbrowse-components/issues/2344
849+
// for background. this hackery is needed to ensure the trackId listed in the ix matches the GUID, not the filename:
850+
log.info("Updating trackId in trix ix file");
851+
File ix = getExpectedLocationOfIndexFile(".ix", false);
852+
if (!ix.exists())
853+
{
854+
throw new PipelineJobException("Unable to find file: " + ix.getPath());
855+
}
839856
}
840857
}
841858

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
totalPassed <- 0
2+
for (datasetId in names(seuratObjects)) {
3+
printName(datasetId)
4+
seuratObj <- readRDS(seuratObjects[[datasetId]])
5+
6+
cellsToKeep <- NULL
7+
for (locus in c('A', 'B', 'D', 'G')) {
8+
fieldName <- paste0('TR', locus)
9+
10+
cdr3ForLocus <- cdr3s[grepl(cdr3s, pattern = paste0(fieldName, ':'))]
11+
if (length(cdr3ForLocus) == 0) {
12+
next
13+
}
14+
15+
if (!(fieldName %in% names(seuratObj@meta.data))) {
16+
stop(paste0('Missing field: ', fieldName))
17+
}
18+
19+
cdr3ForLocus <- gsub(cdr3ForLocus, pattern = paste0(fieldName, ':'), replacement = '')
20+
matchingCells <- sapply(seuratObj@meta.data[[fieldName]], function(x){
21+
values <- unlist(strsplit(x, split = ','))
22+
return(length(intersect(values, cdr3ForLocus)) != 0)
23+
})
24+
25+
if (sum(matchingCells) == 0) {
26+
next
27+
}
28+
29+
matchingCells <- colnames(seuratObj)[matchingCells]
30+
if (all(is.null(cellsToKeep))) {
31+
cellsToKeep <- matchingCells
32+
} else {
33+
cellsToKeep <- unique(c(cellsToKeep, matchingCells))
34+
}
35+
}
36+
37+
if (all(is.null(cellsToKeep))) {
38+
print('There were no matching cells')
39+
} else {
40+
seuratObj <- subset(seuratObj, cells = cellsToKeep)
41+
#saveData(seuratObj, datasetId)
42+
totalPassed <- totalPassed + 1
43+
}
44+
45+
# Cleanup
46+
rm(seuratObj)
47+
gc()
48+
}
49+
50+
if (totalPassed == 0) {
51+
addErrorMessage('No cells remained in any seurat objects after subsetting')
52+
}

singlecell/resources/web/singlecell/panel/NimbleAlignPanel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Ext4.define('SingleCell.panel.NimbleAlignPanel', {
5252
const store = this.up('singlecell-nimblealignpanel').genomeField.store
5353
if (val && store) {
5454
const recIdx = store.find('rowid', val);
55-
return store.getAt(recIdx).get('name');
55+
return recIdx === -1 ? '[' + val + ']' : store.getAt(recIdx).get('name');
5656
}
5757
else if (val) {
5858
return '[' + val + ']';

singlecell/resources/web/singlecell/panel/NimbleAppendPanel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Ext4.define('SingleCell.panel.NimbleAppendPanel', {
5151
const store = this.up('singlecell-nimbleappendpanel').genomeField.store
5252
if (val && store) {
5353
const recIdx = store.find('rowid', val);
54-
return store.getAt(recIdx).get('name');
54+
return recIdx === -1 ? '[' + val + ']' : store.getAt(recIdx).get('name');
5555
}
5656
else if (val) {
5757
return '[' + val + ']';

singlecell/resources/web/singlecell/panel/PoolImportPanel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ Ext4.define('SingleCell.panel.PoolImportPanel', {
202202
val = val.replace(/^MultiSeq[- ]Idx[- ]RP/ig, 'MultiSeq-Idx-RP');
203203

204204
if (val.length <= 3 && panel.down('#useDualIndex').getValue()) {
205-
val = 'MS-TN-' + val;
205+
val = 'SI-TN-' + val;
206206
}
207207
else {
208208
LDK.Utils.logError('Unexpected value with single-end hashing: ' + val);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ public static void registerPipelineSteps()
190190
SequencePipelineService.get().registerPipelineStep(new RunVision.Provider());
191191
SequencePipelineService.get().registerPipelineStep(new NimbleAppend.Provider());
192192
SequencePipelineService.get().registerPipelineStep(new AppendTcr.Provider());
193+
SequencePipelineService.get().registerPipelineStep(new TcrFilter.Provider());
193194

194195
SequenceAnalysisService.get().registerFileHandler(new NimbleHandler());
195196
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.labkey.singlecell.pipeline.singlecell;
2+
3+
import org.json.JSONObject;
4+
import org.labkey.api.sequenceanalysis.pipeline.AbstractPipelineStepProvider;
5+
import org.labkey.api.sequenceanalysis.pipeline.PipelineContext;
6+
import org.labkey.api.singlecell.pipeline.SeuratToolParameter;
7+
import org.labkey.api.singlecell.pipeline.SingleCellStep;
8+
9+
import java.util.Arrays;
10+
11+
public class TcrFilter extends AbstractCellMembraneStep
12+
{
13+
public TcrFilter(PipelineContext ctx, TcrFilter.Provider provider)
14+
{
15+
super(provider, ctx);
16+
}
17+
18+
public static class Provider extends AbstractPipelineStepProvider<SingleCellStep>
19+
{
20+
public Provider()
21+
{
22+
super("TcrFilter", "TCR-Based Filter", "CellMembrane/Seurat", "This will run standard Seurat processing steps to normalize and scale the data.", Arrays.asList(
23+
SeuratToolParameter.create("cdr3s", "CDR3s To Keep", "A comma- or newline-delimited list of CDR3 sequences where locus prefixes the AA sequence (i.e. TRB:XXXXXX or TRA:YYYYYYY). Any cell matching any of these CDR3s will be kept. If that cell has multiple chains for a locus (i.e. 'CASSXXXXX,CASSYYYYY'), then only one of these needs to match for that cell to be kept. Also, all the input CDR3s should be single-chain (i.e. 'TRA:XXXXX', not 'TRA:XXXX,YYYY').", "sequenceanalysis-trimmingtextarea", new JSONObject(){{
24+
put("height", 150);
25+
put("delimiter", ",");
26+
}}, null).delimiter(",")
27+
), Arrays.asList("/sequenceanalysis/field/TrimmingTextArea.js"), null);
28+
}
29+
30+
@Override
31+
public TcrFilter create(PipelineContext ctx)
32+
{
33+
return new TcrFilter(ctx, this);
34+
}
35+
}
36+
37+
@Override
38+
public String getFileSuffix()
39+
{
40+
return "tcrFilter";
41+
}
42+
}
43+
44+

0 commit comments

Comments
 (0)