Skip to content

Commit

Permalink
Extensive IGV Name Parameter (#316)
Browse files Browse the repository at this point in the history
  • Loading branch information
williamputraintan authored Feb 8, 2024
1 parent c13684d commit eff89b3
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 92 deletions.
4 changes: 0 additions & 4 deletions src/api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,3 @@ export type DjangoRestApiResponse = {
};
results: unknown[];
};

export const getBaseNameFromKey = (key: string) => {
return key.split('/')[key.split('/').length - 1];
};
132 changes: 119 additions & 13 deletions src/components/OpenInIgvDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { constructGDSUrl } from '../../api/gds';
import { useToastContext } from '../../providers/ToastProvider';
import CircularLoaderWithText from '../../components/CircularLoaderWithText';
import { post } from 'aws-amplify/api';
import { useParams } from 'react-router-dom';
import { SubjectApiRes, usePortalSubjectDataAPI } from '../../api/subject';

type OpenIGVDesktopDialogType = {
handleClose: () => void;
Expand All @@ -19,15 +21,32 @@ type OpenIGVDesktopDialogType = {
type: 's3' | 'gds';
};
export default function OpenIGVDesktopDialog(props: OpenIGVDesktopDialogType) {
const { subjectId } = useParams();
const { toastShow } = useToastContext();
const { id, bucketOrVolume, pathOrKey, type, handleClose, handleNeedRestore } = props;

if (!subjectId) return <div>No subject Id found!</div>;

// Pulling data from usePortalSubjectDataAPI (this hook should cache if it was previously called)
const {
isError: subjectIsError,
error: subjectError,
data: subjectData,
} = usePortalSubjectDataAPI(subjectId);

// Query data
const gdsLocalIgvUrl = useQuery(
['gds-local-igv', bucketOrVolume, pathOrKey],
async () =>
await constructGDSLocalIgvUrl({ bucketOrVolume: bucketOrVolume, pathOrKey: pathOrKey }),
{ enabled: type == 'gds', retry: false }
async () => {
const igvName = constructIgvNameParameter({ pathOrKey, subjectData: subjectData! });

return await constructGDSLocalIgvUrl({
bucketOrVolume: bucketOrVolume,
pathOrKey: pathOrKey,
igvName: igvName,
});
},
{ enabled: type == 'gds' && !!subjectData, retry: false }
);

const s3LocalIgvUrl = useQuery(
Expand All @@ -39,12 +58,15 @@ export default function OpenIGVDesktopDialog(props: OpenIGVDesktopDialogType) {
handleNeedRestore();
}

const igvName = constructIgvNameParameter({ pathOrKey, subjectData: subjectData! });

return constructS3LocalIgvUrl({
igvName: igvName,
bucketOrVolume: bucketOrVolume,
pathOrKey: pathOrKey,
});
},
{ enabled: type == 's3', retry: false }
{ enabled: type == 's3' && !!subjectData, retry: false }
);

// IsError handling
Expand All @@ -67,7 +89,23 @@ export default function OpenIGVDesktopDialog(props: OpenIGVDesktopDialogType) {
});
handleClose();
}
}, [s3LocalIgvUrl.isError, s3LocalIgvUrl.error, gdsLocalIgvUrl.isError, gdsLocalIgvUrl.error]);
if (subjectError && subjectIsError) {
toastShow({
severity: 'error',
summary: 'Error on retrieving subject data.',
detail: `${subjectError}`,
sticky: true,
});
handleClose();
}
}, [
s3LocalIgvUrl.isError,
s3LocalIgvUrl.error,
gdsLocalIgvUrl.isError,
gdsLocalIgvUrl.error,
subjectError,
subjectIsError,
]);

useEffect(() => {
let localIgvUrl: string;
Expand Down Expand Up @@ -139,8 +177,12 @@ export default function OpenIGVDesktopDialog(props: OpenIGVDesktopDialogType) {
);
}

const constructGDSLocalIgvUrl = async (props: { bucketOrVolume: string; pathOrKey: string }) => {
const { bucketOrVolume, pathOrKey } = props;
const constructGDSLocalIgvUrl = async (props: {
igvName: string;
bucketOrVolume: string;
pathOrKey: string;
}) => {
const { bucketOrVolume, pathOrKey, igvName } = props;

let idxFilePath: string;
if (pathOrKey.endsWith('bam')) {
Expand Down Expand Up @@ -183,15 +225,79 @@ const constructGDSLocalIgvUrl = async (props: { bucketOrVolume: string; pathOrKe

const idx = encodeURIComponent(idxFilePresignUrl);
const enf = encodeURIComponent(filePresignUrl);
const name = pathOrKey.split('/').pop() ?? pathOrKey;
return `http://localhost:60151/load?index=${idx}&file=${enf}&name=${name}`;

return `http://localhost:60151/load?index=${idx}&file=${enf}&name=${igvName}`;
};

const constructS3LocalIgvUrl = async (props: { bucketOrVolume: string; pathOrKey: string }) => {
const { bucketOrVolume, pathOrKey } = props;
const constructS3LocalIgvUrl = (props: {
igvName: string;
bucketOrVolume: string;
pathOrKey: string;
}) => {
const { bucketOrVolume, pathOrKey, igvName } = props;

const name = pathOrKey.split('/').pop() ?? pathOrKey;
const file = `s3://${bucketOrVolume + '/' + pathOrKey}`;

return `http://localhost:60151/load?file=${encodeURIComponent(file)}&name=${name}`;
return `http://localhost:60151/load?file=${encodeURIComponent(file)}&name=${igvName}`;
};

/**
*
* We wanted to show more info in the name parameter when opening in IGV
* Ref: https://umccr.slack.com/archives/CP356DDCH/p1707116441928299?thread_ts=1706583808.733149&cid=CP356DDCH
*
* For BAM files the desired outcome is to include libraryId, sampleId, type, and filetype
* Desired output: SBJ00000_L0000000_PRJ00000_tumor.bam
*
* Other than BAM
* Desired output: SBJ00000_MDX0000.vcf.gz
*
* To find the match of metadata for the specific key/path will iterate through the lims record
* @param props
*/
export const constructIgvNameParameter = ({
subjectData,
pathOrKey,
}: {
pathOrKey: string;
subjectData: SubjectApiRes;
}): string => {
const nameArray: string[] = [];

const filetype = pathOrKey.split('.').pop();
// Find sampleId from its filename
const filename = pathOrKey.split('/').pop() ?? pathOrKey;
const sampleId = filename.split('.').shift()?.split('_').shift() ?? filename;

// Append subjectId if filename does not contain subjectId
if (!filename.startsWith(subjectData.id)) {
nameArray.push(subjectData.id);
}

// If it is a `bam` file it will try to figure out the appropriate libraryId
if (filetype?.toLocaleLowerCase() == 'bam') {
const libraryIdArray = subjectData.lims.reduce((acc, curr) => {
const currLibId = curr.library_id;
const currSampId = curr.sample_id;

// do not want value to appear twice at the return array
if (acc.includes(currLibId)) {
return acc;
}

// find the matching value and push to the array
if (currSampId == sampleId) {
acc.push(currLibId);
}

return acc;
}, [] as Array<string>);

nameArray.push(...libraryIdArray);
}

// Append filename at the end
nameArray.push(filename);

return nameArray.join('_');
};
4 changes: 2 additions & 2 deletions src/containers/subjects/AnalysisResultTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ function AnalysisResultS3Table(prop: AnalysisResultS3TableProps) {

type Props = { subjectId: string };

function AnalysisResultsPanel({ subjectId }: Props) {
function AnalysisResultsTable({ subjectId }: Props) {
const { isFetching, isLoading, data } = usePortalSubjectDataAPI(subjectId);

if (isLoading || isFetching) {
Expand Down Expand Up @@ -396,7 +396,7 @@ function AnalysisResultsPanel({ subjectId }: Props) {
return <div className='pi pi-exclamation-triangle text-xl' />;
}

export default AnalysisResultsPanel;
export default AnalysisResultsTable;

function groupResultsData({
results_s3,
Expand Down
41 changes: 26 additions & 15 deletions src/containers/subjects/IGV/genomes.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,68 @@
// More info see https://github.com/igvteam/igv.js/wiki/Reference-Genome

export const genomes = [
{
id: 'hg38',
name: 'Human (GRCh38/hg38)',
fastaURL: 'https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg38/hg38.fa',
indexURL: 'https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg38/hg38.fa.fai',
cytobandURL: 'https://s3.amazonaws.com/igv.org.genomes/hg38/annotations/cytoBandIdeo.txt.gz',
fastaURL: 'https://igv-genepattern-org.s3.amazonaws.com/genomes/seq/hg38/hg38.fa',
indexURL: 'https://igv-genepattern-org.s3.amazonaws.com/genomes/seq/hg38/hg38.fa.fai',
cytobandURL: 'https://igv-genepattern-org.s3.amazonaws.com/genomes/hg38/cytoBandIdeo.txt.gz',
aliasURL: 'https://igv-genepattern-org.s3.amazonaws.com/genomes/hg38/hg38_alias.tab',
chromSizesURL: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.chrom.sizes',
twoBitURL: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.2bit',
tracks: [
{
name: 'Refseq Genes',
format: 'refgene',
url: 'https://s3.amazonaws.com/igv.org.genomes/hg38/ncbiRefGene.txt.gz',
url: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/database/ncbiRefSeq.txt.gz',
indexed: false,
visibilityWindow: -1,
removable: false,
order: 1000000,
infoURL: 'https://www.ncbi.nlm.nih.gov/gene/?term=$$',
},
],
},
{
id: 'hg38_1kg',
name: 'Human (hg38 1kg/GATK)',
compressedFastaURL:
'https://s3.amazonaws.com/igv.org.genomes/hg38/Homo_sapiens_assembly38.fasta.gz',
fastaURL: 'https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg38/hg38.fa',
indexURL: 'https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg38/hg38.fa.fai',
fastaURL:
'https://1000genomes.s3.amazonaws.com/technical/reference/GRCh38_reference_genome/GRCh38_full_analysis_set_plus_decoy_hla.fa',
indexURL:
'https://1000genomes.s3.amazonaws.com/technical/reference/GRCh38_reference_genome/GRCh38_full_analysis_set_plus_decoy_hla.fa.fai',
cytobandURL: 'https://s3.amazonaws.com/igv.org.genomes/hg38/annotations/cytoBandIdeo.txt.gz',
tracks: [
{
name: 'Refseq Genes',
format: 'refgene',
url: 'https://s3.amazonaws.com/igv.org.genomes/hg38/ncbiRefGene.txt.gz',
id: 'hg19_genes',
url: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/database/ncbiRefSeq.txt.gz',
indexed: false,
visibilityWindow: -1,
removable: false,
order: 1000000,
infoURL: 'https://www.ncbi.nlm.nih.gov/gene/?term=$$',
},
],
},
{
id: 'hg19',
name: 'Human (CRCh37/hg19)',
fastaURL: 'https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg19/hg19.fasta',
indexURL: 'https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg19/hg19.fasta.fai',
cytobandURL: 'https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg19/cytoBand.txt',
name: 'Human (GRCh37/hg19)',
fastaURL: 'https://igv-genepattern-org.s3.amazonaws.com/genomes/seq/hg19/hg19.fasta',
indexURL: 'https://igv-genepattern-org.s3.amazonaws.com/genomes/seq/hg19/hg19.fasta.fai',
cytobandURL: 'https://igv-genepattern-org.s3.amazonaws.com/genomes/seq/hg19/cytoBand.txt',
aliasURL: 'https://s3.amazonaws.com/igv.org.genomes/hg19/hg19_alias.tab',
tracks: [
{
name: 'Refseq Genes',
format: 'refgene',
url: 'https://s3.amazonaws.com/igv.org.genomes/hg19/ncbiRefGene.txt.gz',
id: 'hg19_genes',
url: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg19/database/ncbiRefSeq.txt.gz',
indexed: false,
visibilityWindow: -1,
removable: false,
order: 1000000,
infoURL: 'https://www.ncbi.nlm.nih.gov/gene/?term=$$',
visibilityWindow: -1,
},
],
},
Expand Down
Loading

0 comments on commit eff89b3

Please sign in to comment.