diff --git a/cirrocumulus/api.py b/cirrocumulus/api.py
index c1a94062..43da9d7e 100644
--- a/cirrocumulus/api.py
+++ b/cirrocumulus/api.py
@@ -24,6 +24,7 @@
CIRRO_LIBRARY,
CIRRO_MIXPANEL,
CIRRO_SERVE,
+ CIRRO_SERVER_DATA_DIR,
CIRRO_SPECIES,
CIRRO_STATIC_DIR,
CIRRO_UPLOAD,
@@ -167,6 +168,43 @@ def handle_server():
other=["Gallus gallus", "Macaca fascicularis", "Macaca mulatta", "Rattus norvegicus"],
)
+ # browse server-side data files to allow user to select the desired dataset instead of necessarily typing the URL:
+ d["server_files"] = []
+ if CIRRO_SERVER_DATA_DIR in os.environ and os.environ[CIRRO_SERVER_DATA_DIR]:
+ VALID_EXTENSIONS = [
+ ".h5ad",
+ ".h5",
+ ".zip",
+ ".tar",
+ ".tar.gz",
+ ".loom",
+ ".h5seurat",
+ ".rds",
+ ".zarr",
+ ]
+ for root, dirs, files in os.walk(os.environ[CIRRO_SERVER_DATA_DIR]):
+ for file in dirs + files:
+ if any([file.endswith(ext) for ext in VALID_EXTENSIONS]):
+ d["server_files"] += [os.path.join(root, file)]
+ # if this is a directory, e.g. .zarr "files", prevent further recursion:
+ if file in dirs:
+ dirs.remove(file)
+ elif (
+ file in dirs
+ ): # check if `file` is a MEX formatted directory (we must look at the subfiles)
+ # https://www.10xgenomics.com/support/software/xenium-panel-designer/latest/tutorials/create-single-cell-reference
+ count_tsvgz = 0 # MEX directoris have two subfiles with .tsv.gz extension
+ count_mtxgz = 0 # MEX directories also have one subfile with .mtx.gz extension
+ for subfile in os.listdir(os.path.join(root, file)):
+ if subfile.endswith(".tsv.gz"):
+ count_tsvgz += 1
+ elif subfile.endswith(".mtx.gz"):
+ count_mtxgz += 1
+ if count_tsvgz == 2 and count_mtxgz == 1:
+ d["server_files"] += [os.path.join(root, file)]
+ dirs.remove(file)
+ break
+
# from https://www.ebi.ac.uk/ols/ontologies/efo/terms?iri=http%3A%2F%2Fwww.ebi.ac.uk%2Fefo%2FEFO_0010183&viewMode=All&siblings=false
d["library"] = load_json(CIRRO_LIBRARY) or [
"10x 3' v1",
diff --git a/cirrocumulus/envir.py b/cirrocumulus/envir.py
index 79b479b3..698a453c 100644
--- a/cirrocumulus/envir.py
+++ b/cirrocumulus/envir.py
@@ -38,6 +38,9 @@
# comma separated list of paths to allow all logged in users to download from
CIRRO_STATIC_DIR = "CIRRO_STATIC_DIR"
+# directory in server whose files can be available to user for direct selection
+CIRRO_SERVER_DATA_DIR = "CIRRO_SERVER_DATA_DIR"
+
SERVER_CAPABILITY_RENAME_CATEGORIES = "SERVER_CAPABILITY_RENAME_CATEGORIES"
SERVER_CAPABILITY_JOBS = "SERVER_CAPABILITY_JOBS"
SERVER_CAPABILITY_FEATURE_SETS = "SERVER_CAPABILITY_FEATURE_SETS"
diff --git a/cirrocumulus/serve.py b/cirrocumulus/serve.py
index f1290ed9..4f449e14 100644
--- a/cirrocumulus/serve.py
+++ b/cirrocumulus/serve.py
@@ -15,6 +15,7 @@
CIRRO_JOB_RESULTS,
CIRRO_JOB_TYPE,
CIRRO_SERVE,
+ CIRRO_SERVER_DATA_DIR,
CIRRO_UPLOAD,
)
from cirrocumulus.launch import create_app
@@ -100,6 +101,10 @@ def create_parser(description=False):
"--results", help="URL to save user computed results (e.g. differential expression) to"
)
parser.add_argument("--ontology", help="Path to ontology in OBO format for annotation")
+ parser.add_argument(
+ "--datadir",
+ help='Path to directory in the server where the user can select its dataset from with no need to type the full URL in the "New Dataset" window.',
+ )
return parser
@@ -127,6 +132,11 @@ def main(argsv):
os.environ[CIRRO_JOB_RESULTS] = args.results
get_fs(os.environ[CIRRO_JOB_RESULTS]).makedirs(os.environ[CIRRO_JOB_RESULTS], exist_ok=True)
+ if args.datadir is not None:
+ if not os.path.isdir(args.datadir):
+ raise ValueError("--datadir is not a valid path to a directory")
+ os.environ[CIRRO_SERVER_DATA_DIR] = args.datadir
+
run_args = [
"gunicorn",
"-b",
diff --git a/src/EditNewDatasetDialog.js b/src/EditNewDatasetDialog.js
index e9947cb5..90185537 100644
--- a/src/EditNewDatasetDialog.js
+++ b/src/EditNewDatasetDialog.js
@@ -81,8 +81,11 @@ function EditNewDatasetDialog(props) {
const otherSpecies = serverInfo.species.other;
const libraryOptions = serverInfo.library;
+ const dataFiles = serverInfo.server_files;
+
const canUpload = serverInfo.upload;
const isNew = dataset == null;
+ const mustBrowse = dataFiles.length > 0;
let saveEnabled = !loading && name.trim() !== '';
const isAuthEnabled = serverInfo.auth.clientId !== '';
@@ -340,7 +343,7 @@ function EditNewDatasetDialog(props) {
+