Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensive IGV Name Parameter #316

Merged
merged 12 commits into from
Feb 8, 2024
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 @@ -292,7 +292,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 @@ -374,7 +374,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=$$',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, these igv.js reference genomes have changed. Nice update there, Will.!

It seems to load a tad quicker than previous ones. Nice. (FYI @jrobinso). Was thinking mirroring local region ap-southeast with #87

},
],
},
{
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