Skip to content

Commit

Permalink
...
Browse files Browse the repository at this point in the history
  • Loading branch information
jrussell9000 committed Apr 14, 2024
1 parent 26d4e2b commit 0312b98
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 29 deletions.
10 changes: 5 additions & 5 deletions Docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,8 @@ RUN cd /fastsurfer ; python3 FastSurferCNN/download_checkpoints.py --all && \
mv fullBUILD.info BUILD.info

# Create input, output, and NDATools directories and set their permissions
RUN mkdir /input /output /log /NDA /.NDATools /.local && \
chmod 777 /input /output /log /NDA /.NDATools /.local

# Set up NDA authentication
COPY ./abcd/keyringrc.cfg /.config/python_keyring/
RUN mkdir /input /output /log && \
chmod 777 /input /output /log

# TODO: SBOM info of FastSurfer and FreeSurfer are missing, it is unclear how to add
# those at the moment, as the buildscanner syft does not find simple package.json
Expand Down Expand Up @@ -259,3 +256,6 @@ FROM runtime as runtime_cuda
ENV NVIDIA_VISIBLE_DEVICES=all \
NVIDIA_DRIVER_CAPABILITIES=compute,utility \
NVIDIA_REQUIRE_CUDA="cuda>=8.0"

# Set up NDA authentication
COPY ./abcd/keyringrc.cfg /home/nonroot/.config/python_keyring/
148 changes: 148 additions & 0 deletions abcd/ndaDownload.26
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import logging
import os
import tarfile

from argparse import Namespace
from dataclasses import dataclass
from pathlib import Path
from utils import logTitle

# # Silence obnoxious messages from nda-tools
# with suppress_stdout():
from NDATools import prerun_checks_and_setup
from NDATools.Download import Download
from NDATools.Configuration import ClientConfiguration

'''
This module provides programmatic access to the 'download' methods
available as part of NDA-tools. It first (createArgs) creates the argument
parser namespace that would normally be created from command line arguments.
We'll pass the raw subject_ses string to the regex filter argument to download
only the files for the current subject/timepoint pairing. It then checks for a
default NDA-tools configuration file in HOME/.NDATools and attempts to create a
new file (and parent directory) if it doesn't find one. It then starts the
downloading the TGZ archives containing each file. After the download is complete,
it decompresses the archives and returns ONLY the files, ignoring any internal
directory structure.

###IMPORTANT####
This module relies on the python 'keyring' and 'keyrings.alt' modules (yes, they're distinct)
being installed, as they'll be used by NDATools. Keyring manages the access credentials
to NDA while keyrings provides alternate formats for those credentials. Currently,
we're using the less than optimal DCAN-Labs approach of using plain text storage
for our keyring - an option made possible by keyrings - which MUST BE CONFIGURED MANUALLY
(unless I get around to using something better). Specifically, create a file
named 'keyringrc.cfg' in the directory $HOME/.config/python_keyring . Add the
following text inside it...

[backend]
default-keyring=keyrings.alt.file.PlaintextKeyring
keyring-path=/tmp/work

...to enable plain text keyrings, which will be stored in /tmp/work. Afterwards, set up
keyring authentication by loading a python terminal and running:

import keyring
keyring.set_password("nda-tools", "username", "password")

...where "username" and "password" are the credentials TO THE NDA WEBSITE (NOT the miNDAR)
###############################
'''

@dataclass
class Downloader:
rawsubjses_str: str
input_dir: str
NDA_username: str
miNDAR_packageID: str
nThreads: str

def __post_init__(self):
self.logger = logging.getLogger('Downloading')
logTitle(f'Downloading and Unpacking Scan Files for {self.rawsubjses_str}')
self.main()

def createArgs(self):
# nda-tools makes us pass an 'args' namespace to the download module
# that contains the following contents
args = Namespace(
package=self.miNDAR_packageID,
file_regex=self.rawsubjses_str,
username=self.NDA_username,
directory=[str(self.input_dir)],
txt="",
datastructure="",
s3_destination="",
paths="",
workerThreads=self.nThreads,
verify=False)
return (args)


# Check if an NDATools configuration settings file exists, and create one if it does not
# Load the configuration settings into 'config'
def configCheck(self):
defaultConfigFile = Path.home() / ".NDATools/settings.cfg"
if defaultConfigFile.is_file():
config = ClientConfiguration(
self.NDA_username
)
config._check_and_fix_missing_options()
config.read_user_credentials()
else:
config = ClientConfiguration(
"clientscripts/config/settings.cfg", self.NDA_username)
config.read_user_credentials()

return config

# Initiate the download for this subject_ses
def download(self, config, args):

rawsubj_str = self.rawsubjses_str.split("_")[0]
rawses_str = self.rawsubjses_str.split("_")[1]

try:
self.logger.info(f'Now downloading scan files for subject '
f'{rawsubj_str}, timepoint {rawses_str}...')
s3Download = Download(config, args)
s3Download.start()

except Exception as exc:
print(exc)
self.logger.exception(exc, exc_info=True)

# Loop over all the tgz files in the subject_ses folder and unpack them
def unpack(self):
self.logger.info('Unpacking all the TGZ files...')

def niiextract(tgz):
try:
with tarfile.open(tgz, "r") as tar:
# Only extract the NIFTI files
members = [member for member in tar.getmembers() if member.name.split(".")[-1] == "nii"]
for member in members:
# Don't extract parent folders, just the files
member.name = Path(member.name).name
self.logger.info(f'Decompressing {member.name}...')
tar.extractall(path=self.input_dir, members=members)
except Exception as exc:
self.logger.exception(f'Failed to unpack {tgz}: {exc}', exc_info=True)
else:
os.remove(tgz)

[niiextract(tgz) for tgz in Path(self.input_dir).rglob(f'{self.rawsubjses_str}*.tgz')]

def main(self):
try:
self.logger = logging.getLogger(__name__)
prerun_checks_and_setup()
args = self.createArgs()
config = self.configCheck()
self.download(config, args)
self.unpack()
except Exception as exc:
self.logger.exception(f'Encountered exception while downloading and unpacking files: {exc}', exc_info=True)


Downloader('NDARINV00LH735Y_baselineYear1Arm1', "/fastscratch/jdr/ABCD/fastSurf/inputs", "jrusse10", "1226193", "2")
46 changes: 22 additions & 24 deletions abcd/ndaDownload.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import config
import keyring
import logging
import os
import tarfile
Expand All @@ -11,7 +13,7 @@
# with suppress_stdout():
from NDATools import prerun_checks_and_setup
from NDATools.Download import Download
from NDATools.Configuration import ClientConfiguration
from NDATools import init_and_create_configuration, NDA_TOOLS_DOWNLOADCMD_LOGS_FOLDER

'''
This module provides programmatic access to the 'download' methods
Expand Down Expand Up @@ -62,38 +64,33 @@ def __post_init__(self):
logTitle(f'Downloading and Unpacking Scan Files for {self.rawsubjses_str}')
self.main()

def keyringSetup(self):
if not keyring.get_password('nda-tools', self.NDA_username):
keyring.set_password('nda-tools', self.NDA_username, config.nda_password)

def createArgs(self):
# nda-tools makes us pass an 'args' namespace to the download module
# that contains the following contents
args = Namespace(
package=self.miNDAR_packageID,
file_regex=self.rawsubjses_str,
username=self.NDA_username,
directory=[str(self.input_dir)],
txt="",
datastructure="",
s3_destination="",
directory=[str(self.input_dir)],
file_regex=self.rawsubjses_str,
log_dir=None,
package=self.miNDAR_packageID,
paths="",
workerThreads=int(self.nThreads),
verify=False)
s3_destination="",
txt="",
username=self.NDA_username,
verbose=False,
verify=False,
workerThreads=int(self.nThreads))
return (args)


# Check if an NDATools configuration settings file exists, and create one if it does not
# Load the configuration settings into 'config'
def configCheck(self):
defaultConfigFile = Path.home() / ".NDATools/settings.cfg"
if defaultConfigFile.is_file():
config = ClientConfiguration(
self.NDA_username
)
config._check_and_fix_missing_options()
config.read_user_credentials()
else:
config = ClientConfiguration(
"clientscripts/config/settings.cfg", self.NDA_username)
config.read_user_credentials()

def configCheck(self, args):
config = init_and_create_configuration(args, NDA_TOOLS_DOWNLOADCMD_LOGS_FOLDER)
return config

# Initiate the download for this subject_ses
Expand Down Expand Up @@ -138,11 +135,12 @@ def main(self):
self.logger = logging.getLogger(__name__)
prerun_checks_and_setup()
args = self.createArgs()
config = self.configCheck()
self.keyringSetup()
config = self.configCheck(args)
self.download(config, args)
self.unpack()
except Exception as exc:
self.logger.exception(f'Encountered exception while downloading and unpacking files: {exc}', exc_info=True)


# SubjsesDL('NDARINV00LH735Y_baselineYear1Arm1', "/fastscratch/jdr/ABCD/fastSurf/inputs", "jrusse10_1223110", "2")
# Downloader('NDARINV00LH735Y_baselineYear1Arm1', "/fastscratch/jdr/ABCD/fastSurf/inputs", "jrusse10", "1226193", "2")

0 comments on commit 0312b98

Please sign in to comment.