diff --git a/packages/data-portal-explore/src/components/DataAvailabilityTable.tsx b/packages/data-portal-explore/src/components/DataAvailabilityTable.tsx index 5ba73482..e98c79a2 100644 --- a/packages/data-portal-explore/src/components/DataAvailabilityTable.tsx +++ b/packages/data-portal-explore/src/components/DataAvailabilityTable.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { Table } from 'react-bootstrap'; import { generatePublicationPageTabUrl } from '../lib/dataTableHelpers'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faLockOpen } from '@fortawesome/free-solid-svg-icons'; +import { faLock, faLockOpen } from '@fortawesome/free-solid-svg-icons'; interface IDataAvailabilityTableProps { assays: { [assayName: string]: Entity[] }; @@ -88,13 +88,20 @@ export const DataAvailabilityTable: React.FunctionComponent Files - CDS/SB-CGC (dbGaP 🔒) + + CDS/SB-CGC (dbGaP{' '} + + ) + CDS/SB-CGC (Open Access{` `} ) @@ -104,7 +111,7 @@ export const DataAvailabilityTable: React.FunctionComponent Synapse (Open Access{` `} ) diff --git a/packages/data-portal-explore/src/components/FileFilterControls.tsx b/packages/data-portal-explore/src/components/FileFilterControls.tsx index a8ba65c7..a4caebbf 100644 --- a/packages/data-portal-explore/src/components/FileFilterControls.tsx +++ b/packages/data-portal-explore/src/components/FileFilterControls.tsx @@ -1,7 +1,7 @@ import { observer } from 'mobx-react'; import React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faLockOpen } from '@fortawesome/free-solid-svg-icons'; +import { faLock, faLockOpen } from '@fortawesome/free-solid-svg-icons'; import { FilterControls, @@ -128,14 +128,22 @@ export const FileFilterControls: React.FunctionComponent { const downloadLabels = { - [DownloadSourceCategory.dbgap]: - 'CDS/SB-CGC (dbGaP 🔒)', + [DownloadSourceCategory.dbgap]: ( + + CDS/SB-CGC (dbGaP{' '} + + ) + + ), // [DownloadSourceCategory.idc]: 'IDC (Imaging)', [DownloadSourceCategory.cds]: ( CDS/SB-CGC (Open Access{' '} ) @@ -145,7 +153,7 @@ export const FileFilterControls: React.FunctionComponent Synapse (Open Access{' '} ) diff --git a/packages/data-portal-explore/src/components/FileTable.tsx b/packages/data-portal-explore/src/components/FileTable.tsx index 76a4d3dc..f3a61726 100644 --- a/packages/data-portal-explore/src/components/FileTable.tsx +++ b/packages/data-portal-explore/src/components/FileTable.tsx @@ -10,6 +10,8 @@ import { faDownload, faExternalLinkAlt, faLockOpen, + faLock, + faHourglassStart, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -43,6 +45,7 @@ import { } from '@htan/data-portal-commons'; const CDS_MANIFEST_FILENAME = 'cds_manifest.csv'; +const GEN3_MANIFEST_FILENAME = 'gen3_manifest.json'; interface IFileDownloadModalProps { files: Entity[]; @@ -87,17 +90,34 @@ function generateCdsManifestFile(files: Entity[]): string | undefined { } } +function generateGen3ManifestFile(files: Entity[]): string | undefined { + const data = _(files) + .filter((f) => !!f.viewers?.cds) + .map((f) => ({ + object_id: f.viewers?.cds?.drs_uri?.replace( + 'drs://nci-crdc.datacommons.io/', + '' + ), + })) + .value(); + + return data.length > 0 ? JSON.stringify(data, null, 2) : undefined; +} + + const FilenameWithAccessIcon: React.FunctionComponent<{ file: Entity; -}> = (props) => { +}> = ({ file }) => { + const isControlledAccess = file.downloadSource === DownloadSourceCategory.dbgap; + const iconColor = isControlledAccess ? '#FF8C00' : '#00796B'; // Amber for controlled, Dark Teal for open + return ( <> - {props.file.downloadSource === DownloadSourceCategory.dbgap ? ( - '🔒' - ) : ( - - )}{' '} - {getFileBase(props.file.Filename)} + {' '} + {getFileBase(file.Filename)} {'\n'} ); @@ -117,75 +137,167 @@ const CDSFileList: React.FunctionComponent<{ ); }; -const CDSInstructions: React.FunctionComponent<{ - files: Entity[]; -}> = (props) => { - const manifestFile = generateCdsManifestFile(props.files); - const dbgapFiles = props.files.filter( +const dbgapInstructions = (files: Entity[]) => { + const dbgapFiles = files.filter( (f) => f.downloadSource === DownloadSourceCategory.dbgap ); + if (dbgapFiles.length === 0) return null; - const manifestInstructions = ( - <> - you can import this manifest file into CGC following the - instructions{' '} - - here - - . - + return ( +
+ +

+ Your selection includes controlled-access Level 1 and/or Level 2 sequencing data (🔒). + To download Level 1/2 sequencing data, you first need to have been granted access to the{' '} + + HTAN dbGaP Study, Accession: phs002371 + . +

+
); +}; - const dbgapInstructions = ( - <> +const openAccessInstructions = (files: Entity[]) => { + return ( +
+ +
+ ); +}; + +const cdsManifestInstructions = (manifestFile: string | undefined) => { + if (!manifestFile) return null; + + return ( +

- Your selection includes Level 1 and/or Level 2 sequencing data - (🔒): + Load Files into SevenBridges CGC:{' '} + You can import these files into the{' '} + + SevenBridges Cancer Genomics Cloud (SB-CGC) + {' '} + by downloading the CDS manifest file below and following the instructions{' '} + + here + .

- + +
+ ); +}; + +const gen3ManifestInstructions = (gen3manifestFile: string | undefined) => { + if (!gen3manifestFile) return null; + + return ( +
+

- To download Level 1/2 sequencing data you first need to request{' '} + Download Files using the Gen3 SDK for Python:{' '} + You can download these files using the{' '} + Gen3 SDK for Python{' '} + (pip install gen3). Ensure that your{' '} - dbGaP + NCI Data Commons Framework Services API Key {' '} - access. Afterwards {manifestInstructions} + is stored in ~/.gen3/credentials.json.{' '} + (Note that Python 3.12 is not supported by the Gen3 SDK at this time.)

- +

+ +

+

+ Run the following gen3 command. +

+                    
+                        
+                        {`gen3 \\
+    --endpoint=nci-crdc.datacommons.io \\
+    drs-pull \\
+    manifest gen3_manifest.json \\
+    my_htan_dir`}
+                        
+                    
+
+

+
); +}; - const openAccessInstructions = ( - <> - -

To download selected files {manifestInstructions}

- +const CDSInstructions: React.FunctionComponent<{ files: Entity[] }> = ({ files }) => { + const dbgapFiles = files.filter( + (f) => f.downloadSource === DownloadSourceCategory.dbgap + ); + const openAccessFiles = files.filter( + (f) => f.downloadSource !== DownloadSourceCategory.dbgap ); + const manifestFile = generateCdsManifestFile(files); + const gen3manifestFile = generateGen3ManifestFile(files); + return ( - <> - {dbgapFiles.length > 0 && dbgapInstructions} - {dbgapFiles.length === 0 && openAccessInstructions} - {manifestFile?.length && ( -

- -

+
+
+

Files Available through NCI CRDC Cancer Data Service (CDS)

+ + {/* Render dbGaP instructions if dbGaP files exist */} + {dbgapFiles.length > 0 && ( +
+

+ {' '} + Your selection includes controlled-access Level 1 and/or Level 2 sequencing data.{' '} + To download Level 1/2 sequencing data, you first need to have been granted access to the{' '} + + HTAN dbGaP Study, Accession: phs002371 + . +

+ +
)} - + + {/* Render open access instructions if open access files exist */} + {openAccessFiles.length > 0 && ( +
+

+ {' '} + The files listed below are available without additional access requirements. +

+ +
+ )} + + {/* CDS and Gen3 manifest instructions */} + {cdsManifestInstructions(manifestFile)} + {gen3ManifestInstructions(gen3manifestFile)} +
); }; + const NotDownloadableInstructions: React.FunctionComponent<{ files: Entity[]; }> = (props) => { @@ -195,119 +307,34 @@ const NotDownloadableInstructions: React.FunctionComponent<{ return props.files.length > 0 ? (
+

Files coming soon

Your selection includes data that is not downloadable yet:

+ + {/* List the files */}
                 
-                    {props.files.map((f) => getFileBase(f.Filename)).join('\n')}
+                    {props.files.map((f) => (
+                        
+ {getFileBase(f.Filename)} +
+ ))}
- {hasAnyImageViewersForNonDownloadableFiles && ( - - Note however that you can explore them in one of the viewers - in the right most column. - + + {/* Additional message if files have preview viewers */} + {hasAnyImageViewersForNonDownloadableFiles ? ( +

+ These files are in preview. You can view metadata or explore them in one of the viewers in the rightmost column while they are prepared for public access. +

+ ) : ( +

+ These files are in preview. Metadata is available for review while the files are prepared for public access. +

)}
) : null; }; -const ImagingInstructionsIDC: React.FunctionComponent<{ files: Entity[] }> = ( - props -) => { - const [cloudSource, setCloudSource] = useState<'gcp' | 'aws'>('gcp'); - const getImageBucketUrl = (info: ImageViewerInfo) => { - return cloudSource === 'gcp' - ? info.idcImageBucketGcpUrl - : info.idcImageBucketAwsUrl; - }; - - const idcImageBucketUrls: string[] = props.files - .map( - (f) => getImageBucketUrl(getImageViewersAssociatedWithFile(f)) || [] - ) - .flat(); - const filesNotDownloadableFromIDC: Entity[] = props.files.filter( - (f) => - getImageBucketUrl(getImageViewersAssociatedWithFile(f)) === - undefined - ); - - return ( - <> - {idcImageBucketUrls.length > 0 && ( -
-

- Imaging data is available in{' '} - - DICOM-TIFF format - {' '} - from the{' '} - - Imaging Data Commons (IDC) - -

-

- Cloud source:{' '} -

- - setCloudSource('gcp')} - type="radio" - label={ - 'GCP - sponsored by Google Public Data Program' - } - checked={cloudSource === 'gcp'} - /> - setCloudSource('aws')} - type="radio" - label={ - 'AWS - sponsored by AWS Open Data Sponsorship Program' - } - checked={cloudSource === 'aws'} - /> - -
-

-

- To download the images in this manifest,{' '} - - install s5cmd - - , then run the following command: -

-

- - s5cmd --no-sign-request --endpoint-url{' '} - {cloudSource === 'gcp' - ? 'https://storage.googleapis.com' - : 'https://s3.amazonaws.com'}{' '} - run manifest.txt - -

-

- You can use the below bucket URLs for the{' '} - manifest.txt file: -

-
-                        {idcImageBucketUrls.join('\n')}
-                    
-
- )} - - - ); -}; - function generateDownloadScript(files: Entity[]) { const filesByName = _.groupBy(files, (f) => getFileBase(f.Filename)); @@ -322,6 +349,31 @@ function generateDownloadScript(files: Entity[]) { .join('\n'); } +const SynapseFileList: React.FunctionComponent<{ + files: Entity[]; +}> = ({ files }) => { + return ( +
+            
+                {files.map((file) => (
+                    
+ {' '} + {getFileBase(file.Filename)} ( + + {file.synapseId} + + ) +
+ ))} +
+
+ ); +}; + const SynapseInstructions: React.FunctionComponent<{ files: Entity[] }> = ( props ) => { @@ -329,21 +381,33 @@ const SynapseInstructions: React.FunctionComponent<{ files: Entity[] }> = ( return ( <> +
+

Files available in Synapse

+

+ The files listed below are available through{' '} + + Synapse + . +

+

- Use the{' '} + You can use the{' '} - Synapse command line client + Synapse CLI {' '} - to download the selected files: + command below to download the selected files.

                 {script}
             

- It is required to{' '} + You'll need to{' '} = ( > Synapse documentation - . -

-

- Note that the files can also be downloaded manually through the - Synapse web interface by clicking on the file name. + . Files can also be downloaded manually through the Synapse web interface by clicking on the file name.

); @@ -386,6 +446,20 @@ const FileDownloadModal: React.FunctionComponent = ( !f.viewers?.cds) ); + const availabilityMessage = () => { + const messages = []; + if (cdsFiles.length > 0) { + messages.push("Available through NCI CRDC Cancer Data Service (CDS)"); + } + if (synapseFiles.length > 0) { + messages.push("Available through Synapse"); + } + if (notDownloadableFiles.length > 0) { + messages.push("Coming soon (not downloadable yet)"); + } + return messages; + }; + return ( @@ -393,13 +467,28 @@ const FileDownloadModal: React.FunctionComponent = ( + {/* Summary Section */} +

Your selection include files that are:

+
    + {availabilityMessage().map((message, index) => ( +
  • {message}
  • + ))} +
+

Follow the instructions below on how to access data from each of these sources.{' '} + Further details are avaliable in the HTAN Manual.

+ + {/* CDS Section */} {cdsFiles.length > 0 && } - {notDownloadableFiles.length > 0 && ( - - )} + + {/* Synapse Section */} {synapseFiles.length > 0 && ( )} + + {/* Not Downloadable Section */} + {notDownloadableFiles.length > 0 && ( + + )}